diff --git a/.gitignore b/.gitignore
index a918936f20..191fbe6f50 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,3 +26,4 @@ jni/libspeex/.deps/
pkcs11.password
fabric.properties
play
+google-services.json
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index df2bebd0b5..1ea69e4192 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,12 +16,6 @@
-
@@ -56,8 +50,6 @@
-
-
@@ -96,62 +88,62 @@
+ android:targetActivity="org.thoughtcrime.securesms.loki.activities.HomeActivity">
@@ -264,7 +256,7 @@
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
android:label="@string/AndroidManifest_archived_conversations"
android:launchMode="singleTask"
- android:parentActivityName="org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity">
+ android:parentActivityName="org.thoughtcrime.securesms.loki.activities.HomeActivity">
@@ -275,7 +267,7 @@
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="@style/Session.DarkTheme.NoActionBar"
- android:parentActivityName="org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity"
+ android:parentActivityName="org.thoughtcrime.securesms.loki.activities.HomeActivity"
android:windowSoftInputMode="stateUnchanged">
@@ -673,12 +665,12 @@
-
+
-
+
diff --git a/build.gradle b/build.gradle
index 392ad467f4..8aab757f08 100644
--- a/build.gradle
+++ b/build.gradle
@@ -190,8 +190,8 @@ dependencies {
implementation "com.github.ybq:Android-SpinKit:1.4.0"
}
-def canonicalVersionCode = 49
-def canonicalVersionName = "1.1.0"
+def canonicalVersionCode = 50
+def canonicalVersionName = "1.2.0"
def postFixSize = 10
def abiPostFix = ['armeabi-v7a' : 1,
diff --git a/res/layout-sw400dp/activity_landing.xml b/res/layout-sw400dp/activity_landing.xml
index 77034954d5..a4f7790d60 100644
--- a/res/layout-sw400dp/activity_landing.xml
+++ b/res/layout-sw400dp/activity_landing.xml
@@ -21,7 +21,7 @@
android:textColor="@color/text"
android:text="Your Session begins here..." />
-
-
diff --git a/res/layout-sw400dp/fragment_enter_public_key.xml b/res/layout-sw400dp/fragment_enter_public_key.xml
index bb85cef9c5..5a3dfbc85b 100644
--- a/res/layout-sw400dp/fragment_enter_public_key.xml
+++ b/res/layout-sw400dp/fragment_enter_public_key.xml
@@ -30,7 +30,7 @@
android:textAlignment="center"
android:text="Users can share their Session ID by going into their account settings and tapping "Share Session ID", or by sharing their QR code." />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout-sw400dp/fragment_enter_session_id.xml b/res/layout-sw420dp/fragment_enter_session_id.xml
similarity index 100%
rename from res/layout-sw400dp/fragment_enter_session_id.xml
rename to res/layout-sw420dp/fragment_enter_session_id.xml
diff --git a/res/layout/activity_display_name.xml b/res/layout/activity_display_name.xml
index 443f6c23e0..0c501e8343 100644
--- a/res/layout/activity_display_name.xml
+++ b/res/layout/activity_display_name.xml
@@ -25,7 +25,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing"
- android:layout_marginTop="6dp"
+ android:layout_marginTop="4dp"
android:layout_marginRight="@dimen/very_large_spacing"
android:textSize="@dimen/small_font_size"
android:textColor="@color/text"
@@ -37,7 +37,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing"
- android:layout_marginTop="12dp"
+ android:layout_marginTop="10dp"
android:layout_marginRight="@dimen/very_large_spacing"
android:hint="Enter a display name" />
diff --git a/res/layout/activity_home.xml b/res/layout/activity_home.xml
index ce436d7b31..4079874f8a 100644
--- a/res/layout/activity_home.xml
+++ b/res/layout/activity_home.xml
@@ -21,11 +21,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
@@ -44,7 +44,7 @@
-
@@ -66,13 +66,39 @@
android:layout_height="match_parent"
android:background="@drawable/home_activity_gradient" />
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/activity_landing.xml b/res/layout/activity_landing.xml
index a1c5c7ccf1..12bf0bc789 100644
--- a/res/layout/activity_landing.xml
+++ b/res/layout/activity_landing.xml
@@ -21,7 +21,7 @@
android:textColor="@color/text"
android:text="Your Session begins here..." />
-
+ android:text="Link a Device (Beta)" />
diff --git a/res/layout/activity_register.xml b/res/layout/activity_register.xml
index 636ab61f0c..da4da09770 100644
--- a/res/layout/activity_register.xml
+++ b/res/layout/activity_register.xml
@@ -25,7 +25,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing"
- android:layout_marginTop="6dp"
+ android:layout_marginTop="4dp"
android:layout_marginRight="@dimen/very_large_spacing"
android:textSize="@dimen/small_font_size"
android:textColor="@color/text"
@@ -38,7 +38,7 @@
android:layout_height="wrap_content"
android:textSize="18dp"
android:layout_marginLeft="@dimen/very_large_spacing"
- android:layout_marginTop="12dp"
+ android:layout_marginTop="10dp"
android:layout_marginRight="@dimen/very_large_spacing"
android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" />
diff --git a/res/layout/activity_restore.xml b/res/layout/activity_restore.xml
index 70749c5186..08e6a3f341 100644
--- a/res/layout/activity_restore.xml
+++ b/res/layout/activity_restore.xml
@@ -25,7 +25,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing"
- android:layout_marginTop="6dp"
+ android:layout_marginTop="4dp"
android:layout_marginRight="@dimen/very_large_spacing"
android:textSize="@dimen/small_font_size"
android:textColor="@color/text"
@@ -37,7 +37,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing"
- android:layout_marginTop="12dp"
+ android:layout_marginTop="10dp"
android:layout_marginRight="@dimen/very_large_spacing"
android:hint="Enter your recovery phrase" />
diff --git a/res/layout/activity_seed.xml b/res/layout/activity_seed.xml
index a93318893d..db9e3df0d3 100644
--- a/res/layout/activity_seed.xml
+++ b/res/layout/activity_seed.xml
@@ -7,7 +7,7 @@
android:gravity="center_horizontal"
android:orientation="vertical">
-
@@ -22,7 +22,7 @@
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing"
android:layout_marginRight="@dimen/very_large_spacing"
- android:textSize="@dimen/large_font_size"
+ android:textSize="19sp"
android:textStyle="bold"
android:textColor="@color/text"
android:text="Meet your recovery phrase" />
@@ -31,9 +31,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing"
- android:layout_marginTop="10dp"
+ android:layout_marginTop="4dp"
android:layout_marginRight="@dimen/very_large_spacing"
- android:textSize="@dimen/medium_font_size"
+ android:textSize="14sp"
android:textColor="@color/text"
android:text="Your recovery phrase is the master key to your Session ID — you can use it to restore your Session ID if you lose access to your device. Store your recovery phrase in a safe place, and don’t give it to anyone. To restore your Session ID, launch Session and tap Continue your Session." />
@@ -43,7 +43,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/very_large_spacing"
- android:layout_marginTop="@dimen/medium_spacing"
+ android:layout_marginTop="10dp"
android:layout_marginRight="@dimen/very_large_spacing"
android:gravity="center"
android:textSize="@dimen/medium_font_size"
@@ -54,9 +54,9 @@
android:id="@+id/revealButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="12dp"
+ android:layout_marginTop="6dp"
android:textAlignment="center"
- android:textSize="@dimen/medium_font_size"
+ android:textSize="14sp"
android:textColor="@color/text"
android:alpha="0.6"
android:text="Hold to reveal" />
diff --git a/res/layout/activity_settings.xml b/res/layout/activity_settings.xml
index 3c2490af60..a575aeb636 100644
--- a/res/layout/activity_settings.xml
+++ b/res/layout/activity_settings.xml
@@ -70,7 +70,7 @@
-
-
+ android:name="org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment" />
\ No newline at end of file
diff --git a/res/layout/contact_selection_list_fragment.xml b/res/layout/contact_selection_list_fragment.xml
index 6d6536b6a7..2eb74847f8 100644
--- a/res/layout/contact_selection_list_fragment.xml
+++ b/res/layout/contact_selection_list_fragment.xml
@@ -1,92 +1,53 @@
-
-
-
+
-
-
-
-
-
-
-
+ android:gravity="center"
+ android:orientation="vertical">
-
+
-
+
+
+
+
+
+
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:scrollbars="vertical" />
-
+
-
+
-
-
-
-
-
-
diff --git a/res/layout/contact_selection_list_item.xml b/res/layout/contact_selection_list_item.xml
deleted file mode 100644
index e639a524f3..0000000000
--- a/res/layout/contact_selection_list_item.xml
+++ /dev/null
@@ -1,79 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/conversation_activity.xml b/res/layout/conversation_activity.xml
index 17cd6f5aa4..0d7c2091ce 100644
--- a/res/layout/conversation_activity.xml
+++ b/res/layout/conversation_activity.xml
@@ -104,7 +104,7 @@
android:indeterminate="false"
android:progress="0" />
-
diff --git a/res/layout/conversation_item_received.xml b/res/layout/conversation_item_received.xml
index 400377e17c..999f79089c 100644
--- a/res/layout/conversation_item_received.xml
+++ b/res/layout/conversation_item_received.xml
@@ -28,7 +28,7 @@
android:layout_height="42dp"
android:layout_alignParentStart="true">
-
-
@@ -161,7 +161,7 @@
-
diff --git a/res/layout/dialog_link_device_master_mode.xml b/res/layout/dialog_link_device_master_mode.xml
index 786385b823..a2025ddf0c 100644
--- a/res/layout/dialog_link_device_master_mode.xml
+++ b/res/layout/dialog_link_device_master_mode.xml
@@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/default_dialog_background_inset"
android:gravity="center_horizontal"
android:orientation="vertical"
@@ -12,6 +13,7 @@
android:paddingBottom="@dimen/medium_spacing">
+
+
-
diff --git a/res/layout/invite_activity.xml b/res/layout/invite_activity.xml
index f3949c7506..fccfa91128 100644
--- a/res/layout/invite_activity.xml
+++ b/res/layout/invite_activity.xml
@@ -94,7 +94,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
- android:name="org.thoughtcrime.securesms.ContactSelectionListFragment"
+ android:name="org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment"
tools:layout="@layout/contact_selection_list_fragment"/>
+ android:name="org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment" />
-
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/res/layout/view_mention_candidate_selection.xml b/res/layout/view_mention_candidate_selection.xml
index d96c222d63..00c1439ed4 100644
--- a/res/layout/view_mention_candidate_selection.xml
+++ b/res/layout/view_mention_candidate_selection.xml
@@ -1,5 +1,5 @@
-
-
@@ -52,14 +54,15 @@
+ android:layout_weight="1"
+ android:layout_marginLeft="4dp" />
diff --git a/res/layout/view_user.xml b/res/layout/view_user.xml
index 6c63b7759c..8df24dc6a5 100644
--- a/res/layout/view_user.xml
+++ b/res/layout/view_user.xml
@@ -14,7 +14,7 @@
android:gravity="center_vertical"
android:padding="@dimen/medium_spacing">
-
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 8745cd043b..480008a35a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1663,13 +1663,14 @@
Dismiss
Restore
-
-
Copied to clipboard
Are you sure you want to leave this group?
Are you sure you want to delete this conversation?
Conversation deleted
+ Contacts
+ Closed Groups
+ Open Groups
Push Notifications
There are two ways Session can handle push notifications. Make sure to read the descriptions carefully before you choose.
Firebase Cloud Messaging
diff --git a/res/xml/network_security_configuration.xml b/res/xml/network_security_configuration.xml
index b38993a466..cffc495059 100644
--- a/res/xml/network_security_configuration.xml
+++ b/res/xml/network_security_configuration.xml
@@ -1,7 +1,7 @@
- 149.56.148.124
+ 144.76.164.202
storage.seed1.loki.network
storage.seed2.loki.network
public.loki.foundation
diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java
index badff0df5c..5d018bb56d 100644
--- a/src/org/thoughtcrime/securesms/ApplicationContext.java
+++ b/src/org/thoughtcrime/securesms/ApplicationContext.java
@@ -19,10 +19,8 @@ package org.thoughtcrime.securesms;
import android.arch.lifecycle.DefaultLifecycleObserver;
import android.arch.lifecycle.LifecycleOwner;
import android.arch.lifecycle.ProcessLifecycleOwner;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.database.ContentObserver;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
@@ -41,12 +39,10 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.database.Address;
-import org.thoughtcrime.securesms.database.DatabaseContentProviders;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.dependencies.AxolotlStorageModule;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.dependencies.SignalCommunicationModule;
-import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.jobmanager.DependencyInjector;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
@@ -62,16 +58,18 @@ import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.logging.PersistentLogger;
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
-import org.thoughtcrime.securesms.loki.LokiPublicChatManager;
-import org.thoughtcrime.securesms.loki.LokiPushNotificationManager;
-import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
-import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity;
-import org.thoughtcrime.securesms.loki.redesign.messaging.BackgroundOpenGroupPollWorker;
-import org.thoughtcrime.securesms.loki.redesign.messaging.BackgroundPollWorker;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIDatabase;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiRSSFeedPoller;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiUserDatabase;
-import org.thoughtcrime.securesms.loki.redesign.utilities.Broadcaster;
+import org.thoughtcrime.securesms.loki.activities.HomeActivity;
+import org.thoughtcrime.securesms.loki.api.BackgroundOpenGroupPollWorker;
+import org.thoughtcrime.securesms.loki.api.BackgroundPollWorker;
+import org.thoughtcrime.securesms.loki.api.LokiPublicChatManager;
+import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager;
+import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
+import org.thoughtcrime.securesms.loki.protocol.EphemeralMessage;
+import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation;
+import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob;
+import org.thoughtcrime.securesms.loki.utilities.Broadcaster;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
@@ -86,6 +84,7 @@ import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
+import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.PeerConnectionFactory.InitializationOptions;
@@ -96,25 +95,29 @@ import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.util.StreamDetails;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import org.whispersystems.signalservice.loki.api.LokiAPI;
-import org.whispersystems.signalservice.loki.api.LokiAPIDatabaseProtocol;
-import org.whispersystems.signalservice.loki.api.LokiP2PAPI;
-import org.whispersystems.signalservice.loki.api.LokiP2PAPIDelegate;
import org.whispersystems.signalservice.loki.api.LokiPoller;
import org.whispersystems.signalservice.loki.api.LokiPushNotificationAcknowledgement;
import org.whispersystems.signalservice.loki.api.LokiSwarmAPI;
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
-import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat;
-import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChatAPI;
-import org.whispersystems.signalservice.loki.api.rssfeeds.LokiRSSFeed;
+import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI;
+import org.whispersystems.signalservice.loki.api.p2p.LokiP2PAPI;
+import org.whispersystems.signalservice.loki.api.p2p.LokiP2PAPIDelegate;
+import org.whispersystems.signalservice.loki.database.LokiAPIDatabaseProtocol;
+import org.whispersystems.signalservice.loki.protocol.friendrequests.FriendRequestProtocol;
+import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
+import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol;
+import org.whispersystems.signalservice.loki.protocol.multidevice.DeviceLink;
+import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
+import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocol;
+import org.whispersystems.signalservice.loki.protocol.sessionmanagement.SessionManagementProtocolDelegate;
+import org.whispersystems.signalservice.loki.protocol.syncmessages.SyncMessagesProtocol;
import java.io.File;
import java.io.FileInputStream;
import java.security.SecureRandom;
import java.security.Security;
-import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
import dagger.ObjectGraph;
@@ -132,7 +135,7 @@ import static nl.komponents.kovenant.android.KovenantAndroid.stopKovenant;
*
* @author Moxie Marlinspike
*/
-public class ApplicationContext extends MultiDexApplication implements DependencyInjector, DefaultLifecycleObserver, LokiP2PAPIDelegate {
+public class ApplicationContext extends MultiDexApplication implements DependencyInjector, DefaultLifecycleObserver, LokiP2PAPIDelegate, SessionManagementProtocolDelegate {
private static final String TAG = ApplicationContext.class.getSimpleName();
private final static int OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024; // 10 MB
@@ -146,10 +149,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
private PersistentLogger persistentLogger;
// Loki
- private LokiPoller lokiPoller = null;
- private LokiRSSFeedPoller lokiNewsFeedPoller = null;
- private LokiRSSFeedPoller lokiMessengerUpdatesFeedPoller = null;
- private LokiPublicChatManager lokiPublicChatManager = null;
+ public LokiPoller lokiPoller = null;
+ public LokiPublicChatManager lokiPublicChatManager = null;
private LokiPublicChatAPI lokiPublicChatAPI = null;
public Broadcaster broadcaster = null;
public SignalCommunicationModule communicationModule;
@@ -164,13 +165,42 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate()");
- broadcaster = new Broadcaster(this);
- checkNeedsDatabaseReset();
startKovenant();
initializeSecurityProvider();
initializeLogging();
initializeCrashHandling();
initializeDependencyInjection();
+ NotificationChannels.create(this);
+ ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
+ // Loki
+ // ========
+ broadcaster = new Broadcaster(this);
+ LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
+ LokiThreadDatabase threadDB = DatabaseFactory.getLokiThreadDatabase(this);
+ LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
+ String userPublicKey = TextSecurePreferences.getLocalNumber(this);
+ LokiSessionResetImplementation sessionResetImpl = new LokiSessionResetImplementation(this);
+ if (userPublicKey != null) {
+ FriendRequestProtocol.Companion.configureIfNeeded(apiDB, userPublicKey);
+ MentionsManager.Companion.configureIfNeeded(userPublicKey, threadDB, userDB);
+ SessionMetaProtocol.Companion.configureIfNeeded(apiDB, userPublicKey);
+ SyncMessagesProtocol.Companion.configureIfNeeded(apiDB, userPublicKey);
+ }
+ MultiDeviceProtocol.Companion.configureIfNeeded(apiDB);
+ SessionManagementProtocol.Companion.configureIfNeeded(sessionResetImpl, threadDB, this);
+ setUpP2PAPIIfNeeded();
+ LokiPushNotificationAcknowledgement.Companion.configureIfNeeded(BuildConfig.DEBUG);
+ if (setUpStorageAPIIfNeeded()) {
+ if (userPublicKey != null) {
+ Set deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userPublicKey);
+ LokiFileServerAPI.shared.setDeviceLinks(deviceLinks);
+ }
+ }
+ resubmitProfilePictureIfNeeded();
+ lokiPublicChatManager = new LokiPublicChatManager(this);
+ updateOpenGroupProfilePicturesIfNeeded();
+ registerForFCMIfNeeded(false);
+ // ========
initializeJobManager();
initializeMessageRetrieval();
initializeExpiringMessageManager();
@@ -182,30 +212,6 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
initializePendingMessages();
initializeUnidentifiedDeliveryAbilityRefresh();
initializeBlobProvider();
- NotificationChannels.create(this);
- ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
- // Loki - Set up P2P API if needed
- setUpP2PAPI();
- // Loki - Set up push notification acknowledgement
- LokiPushNotificationAcknowledgement.Companion.configureIfNeeded(BuildConfig.DEBUG);
- // Loki - Update device mappings
- if (setUpStorageAPIIfNeeded()) {
- String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
- if (userHexEncodedPublicKey != null) {
- if (TextSecurePreferences.getNeedsIsRevokedSlaveDeviceCheck(this)) {
- MultiDeviceUtilities.checkIsRevokedSlaveDevice(this);
- } else {
- // We always update our current device links onto the server in case we failed to do so upon linking
- MultiDeviceUtilities.updateDeviceLinksOnServer(this);
- }
- }
- }
- // Loki - Resubmit profile picture if needed
- resubmitProfilePictureIfNeeded();
- // Loki - Set up public chat manager
- lokiPublicChatManager = new LokiPublicChatManager(this);
- updatePublicChatProfilePictureIfNeeded();
- registerForFCMIfNeeded(false);
}
@Override
@@ -214,9 +220,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
Log.i(TAG, "App is now visible.");
executePendingContactSync();
KeyCachingService.onAppForegrounded(this);
- // Loki - Start polling if needed
+ // Loki
startPollingIfNeeded();
- // Loki - Start open group polling if needed
lokiPublicChatManager.startPollersIfNeeded();
}
@@ -226,14 +231,14 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
Log.i(TAG, "App is no longer visible.");
KeyCachingService.onAppBackgrounded(this);
MessageNotifier.setVisibleThread(-1);
- // Loki - Stop polling if needed
+ // Loki
if (lokiPoller != null) { lokiPoller.stopIfNeeded(); }
if (lokiPublicChatManager != null) { lokiPublicChatManager.stopPollers(); }
}
@Override
public void onTerminate() {
- stopKovenant();
+ stopKovenant(); // Loki
super.onTerminate();
}
@@ -268,20 +273,15 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
return persistentLogger;
}
- public LokiPublicChatManager getLokiPublicChatManager() {
- return lokiPublicChatManager;
- }
-
+ // Loki
public @Nullable LokiPublicChatAPI getLokiPublicChatAPI() {
- if (lokiPublicChatAPI == null && IdentityKeyUtil.hasIdentityKey(this)) {
- String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
- if (userHexEncodedPublicKey != null) {
- byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
- LokiAPIDatabase apiDatabase = DatabaseFactory.getLokiAPIDatabase(this);
- LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(this);
- lokiPublicChatAPI = new LokiPublicChatAPI(userHexEncodedPublicKey, userPrivateKey, apiDatabase, userDatabase);
- }
- }
+ if (lokiPublicChatAPI != null || !IdentityKeyUtil.hasIdentityKey(this)) { return lokiPublicChatAPI; }
+ String userPublicKey = TextSecurePreferences.getLocalNumber(this);
+ if (userPublicKey== null) { return lokiPublicChatAPI; }
+ byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
+ LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
+ LokiUserDatabase userDB = DatabaseFactory.getLokiUserDatabase(this);
+ lokiPublicChatAPI = new LokiPublicChatAPI(userPublicKey, userPrivateKey, apiDB, userDB);
return lokiPublicChatAPI;
}
@@ -363,8 +363,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
RotateSignedPreKeyListener.schedule(this);
LocalBackupListener.schedule(this);
RotateSenderCertificateListener.schedule(this);
- BackgroundPollWorker.schedule(this); // Session
- BackgroundOpenGroupPollWorker.schedule(this); // Session
+ BackgroundPollWorker.schedule(this); // Loki
+ BackgroundOpenGroupPollWorker.schedule(this); // Loki
if (BuildConfig.PLAY_STORE_DISABLED) {
UpdateApkRefreshListener.schedule(this);
@@ -442,18 +442,16 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
// region Loki
public boolean setUpStorageAPIIfNeeded() {
- String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
- if (userHexEncodedPublicKey != null && IdentityKeyUtil.hasIdentityKey(this)) {
- boolean isDebugMode = BuildConfig.DEBUG;
- byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
- LokiAPIDatabaseProtocol database = DatabaseFactory.getLokiAPIDatabase(this);
- LokiFileServerAPI.Companion.configure(isDebugMode, userHexEncodedPublicKey, userPrivateKey, database);
- return true;
- }
- return false;
+ String userPublicKey = TextSecurePreferences.getLocalNumber(this);
+ if (userPublicKey == null || !IdentityKeyUtil.hasIdentityKey(this)) { return false; }
+ boolean isDebugMode = BuildConfig.DEBUG;
+ byte[] userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(this).getPrivateKey().serialize();
+ LokiAPIDatabaseProtocol apiDB = DatabaseFactory.getLokiAPIDatabase(this);
+ LokiFileServerAPI.Companion.configure(isDebugMode, userPublicKey, userPrivateKey, apiDB);
+ return true;
}
- public void setUpP2PAPI() {
+ public void setUpP2PAPIIfNeeded() {
String hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
if (hexEncodedPublicKey == null) { return; }
LokiP2PAPI.Companion.configure(hexEncodedPublicKey, (isOnline, contactPublicKey) -> {
@@ -470,10 +468,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
return;
}
String token = task.getResult().getToken();
- String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
- if (userHexEncodedPublicKey == null) return;
+ String userPublicKey = TextSecurePreferences.getLocalNumber(context);
+ if (userPublicKey == null) return;
if (TextSecurePreferences.isUsingFCM(this)) {
- LokiPushNotificationManager.register(token, userHexEncodedPublicKey, context, force);
+ LokiPushNotificationManager.register(token, userPublicKey, context, force);
} else {
LokiPushNotificationManager.unregister(token, context);
}
@@ -487,15 +485,15 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
private void setUpPollingIfNeeded() {
if (lokiPoller != null) return;
- String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
- if (userHexEncodedPublicKey == null) return;
- LokiAPIDatabase lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(this);
+ String userPublicKey = TextSecurePreferences.getLocalNumber(this);
+ if (userPublicKey == null) return;
+ LokiAPIDatabase apiDB = DatabaseFactory.getLokiAPIDatabase(this);
Context context = this;
- LokiSwarmAPI.Companion.configureIfNeeded(lokiAPIDatabase);
- LokiAPI.Companion.configureIfNeeded(userHexEncodedPublicKey, lokiAPIDatabase, broadcaster);
- lokiPoller = new LokiPoller(userHexEncodedPublicKey, lokiAPIDatabase, protos -> {
+ LokiSwarmAPI.Companion.configureIfNeeded(apiDB);
+ LokiAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster);
+ lokiPoller = new LokiPoller(userPublicKey, apiDB, protos -> {
for (SignalServiceProtos.Envelope proto : protos) {
- new PushContentReceiveJob(context).processEnvelope(new SignalServiceEnvelope(proto));
+ new PushContentReceiveJob(context).processEnvelope(new SignalServiceEnvelope(proto), false);
}
return Unit.INSTANCE;
});
@@ -506,94 +504,9 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
if (lokiPoller != null) { lokiPoller.startIfNeeded(); }
}
- private LokiRSSFeed lokiNewsFeed() {
- return new LokiRSSFeed("loki.network.feed", "https://loki.network/feed/", "Loki News", true);
- }
-
- private LokiRSSFeed lokiMessengerUpdatesFeed() {
- return new LokiRSSFeed("loki.network.messenger-updates.feed", "https://loki.network/category/messenger-updates/feed", "Session Updates", false);
- }
-
- public void createDefaultPublicChatsIfNeeded() {
- List defaultPublicChats = LokiPublicChatAPI.Companion.getDefaultChats(BuildConfig.DEBUG);
- for (LokiPublicChat publicChat : defaultPublicChats) {
- long threadID = GroupManager.getPublicChatThreadId(publicChat.getId(), this);
- String migrationKey = publicChat.getId() + "_migrated";
- boolean isChatMigrated = TextSecurePreferences.getBooleanPreference(this, migrationKey, false);
- boolean isChatSetUp = TextSecurePreferences.isChatSetUp(this, publicChat.getId());
- if (!isChatSetUp || !publicChat.isDeletable()) {
- lokiPublicChatManager.addChat(publicChat.getServer(), publicChat.getChannel(), publicChat.getDisplayName());
- TextSecurePreferences.markChatSetUp(this, publicChat.getId());
- TextSecurePreferences.setBooleanPreference(this, migrationKey, true);
- } else if (threadID > -1 && !isChatMigrated) {
- // Migrate the old public chats
- DatabaseFactory.getLokiThreadDatabase(this).setPublicChat(publicChat, threadID);
- TextSecurePreferences.setBooleanPreference(this, migrationKey, true);
- }
- }
- }
-
- public void createRSSFeedsIfNeeded() {
- ArrayList feeds = new ArrayList<>();
-// feeds.add(lokiNewsFeed());
- feeds.add(lokiMessengerUpdatesFeed());
- for (LokiRSSFeed feed : feeds) {
- boolean isFeedSetUp = TextSecurePreferences.isChatSetUp(this, feed.getId());
- if (!isFeedSetUp || !feed.isDeletable()) {
- GroupManager.createRSSFeedGroup(feed.getId(), this, null, feed.getDisplayName());
- TextSecurePreferences.markChatSetUp(this, feed.getId());
- }
- }
- }
-
- private void createRSSFeedPollersIfNeeded() {
- // Only create the RSS feed pollers if their threads aren't deleted
- LokiRSSFeed lokiNewsFeed = lokiNewsFeed();
- long lokiNewsFeedThreadID = GroupManager.getRSSFeedThreadId(lokiNewsFeed.getId(), this);
- if (lokiNewsFeedThreadID >= 0 && lokiNewsFeedPoller == null) {
- lokiNewsFeedPoller = new LokiRSSFeedPoller(this, lokiNewsFeed);
- // Set up deletion listeners if needed
- setUpThreadDeletionListeners(lokiNewsFeedThreadID, () -> {
- if (lokiNewsFeedPoller != null) lokiNewsFeedPoller.stop();
- lokiNewsFeedPoller = null;
- });
- }
- // The user can't delete the Session Updates RSS feed
- if (lokiMessengerUpdatesFeedPoller == null) {
- lokiMessengerUpdatesFeedPoller = new LokiRSSFeedPoller(this, lokiMessengerUpdatesFeed());
- }
- }
-
- private void setUpThreadDeletionListeners(long threadID, Runnable onDelete) {
- if (threadID < 0) { return; }
- ContentObserver observer = new ContentObserver(null) {
-
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- // Stop the poller if thread is deleted
- try {
- if (!DatabaseFactory.getThreadDatabase(getApplicationContext()).hasThread(threadID)) {
- onDelete.run();
- getContentResolver().unregisterContentObserver(this);
- }
- } catch (Exception e) {
- // TODO: Handle
- }
- }
- };
- this.getContentResolver().registerContentObserver(DatabaseContentProviders.Conversation.getUriForThread(threadID), true, observer);
- }
-
- public void startRSSFeedPollersIfNeeded() {
- createRSSFeedPollersIfNeeded();
- if (lokiNewsFeedPoller != null) lokiNewsFeedPoller.startIfNeeded();
- if (lokiMessengerUpdatesFeedPoller != null) lokiMessengerUpdatesFeedPoller.startIfNeeded();
- }
-
private void resubmitProfilePictureIfNeeded() {
- String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
- if (userHexEncodedPublicKey == null) return;
+ String userPublicKey = TextSecurePreferences.getLocalNumber(this);
+ if (userPublicKey == null) return;
long now = new Date().getTime();
long lastProfilePictureUpload = TextSecurePreferences.getLastProfilePictureUpload(this);
if (now - lastProfilePictureUpload <= 14 * 24 * 60 * 60 * 1000) return;
@@ -601,7 +514,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
String encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this);
byte[] profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey);
try {
- File profilePicture = AvatarHelper.getAvatarFile(this, Address.fromSerialized(userHexEncodedPublicKey));
+ File profilePicture = AvatarHelper.getAvatarFile(this, Address.fromSerialized(userPublicKey));
StreamDetails stream = new StreamDetails(new FileInputStream(profilePicture), "image/jpeg", profilePicture.length());
LokiFileServerAPI.shared.uploadProfilePicture(LokiFileServerAPI.shared.getServer(), profileKey, stream, () -> {
TextSecurePreferences.setLastProfilePictureUpload(this, new Date().getTime());
@@ -615,7 +528,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
});
}
- public void updatePublicChatProfilePictureIfNeeded() {
+ public void updateOpenGroupProfilePicturesIfNeeded() {
AsyncTask.execute(() -> {
LokiPublicChatAPI publicChatAPI = null;
try {
@@ -623,48 +536,46 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
} catch (Exception e) {
// Do nothing
}
- if (publicChatAPI != null) {
- byte[] profileKey = ProfileKeyUtil.getProfileKey(this);
- String url = TextSecurePreferences.getProfileAvatarUrl(this);
- String ourMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(this);
- if (ourMasterDevice != null) {
- Recipient masterDevice = Recipient.from(this, Address.fromSerialized(ourMasterDevice), false).resolve();
- profileKey = masterDevice.getProfileKey();
- url = masterDevice.getProfileAvatar();
- }
- Set servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers();
- for (String server : servers) {
- if (profileKey != null) {
- publicChatAPI.setProfilePicture(server, profileKey, url);
- }
+ if (publicChatAPI == null) { return; }
+ byte[] profileKey = ProfileKeyUtil.getProfileKey(this);
+ String url = TextSecurePreferences.getProfilePictureURL(this);
+ String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(this);
+ if (userMasterDevice != null) {
+ Recipient userMasterDeviceAsRecipient = Recipient.from(this, Address.fromSerialized(userMasterDevice), false).resolve();
+ profileKey = userMasterDeviceAsRecipient.getProfileKey();
+ url = userMasterDeviceAsRecipient.getProfileAvatar();
+ }
+ Set servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers();
+ for (String server : servers) {
+ if (profileKey != null) {
+ publicChatAPI.setProfilePicture(server, profileKey, url);
}
}
});
}
- public void checkNeedsDatabaseReset() {
- if (TextSecurePreferences.resetDatabase(this)) {
- boolean wasUnlinked = TextSecurePreferences.databaseResetFromUnpair(this);
- TextSecurePreferences.clearAll(this);
- TextSecurePreferences.setDatabaseResetFromUnpair(this, wasUnlinked); // Loki - Re-set the preference so we can use it in the starting screen to determine whether device was unlinked or not
- MasterSecretUtil.clear(this);
- if (this.deleteDatabase("signal.db")) {
- Log.d("Loki", "Deleted database");
- }
- }
- }
-
public void clearData() {
- TextSecurePreferences.setResetDatabase(this, true);
- new Handler().postDelayed(this::restartApplication, 200);
+ boolean wasUnlinked = TextSecurePreferences.getWasUnlinked(this);
+ TextSecurePreferences.clearAll(this);
+ TextSecurePreferences.setWasUnlinked(this, wasUnlinked);
+ MasterSecretUtil.clear(this);
+ if (!deleteDatabase("signal.db")) {
+ Log.d("Loki", "Failed to delete database.");
+ }
+ Util.runOnMain(() -> new Handler().postDelayed(ApplicationContext.this::restartApplication, 200));
}
public void restartApplication() {
Intent intent = new Intent(this, HomeActivity.class);
- ComponentName componentName = intent.getComponent();
- Intent mainIntent = Intent.makeRestartActivityTask(componentName);
- this.startActivity(mainIntent);
+ startActivity(Intent.makeRestartActivityTask(intent.getComponent()));
Runtime.getRuntime().exit(0);
}
+
+ @Override
+ public void sendSessionRequest(@NotNull String publicKey) {
+ DatabaseFactory.getLokiAPIDatabase(this).setSessionRequestTimestamp(publicKey, new Date().getTime());
+ EphemeralMessage sessionRequest = EphemeralMessage.createSessionRequest(publicKey);
+ jobManager.add(new PushEphemeralMessageSendJob(sessionRequest));
+ }
// endregion
}
diff --git a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java
index 86c436f345..fdf751a0f2 100644
--- a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java
+++ b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java
@@ -41,7 +41,7 @@ import android.util.Log;
import android.widget.Toast;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
-import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity;
+import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment;
import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment;
@@ -345,16 +345,16 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
}
String seed = new MnemonicCodec(languageFileDirectory).encode(hexEncodedSeed, MnemonicCodec.Language.Configuration.Companion.getEnglish());
new AlertDialog.Builder(getContext())
- .setTitle(R.string.activity_settings_seed_dialog_title)
- .setMessage(seed)
- .setPositiveButton(R.string.activity_settings_seed_dialog_copy_button_title, (DialogInterface.OnClickListener) (dialog, which) -> {
- ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
- ClipData clip = ClipData.newPlainText("seed", seed);
- clipboard.setPrimaryClip(clip);
- Toast.makeText(getContext(), R.string.activity_settings_seed_copied_message, Toast.LENGTH_SHORT).show();
- })
- .setNeutralButton(R.string.activity_settings_seed_dialog_ok_button_title, null)
- .show();
+ .setTitle(R.string.activity_settings_seed_dialog_title)
+ .setMessage(seed)
+ .setPositiveButton(R.string.activity_settings_seed_dialog_copy_button_title, (DialogInterface.OnClickListener) (dialog, which) -> {
+ ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clip = ClipData.newPlainText("seed", seed);
+ clipboard.setPrimaryClip(clip);
+ Toast.makeText(getContext(), R.string.activity_settings_seed_copied_message, Toast.LENGTH_SHORT).show();
+ })
+ .setNeutralButton(R.string.activity_settings_seed_dialog_ok_button_title, null)
+ .show();
} catch (Exception e) {
Log.d("Loki", e.getMessage());
}
diff --git a/src/org/thoughtcrime/securesms/ContactSelectionActivity.java b/src/org/thoughtcrime/securesms/ContactSelectionActivity.java
index e408709009..ccf0c98df7 100644
--- a/src/org/thoughtcrime/securesms/ContactSelectionActivity.java
+++ b/src/org/thoughtcrime/securesms/ContactSelectionActivity.java
@@ -20,11 +20,11 @@ import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
-import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
+import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment;
+import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListLoader.DisplayMode;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
-import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import network.loki.messenger.R;
@@ -36,8 +36,8 @@ import network.loki.messenger.R;
*
*/
public abstract class ContactSelectionActivity extends PassphraseRequiredActionBarActivity
- implements SwipeRefreshLayout.OnRefreshListener,
- ContactSelectionListFragment.OnContactSelectedListener
+ implements SwipeRefreshLayout.OnRefreshListener,
+ ContactSelectionListFragment.OnContactSelectedListener
{
private static final String TAG = ContactSelectionActivity.class.getSimpleName();
@@ -57,9 +57,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
@Override
protected void onCreate(Bundle icicle, boolean ready) {
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
- int displayMode = TextSecurePreferences.isSmsEnabled(this) ? DisplayMode.FLAG_ALL
- : DisplayMode.FLAG_PUSH | DisplayMode.FLAG_GROUPS;
- getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, displayMode);
+ getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_ALL);
}
setContentView(R.layout.contact_selection_activity);
diff --git a/src/org/thoughtcrime/securesms/ContactSelectionListFragment.java b/src/org/thoughtcrime/securesms/ContactSelectionListFragment.java
deleted file mode 100644
index c95359516f..0000000000
--- a/src/org/thoughtcrime/securesms/ContactSelectionListFragment.java
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * Copyright (C) 2015 Open Whisper Systems
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package org.thoughtcrime.securesms;
-
-
-import android.annotation.SuppressLint;
-import android.database.Cursor;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.LoaderManager;
-import android.support.v4.content.Loader;
-import android.support.v4.widget.SwipeRefreshLayout;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.pnikosis.materialishprogress.ProgressWheel;
-
-import org.thoughtcrime.securesms.components.RecyclerViewFastScroller;
-import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
-import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
-import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
-import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
-import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
-import org.thoughtcrime.securesms.mms.GlideApp;
-import org.thoughtcrime.securesms.permissions.Permissions;
-import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
-import org.thoughtcrime.securesms.util.ViewUtil;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
-import network.loki.messenger.R;
-
-/**
- * Fragment for selecting a one or more contacts from a list.
- *
- * @author Moxie Marlinspike
- *
- */
-public class ContactSelectionListFragment extends Fragment
- implements LoaderManager.LoaderCallbacks
-{
- @SuppressWarnings("unused")
- private static final String TAG = ContactSelectionListFragment.class.getSimpleName();
-
- public static final String DISPLAY_MODE = "display_mode";
- public static final String MULTI_SELECT = "multi_select";
- public static final String REFRESHABLE = "refreshable";
- public static final String RECENTS = "recents";
-
- private TextView emptyText;
- private Set selectedContacts;
- private OnContactSelectedListener onContactSelectedListener;
- private SwipeRefreshLayout swipeRefresh;
- private View showContactsLayout;
- private Button showContactsButton;
- private TextView showContactsDescription;
- private ProgressWheel showContactsProgress;
- private String cursorFilter;
- private RecyclerView recyclerView;
- private RecyclerViewFastScroller fastScroller;
-
- @Override
- public void onActivityCreated(Bundle icicle) {
- super.onActivityCreated(icicle);
-
- initializeCursor();
- }
-
- @Override
- public void onStart() {
- super.onStart();
-
- handleContactPermissionGranted();
-
-// Permissions.with(this)
-// .request(Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CONTACTS)
-// .ifNecessary()
-// .onAllGranted(() -> {
-// if (!TextSecurePreferences.hasSuccessfullyRetrievedDirectory(getActivity())) {
-// handleContactPermissionGranted();
-// } else {
-// this.getLoaderManager().initLoader(0, null, this);
-// }
-// })
-// .onAnyDenied(() -> {
-// getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
-//
-// if (getActivity().getIntent().getBooleanExtra(RECENTS, false)) {
-// getLoaderManager().initLoader(0, null, ContactSelectionListFragment.this);
-// } else {
-// initializeNoContactsPermission();
-// }
-// })
-// .execute();
- }
-
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.contact_selection_list_fragment, container, false);
-
- emptyText = ViewUtil.findById(view, android.R.id.empty);
- recyclerView = ViewUtil.findById(view, R.id.recycler_view);
- swipeRefresh = ViewUtil.findById(view, R.id.swipe_refresh);
- fastScroller = ViewUtil.findById(view, R.id.fast_scroller);
- showContactsLayout = view.findViewById(R.id.show_contacts_container);
- showContactsButton = view.findViewById(R.id.show_contacts_button);
- showContactsDescription = view.findViewById(R.id.show_contacts_description);
- showContactsProgress = view.findViewById(R.id.progress);
- recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
-
- swipeRefresh.setEnabled(getActivity().getIntent().getBooleanExtra(REFRESHABLE, true));
-
- return view;
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
- }
-
- public @NonNull List getSelectedContacts() {
- List selected = new LinkedList<>();
- if (selectedContacts != null) {
- selected.addAll(selectedContacts);
- }
-
- return selected;
- }
-
- private boolean isMulti() {
- return getActivity().getIntent().getBooleanExtra(MULTI_SELECT, false);
- }
-
- private void initializeCursor() {
- ContactSelectionListAdapter adapter = new ContactSelectionListAdapter(getActivity(),
- GlideApp.with(this),
- null,
- new ListClickListener(),
- isMulti());
- selectedContacts = adapter.getSelectedContacts();
- recyclerView.setAdapter(adapter);
- recyclerView.addItemDecoration(new StickyHeaderDecoration(adapter, true, true));
- }
-
- private void initializeNoContactsPermission() {
- swipeRefresh.setVisibility(View.GONE);
-
- showContactsLayout.setVisibility(View.VISIBLE);
- showContactsProgress.setVisibility(View.INVISIBLE);
- showContactsDescription.setText(R.string.contact_selection_list_fragment__signal_needs_access_to_your_contacts_in_order_to_display_them);
- showContactsButton.setVisibility(View.VISIBLE);
-
- /*
- showContactsButton.setOnClickListener(v -> {
- Permissions.with(this)
- .request(Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CONTACTS)
- .ifNecessary()
- .withPermanentDenialDialog(getString(R.string.ContactSelectionListFragment_signal_requires_the_contacts_permission_in_order_to_display_your_contacts))
- .onSomeGranted(permissions -> {
- if (permissions.contains(Manifest.permission.WRITE_CONTACTS)) {
- handleContactPermissionGranted();
- }
- })
- .execute();
- });
- */
- }
-
- public void setQueryFilter(String filter) {
- this.cursorFilter = filter;
- this.getLoaderManager().restartLoader(0, null, this);
- }
-
- public void resetQueryFilter() {
- setQueryFilter(null);
- swipeRefresh.setRefreshing(false);
- }
-
- public void setRefreshing(boolean refreshing) {
- swipeRefresh.setRefreshing(refreshing);
- }
-
- public void reset() {
- selectedContacts.clear();
-
- if (!isDetached() && !isRemoving() && getActivity() != null && !getActivity().isFinishing()) {
- getLoaderManager().restartLoader(0, null, this);
- }
- }
-
- @Override
- public @NonNull Loader onCreateLoader(int id, Bundle args) {
- return new ContactsCursorLoader(getActivity(),
- getActivity().getIntent().getIntExtra(DISPLAY_MODE, DisplayMode.FLAG_ALL),
- cursorFilter, getActivity().getIntent().getBooleanExtra(RECENTS, false));
- }
-
- @Override
- public void onLoadFinished(@NonNull Loader loader, Cursor data) {
- swipeRefresh.setVisibility(View.VISIBLE);
- showContactsLayout.setVisibility(View.GONE);
-
- ((CursorRecyclerViewAdapter) recyclerView.getAdapter()).changeCursor(data);
- emptyText.setText(R.string.contact_selection_group_activity__no_contacts);
- boolean useFastScroller = (recyclerView.getAdapter().getItemCount() > 20);
- recyclerView.setVerticalScrollBarEnabled(!useFastScroller);
- if (useFastScroller) {
- fastScroller.setVisibility(View.VISIBLE);
- fastScroller.setRecyclerView(recyclerView);
- }
- }
-
- @Override
- public void onLoaderReset(@NonNull Loader loader) {
- ((CursorRecyclerViewAdapter) recyclerView.getAdapter()).changeCursor(null);
- fastScroller.setVisibility(View.GONE);
- }
-
- @SuppressLint("StaticFieldLeak")
- private void handleContactPermissionGranted() {
- new AsyncTask() {
- @Override
- protected void onPreExecute() {
- swipeRefresh.setVisibility(View.GONE);
- showContactsLayout.setVisibility(View.VISIBLE);
- showContactsButton.setVisibility(View.INVISIBLE);
- showContactsDescription.setText(R.string.ConversationListFragment_loading);
- showContactsProgress.setVisibility(View.VISIBLE);
- showContactsProgress.spin();
- }
-
- @Override
- protected Boolean doInBackground(Void... voids) {
- return false;
- }
-
- @Override
- protected void onPostExecute(Boolean result) {
- if (result) {
- showContactsLayout.setVisibility(View.GONE);
- swipeRefresh.setVisibility(View.VISIBLE);
- reset();
- } else {
- Toast.makeText(getContext(), R.string.ContactSelectionListFragment_error_retrieving_contacts_check_your_network_connection, Toast.LENGTH_LONG).show();
- initializeNoContactsPermission();
- }
- }
- }.execute();
- }
-
- private class ListClickListener implements ContactSelectionListAdapter.ItemClickListener {
- @Override
- public void onItemClick(ContactSelectionListItem contact) {
- if (!isMulti() || !selectedContacts.contains(contact.getNumber())) {
- selectedContacts.add(contact.getNumber());
- contact.setChecked(true);
- if (onContactSelectedListener != null) onContactSelectedListener.onContactSelected(contact.getNumber());
- } else {
- selectedContacts.remove(contact.getNumber());
- contact.setChecked(false);
- if (onContactSelectedListener != null) onContactSelectedListener.onContactDeselected(contact.getNumber());
- }
- }
- }
-
- public void setOnContactSelectedListener(OnContactSelectedListener onContactSelectedListener) {
- this.onContactSelectedListener = onContactSelectedListener;
- }
-
- public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener onRefreshListener) {
- this.swipeRefresh.setOnRefreshListener(onRefreshListener);
- }
-
- public interface OnContactSelectedListener {
- void onContactSelected(String number);
- void onContactDeselected(String number);
- }
-
-}
diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java
index 5b7dfdb697..49fc125778 100644
--- a/src/org/thoughtcrime/securesms/ConversationListActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java
@@ -49,8 +49,8 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.lock.RegistrationLockDialog;
-import org.thoughtcrime.securesms.loki.RecipientAvatarModifiedEvent;
-import org.thoughtcrime.securesms.loki.redesign.activities.JoinPublicChatActivity;
+import org.thoughtcrime.securesms.loki.utilities.ProfilePictureModifiedEvent;
+import org.thoughtcrime.securesms.loki.activities.JoinPublicChatActivity;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
@@ -86,13 +86,6 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
protected void onPreCreate() {
dynamicTheme.onCreate(this);
dynamicLanguage.onCreate(this);
- if (TextSecurePreferences.getLocalNumber(this) != null) {
- ApplicationContext application = ApplicationContext.getInstance(this);
- application.createDefaultPublicChatsIfNeeded();
- application.createRSSFeedsIfNeeded();
- application.getLokiPublicChatManager().startPollersIfNeeded();
- application.startRSSFeedPollersIfNeeded();
- }
}
@Override
@@ -330,9 +323,9 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
}
@Subscribe(threadMode = ThreadMode.MAIN)
- public void onAvatarModified(RecipientAvatarModifiedEvent event) {
+ public void onAvatarModified(ProfilePictureModifiedEvent event) {
Recipient recipient = event.getRecipient();
- if (recipient.isLocalNumber() || recipient.isOurMasterDevice()) {
+ if (recipient.isLocalNumber() || recipient.isUserMasterDevice()) {
initializeProfileIcon(recipient);
}
}
diff --git a/src/org/thoughtcrime/securesms/ConversationListFragment.java b/src/org/thoughtcrime/securesms/ConversationListFragment.java
index 404e464f59..d96d9860b7 100644
--- a/src/org/thoughtcrime/securesms/ConversationListFragment.java
+++ b/src/org/thoughtcrime/securesms/ConversationListFragment.java
@@ -74,7 +74,7 @@ import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.loaders.ConversationListLoader;
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob;
-import org.thoughtcrime.securesms.loki.redesign.activities.CreatePrivateChatActivity;
+import org.thoughtcrime.securesms.loki.activities.CreatePrivateChatActivity;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
diff --git a/src/org/thoughtcrime/securesms/ConversationListItem.java b/src/org/thoughtcrime/securesms/ConversationListItem.java
index 5cc0a74711..aabad17223 100644
--- a/src/org/thoughtcrime/securesms/ConversationListItem.java
+++ b/src/org/thoughtcrime/securesms/ConversationListItem.java
@@ -37,8 +37,8 @@ import org.thoughtcrime.securesms.components.FromTextView;
import org.thoughtcrime.securesms.components.ThumbnailView;
import org.thoughtcrime.securesms.components.TypingIndicatorView;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIUtilities;
-import org.thoughtcrime.securesms.loki.redesign.utilities.MentionUtilities;
+import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
+import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
@@ -272,7 +272,7 @@ public class ConversationListItem extends RelativeLayout
}
private @NonNull CharSequence getTrimmedSnippet(@NonNull CharSequence snippet) {
- LokiAPIUtilities.INSTANCE.populateUserHexEncodedPublicKeyCacheIfNeeded(threadId, getContext()); // TODO: Terrible place to do this, but okay for now
+ MentionManagerUtilities.INSTANCE.populateUserHexEncodedPublicKeyCacheIfNeeded(threadId, getContext()); // TODO: Terrible place to do this, but okay for now
snippet = MentionUtilities.highlightMentions(snippet, threadId, getContext());
return snippet.length() <= MAX_SNIPPET_LENGTH ? snippet : snippet.subSequence(0, MAX_SNIPPET_LENGTH);
}
diff --git a/src/org/thoughtcrime/securesms/CreateProfileActivity.java b/src/org/thoughtcrime/securesms/CreateProfileActivity.java
index d0db3ae370..62eb077481 100644
--- a/src/org/thoughtcrime/securesms/CreateProfileActivity.java
+++ b/src/org/thoughtcrime/securesms/CreateProfileActivity.java
@@ -59,7 +59,7 @@ import org.whispersystems.signalservice.api.crypto.ProfileCipher;
import org.whispersystems.signalservice.api.util.StreamDetails;
import org.whispersystems.signalservice.loki.api.LokiDotNetAPI;
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
-import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChatAPI;
+import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -406,7 +406,7 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
String newProfileKey = ProfileKeyUtil.generateEncodedProfileKey(context);
byte[] profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(newProfileKey);
- //Loki - Upload the profile photo here
+ // Loki - Upload the profile photo here
if (avatar != null) {
Log.d("Loki", "Start uploading profile photo");
LokiFileServerAPI storageAPI = LokiFileServerAPI.shared;
@@ -415,9 +415,9 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
return Unit.INSTANCE;
});
Log.d("Loki", "Profile photo uploaded, the url is " + result.getUrl());
- TextSecurePreferences.setProfileAvatarUrl(context, result.getUrl());
+ TextSecurePreferences.setProfilePictureURL(context, result.getUrl());
} else {
- TextSecurePreferences.setProfileAvatarUrl(context, null);
+ TextSecurePreferences.setProfilePictureURL(context, null);
}
AvatarHelper.setAvatar(context, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), avatarBytes);
@@ -427,7 +427,7 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
ProfileKeyUtil.setEncodedProfileKey(context, newProfileKey);
// Update profile key on the public chat server
- ApplicationContext.getInstance(context).updatePublicChatProfilePictureIfNeeded();
+ ApplicationContext.getInstance(context).updateOpenGroupProfilePicturesIfNeeded();
} catch (Exception e) {
Log.d("Loki", "Failed to upload profile photo: " + e);
return false;
diff --git a/src/org/thoughtcrime/securesms/DatabaseMigrationActivity.java b/src/org/thoughtcrime/securesms/DatabaseMigrationActivity.java
index c994add111..d7c1031635 100644
--- a/src/org/thoughtcrime/securesms/DatabaseMigrationActivity.java
+++ b/src/org/thoughtcrime/securesms/DatabaseMigrationActivity.java
@@ -18,7 +18,7 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import org.thoughtcrime.securesms.database.SmsMigrator.ProgressDescription;
-import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity;
+import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.service.ApplicationMigrationService;
import org.thoughtcrime.securesms.service.ApplicationMigrationService.ImportState;
diff --git a/src/org/thoughtcrime/securesms/DeviceListFragment.java b/src/org/thoughtcrime/securesms/DeviceListFragment.java
index d725522e3d..b3895b93ae 100644
--- a/src/org/thoughtcrime/securesms/DeviceListFragment.java
+++ b/src/org/thoughtcrime/securesms/DeviceListFragment.java
@@ -25,8 +25,8 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.loaders.DeviceListLoader;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.devicelist.Device;
-import org.thoughtcrime.securesms.loki.redesign.dialogs.DeviceEditingOptionsBottomSheet;
-import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities;
+import org.thoughtcrime.securesms.loki.dialogs.DeviceEditingOptionsBottomSheet;
+import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.whispersystems.libsignal.util.guava.Function;
@@ -39,7 +39,7 @@ import kotlin.Pair;
import kotlin.Unit;
import network.loki.messenger.R;
-import static org.thoughtcrime.securesms.loki.GeneralUtilitiesKt.toPx;
+import static org.thoughtcrime.securesms.loki.utilities.GeneralUtilitiesKt.toPx;
public class DeviceListFragment extends ListFragment
implements LoaderManager.LoaderCallbacks>,
diff --git a/src/org/thoughtcrime/securesms/ExperienceUpgradeActivity.java b/src/org/thoughtcrime/securesms/ExperienceUpgradeActivity.java
index 467d5b8da4..648fcf38c8 100644
--- a/src/org/thoughtcrime/securesms/ExperienceUpgradeActivity.java
+++ b/src/org/thoughtcrime/securesms/ExperienceUpgradeActivity.java
@@ -19,7 +19,7 @@ import com.nineoldandroids.animation.ArgbEvaluator;
import org.thoughtcrime.securesms.IntroPagerAdapter.IntroPage;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity;
+import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.util.ServiceUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
diff --git a/src/org/thoughtcrime/securesms/GroupCreateActivity.java b/src/org/thoughtcrime/securesms/GroupCreateActivity.java
index d87946c97d..ede18c5061 100644
--- a/src/org/thoughtcrime/securesms/GroupCreateActivity.java
+++ b/src/org/thoughtcrime/securesms/GroupCreateActivity.java
@@ -43,7 +43,6 @@ import com.bumptech.glide.request.transition.Transition;
import org.thoughtcrime.securesms.avatar.AvatarSelection;
import org.thoughtcrime.securesms.components.PushRecipientsPanel;
import org.thoughtcrime.securesms.components.PushRecipientsPanel.RecipientsPanelChangedListener;
-import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
@@ -57,6 +56,8 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult;
import org.thoughtcrime.securesms.logging.Log;
+import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment;
+import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListLoader.DisplayMode;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.BitmapUtil;
@@ -321,11 +322,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
@Override
public void onClick(View v) {
Intent intent = new Intent(GroupCreateActivity.this, PushContactSelectionActivity.class);
- if (groupToUpdate.isPresent()) {
- intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_PUSH);
- } else {
- intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_PUSH | DisplayMode.FLAG_SMS);
- }
+ intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_FRIENDS);
startActivityForResult(intent, PICK_CONTACT);
}
}
diff --git a/src/org/thoughtcrime/securesms/InviteActivity.java b/src/org/thoughtcrime/securesms/InviteActivity.java
index 2aab713921..4fbb2bd052 100644
--- a/src/org/thoughtcrime/securesms/InviteActivity.java
+++ b/src/org/thoughtcrime/securesms/InviteActivity.java
@@ -26,9 +26,10 @@ import android.widget.Toast;
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener;
-import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
+import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment;
+import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListLoader.DisplayMode;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
@@ -40,7 +41,7 @@ import java.util.concurrent.ExecutionException;
import network.loki.messenger.R;
-public class InviteActivity extends PassphraseRequiredActionBarActivity implements ContactSelectionListFragment.OnContactSelectedListener {
+public class InviteActivity extends PassphraseRequiredActionBarActivity {
private ContactSelectionListFragment contactsFragment;
private EditText inviteText;
@@ -52,7 +53,7 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
@Override
protected void onCreate(Bundle savedInstanceState, boolean ready) {
- getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_SMS);
+ getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_FRIENDS);
getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, true);
getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false);
@@ -84,7 +85,6 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
heart.getViewTreeObserver().addOnPreDrawListener(new HeartPreDrawListener());
}
- contactsFragment.setOnContactSelectedListener(this);
shareButton.setOnClickListener(new ShareClickListener());
smsButton.setOnClickListener(new SmsClickListener());
smsCancelButton.setOnClickListener(new SmsCancelClickListener());
@@ -99,12 +99,10 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
return animation;
}
- @Override
public void onContactSelected(String number) {
updateSmsButtonText();
}
- @Override
public void onContactDeselected(String number) {
updateSmsButtonText();
}
@@ -132,7 +130,6 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
}
private void cancelSmsSelection() {
- contactsFragment.reset();
updateSmsButtonText();
ViewUtil.animateOut(smsSendFrame, slideOutAnimation, View.GONE);
}
@@ -241,7 +238,6 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
ViewUtil.animateOut(smsSendFrame, slideOutAnimation, View.GONE).addListener(new Listener() {
@Override
public void onSuccess(Boolean result) {
- contactsFragment.reset();
}
@Override
diff --git a/src/org/thoughtcrime/securesms/MessageDetailsActivity.java b/src/org/thoughtcrime/securesms/MessageDetailsActivity.java
index 3de789d491..147299bbe0 100644
--- a/src/org/thoughtcrime/securesms/MessageDetailsActivity.java
+++ b/src/org/thoughtcrime/securesms/MessageDetailsActivity.java
@@ -47,7 +47,7 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.loaders.MessageDetailsLoader;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.loki.LokiMessageDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
diff --git a/src/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java b/src/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java
index 4203041016..b3e4691e78 100644
--- a/src/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java
+++ b/src/org/thoughtcrime/securesms/PassphraseRequiredActionBarActivity.java
@@ -13,9 +13,9 @@ import android.support.v4.app.Fragment;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity;
-import org.thoughtcrime.securesms.loki.redesign.activities.LandingActivity;
-import org.thoughtcrime.securesms.loki.redesign.activities.SeedActivity;
+import org.thoughtcrime.securesms.loki.activities.HomeActivity;
+import org.thoughtcrime.securesms.loki.activities.LandingActivity;
+import org.thoughtcrime.securesms.loki.activities.SeedActivity;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
diff --git a/src/org/thoughtcrime/securesms/PushContactSelectionActivity.java b/src/org/thoughtcrime/securesms/PushContactSelectionActivity.java
index ea64762e8e..9dff2a69ce 100644
--- a/src/org/thoughtcrime/securesms/PushContactSelectionActivity.java
+++ b/src/org/thoughtcrime/securesms/PushContactSelectionActivity.java
@@ -19,6 +19,8 @@ package org.thoughtcrime.securesms;
import android.content.Intent;
import android.os.Bundle;
+import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment;
+
import java.util.ArrayList;
import java.util.List;
diff --git a/src/org/thoughtcrime/securesms/RegistrationActivity.java b/src/org/thoughtcrime/securesms/RegistrationActivity.java
index 6f5ba224be..26395e4fcd 100644
--- a/src/org/thoughtcrime/securesms/RegistrationActivity.java
+++ b/src/org/thoughtcrime/securesms/RegistrationActivity.java
@@ -618,7 +618,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements Verif
unidentifiedAccessKey, universalUnidentifiedAccess);
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(RegistrationActivity.this);
- List records = PreKeyUtil.generatePreKeys(RegistrationActivity.this);
+ List records = PreKeyUtil.generatePreKeyRecords(RegistrationActivity.this);
SignedPreKeyRecord signedPreKey = PreKeyUtil.generateSignedPreKey(RegistrationActivity.this, identityKey, true);
accountManager.setPreKeys(identityKey.getPublicKey(), signedPreKey, records);
diff --git a/src/org/thoughtcrime/securesms/ShareActivity.java b/src/org/thoughtcrime/securesms/ShareActivity.java
index 6138c373fc..6ec7a63992 100644
--- a/src/org/thoughtcrime/securesms/ShareActivity.java
+++ b/src/org/thoughtcrime/securesms/ShareActivity.java
@@ -29,19 +29,19 @@ import android.os.Process;
import android.provider.OpenableColumns;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import org.thoughtcrime.securesms.components.SearchToolbar;
-import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.logging.Log;
+import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListFragment;
+import org.thoughtcrime.securesms.loki.fragments.ContactSelectionListLoader.DisplayMode;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.providers.BlobProvider;
@@ -52,7 +52,6 @@ import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FileUtils;
import org.thoughtcrime.securesms.util.MediaUtil;
-import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.io.FileInputStream;
@@ -68,7 +67,7 @@ import network.loki.messenger.R;
* @author Jake McGinty
*/
public class ShareActivity extends PassphraseRequiredActionBarActivity
- implements ContactSelectionListFragment.OnContactSelectedListener, SwipeRefreshLayout.OnRefreshListener
+ implements ContactSelectionListFragment.OnContactSelectedListener
{
private static final String TAG = ShareActivity.class.getSimpleName();
@@ -96,14 +95,10 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
@Override
protected void onCreate(Bundle icicle, boolean ready) {
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
- getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE,
- TextSecurePreferences.isSmsEnabled(this)
- ? DisplayMode.FLAG_ALL
- : DisplayMode.FLAG_PUSH | DisplayMode.FLAG_GROUPS);
+ getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, DisplayMode.FLAG_ALL);
}
getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false);
- getIntent().putExtra(ContactSelectionListFragment.RECENTS, true);
setContentView(R.layout.share_activity);
@@ -170,7 +165,6 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
searchAction = findViewById(R.id.search_action);
contactsFragment = (ContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
contactsFragment.setOnContactSelectedListener(this);
- contactsFragment.setOnRefreshListener(this);
}
private void initializeSearch() {
@@ -281,12 +275,6 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
@Override
public void onContactDeselected(String number) {
-
- }
-
- @Override
- public void onRefresh() {
-
}
@SuppressLint("StaticFieldLeak")
diff --git a/src/org/thoughtcrime/securesms/ShortcutLauncherActivity.java b/src/org/thoughtcrime/securesms/ShortcutLauncherActivity.java
index 108ca2ac25..554327f2de 100644
--- a/src/org/thoughtcrime/securesms/ShortcutLauncherActivity.java
+++ b/src/org/thoughtcrime/securesms/ShortcutLauncherActivity.java
@@ -11,7 +11,7 @@ import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import org.thoughtcrime.securesms.database.Address;
-import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity;
+import org.thoughtcrime.securesms.loki.activities.HomeActivity;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.CommunicationActions;
diff --git a/src/org/thoughtcrime/securesms/components/AvatarImageView.java b/src/org/thoughtcrime/securesms/components/AvatarImageView.java
index 73a44ee5ef..2fba777f0a 100644
--- a/src/org/thoughtcrime/securesms/components/AvatarImageView.java
+++ b/src/org/thoughtcrime/securesms/components/AvatarImageView.java
@@ -80,7 +80,7 @@ public class AvatarImageView extends AppCompatImageView {
setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
- outline.setOval(0, 0, view.getWidth(), view.getHeight());
+ outline.setOval(0, 0, view.getWidth(), view.getHeight());
}
});
setClipToOutline(true);
@@ -132,11 +132,11 @@ public class AvatarImageView extends AppCompatImageView {
if (photo.contactPhoto != null) {
requestManager.load(photo.contactPhoto)
- .fallback(fallbackContactPhotoDrawable)
- .error(fallbackContactPhotoDrawable)
- .diskCacheStrategy(DiskCacheStrategy.ALL)
- .circleCrop()
- .into(this);
+ .fallback(fallbackContactPhotoDrawable)
+ .error(fallbackContactPhotoDrawable)
+ .diskCacheStrategy(DiskCacheStrategy.ALL)
+ .circleCrop()
+ .into(this);
} else {
setImageDrawable(fallbackContactPhotoDrawable);
}
@@ -184,9 +184,9 @@ public class AvatarImageView extends AppCompatImageView {
if (other == null) return false;
return other.recipient.equals(recipient) &&
- other.recipient.getColor().equals(recipient.getColor()) &&
- other.ready == ready &&
- Objects.equals(other.contactPhoto, contactPhoto);
+ other.recipient.getColor().equals(recipient.getColor()) &&
+ other.ready == ready &&
+ Objects.equals(other.contactPhoto, contactPhoto);
}
}
}
diff --git a/src/org/thoughtcrime/securesms/components/InputPanel.java b/src/org/thoughtcrime/securesms/components/InputPanel.java
index e00b454f03..4fb3640dea 100644
--- a/src/org/thoughtcrime/securesms/components/InputPanel.java
+++ b/src/org/thoughtcrime/securesms/components/InputPanel.java
@@ -31,7 +31,7 @@ import org.thoughtcrime.securesms.conversation.ConversationStickerSuggestionAdap
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.loki.redesign.utilities.MentionUtilities;
+import org.thoughtcrime.securesms.loki.utilities.MentionUtilities;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.QuoteModel;
diff --git a/src/org/thoughtcrime/securesms/components/LinkPreviewView.java b/src/org/thoughtcrime/securesms/components/LinkPreviewView.java
index bbfdbecfb2..52c5cc28f4 100644
--- a/src/org/thoughtcrime/securesms/components/LinkPreviewView.java
+++ b/src/org/thoughtcrime/securesms/components/LinkPreviewView.java
@@ -75,7 +75,6 @@ public class LinkPreviewView extends FrameLayout {
container.setBackgroundColor(Color.TRANSPARENT);
container.setPadding(0, 0, 0, 0);
divider.setVisibility(VISIBLE);
- // closeButton.setVisibility(VISIBLE);
closeButton.setOnClickListener(v -> {
if (closeClickedListener != null) {
diff --git a/src/org/thoughtcrime/securesms/components/QuoteView.java b/src/org/thoughtcrime/securesms/components/QuoteView.java
index 690ce585cb..93e06f6dd7 100644
--- a/src/org/thoughtcrime/securesms/components/QuoteView.java
+++ b/src/org/thoughtcrime/securesms/components/QuoteView.java
@@ -31,7 +31,7 @@ import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.Util;
-import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat;
+import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
import java.util.List;
diff --git a/src/org/thoughtcrime/securesms/components/SendButton.java b/src/org/thoughtcrime/securesms/components/SendButton.java
index d6fa01746c..aa37bd7905 100644
--- a/src/org/thoughtcrime/securesms/components/SendButton.java
+++ b/src/org/thoughtcrime/securesms/components/SendButton.java
@@ -108,11 +108,7 @@ public class SendButton extends AppCompatImageButton
@Override
public boolean onLongClick(View v) {
-// if (isEnabled() && transportOptions.getEnabledTransports().size() > 1) {
-// getTransportOptionsPopup().display(transportOptions.getEnabledTransports());
-// return true;
-// }
-
+ // Loki - Do nothing
return false;
}
}
diff --git a/src/org/thoughtcrime/securesms/components/TransferControlView.java b/src/org/thoughtcrime/securesms/components/TransferControlView.java
index e3e0cbdc5d..9d18035308 100644
--- a/src/org/thoughtcrime/securesms/components/TransferControlView.java
+++ b/src/org/thoughtcrime/securesms/components/TransferControlView.java
@@ -200,7 +200,6 @@ public class TransferControlView extends FrameLayout {
if (view != null) {
view.setVisibility(VISIBLE);
- // setVisibility(VISIBLE);
} else {
setVisibility(GONE);
}
diff --git a/src/org/thoughtcrime/securesms/components/TypingStatusSender.java b/src/org/thoughtcrime/securesms/components/TypingStatusSender.java
index b573020b22..dc510073fc 100644
--- a/src/org/thoughtcrime/securesms/components/TypingStatusSender.java
+++ b/src/org/thoughtcrime/securesms/components/TypingStatusSender.java
@@ -9,17 +9,16 @@ import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.jobs.TypingSendJob;
+import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Util;
-import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities;
-import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
+import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
-import kotlin.Unit;
-
@SuppressLint("UseSparseArrays")
public class TypingStatusSender {
@@ -82,24 +81,17 @@ public class TypingStatusSender {
}
private void sendTyping(long threadId, boolean typingStarted) {
- LokiFileServerAPI storageAPI = LokiFileServerAPI.Companion.getShared();
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
Recipient recipient = threadDatabase.getRecipientForThreadId(threadId);
-
- if (recipient == null) {
- ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(threadId, typingStarted));
- return;
+ // Loki - Check whether we want to send a typing indicator to this user
+ if (!SessionMetaProtocol.shouldSendTypingIndicator(recipient, context)) { return; }
+ // Loki - Take into account multi device
+ Set linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.getAddress().serialize());
+ for (String device : linkedDevices) {
+ Recipient deviceAsRecipient = Recipient.from(context, Address.fromSerialized(device), false);
+ long deviceThreadID = threadDatabase.getThreadIdFor(deviceAsRecipient);
+ ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(deviceThreadID, typingStarted));
}
- LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(recipient.getAddress().serialize()).success(devices -> {
- for (String device : devices) {
- Recipient deviceRecipient = Recipient.from(context, Address.fromSerialized(device), false);
- long deviceThreadID = threadDatabase.getThreadIdIfExistsFor(deviceRecipient);
- if (deviceThreadID > -1) {
- ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(deviceThreadID, typingStarted));
- }
- }
- return Unit.INSTANCE;
- });
}
private class StartRunnable implements Runnable {
diff --git a/src/org/thoughtcrime/securesms/components/reminder/DefaultSmsReminder.java b/src/org/thoughtcrime/securesms/components/reminder/DefaultSmsReminder.java
index 67a5980dfa..02e7607296 100644
--- a/src/org/thoughtcrime/securesms/components/reminder/DefaultSmsReminder.java
+++ b/src/org/thoughtcrime/securesms/components/reminder/DefaultSmsReminder.java
@@ -8,9 +8,9 @@ import android.provider.Telephony;
import android.view.View;
import android.view.View.OnClickListener;
-import network.loki.messenger.R;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
-import org.thoughtcrime.securesms.util.Util;
+
+import network.loki.messenger.R;
public class DefaultSmsReminder extends Reminder {
@@ -40,14 +40,5 @@ public class DefaultSmsReminder extends Reminder {
public static boolean isEligible(Context context) {
return false;
- // Loki - Original code
- // ========
-// final boolean isDefault = Util.isDefaultSmsProvider(context);
-// if (isDefault) {
-// TextSecurePreferences.setPromptedDefaultSmsProvider(context, false);
-// }
-//
-// return !isDefault && !TextSecurePreferences.hasPromptedDefaultSmsProvider(context);
- // ========
}
}
diff --git a/src/org/thoughtcrime/securesms/components/reminder/PushRegistrationReminder.java b/src/org/thoughtcrime/securesms/components/reminder/PushRegistrationReminder.java
index a8b67f515b..83e0f257cb 100644
--- a/src/org/thoughtcrime/securesms/components/reminder/PushRegistrationReminder.java
+++ b/src/org/thoughtcrime/securesms/components/reminder/PushRegistrationReminder.java
@@ -4,9 +4,9 @@ import android.content.Context;
import android.content.Intent;
import android.view.View.OnClickListener;
-import network.loki.messenger.R;
import org.thoughtcrime.securesms.RegistrationActivity;
-import org.thoughtcrime.securesms.util.TextSecurePreferences;
+
+import network.loki.messenger.R;
public class PushRegistrationReminder extends Reminder {
@@ -30,9 +30,5 @@ public class PushRegistrationReminder extends Reminder {
public static boolean isEligible(Context context) {
return false;
- // Loki - Original code
- // ========
-// return !TextSecurePreferences.isPushRegistered(context);
- // ========
}
}
diff --git a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java
deleted file mode 100644
index 3976cfc453..0000000000
--- a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/**
- * Copyright (C) 2014 Open Whisper Systems
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package org.thoughtcrime.securesms.contacts;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.database.Cursor;
-import android.provider.ContactsContract;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v7.widget.RecyclerView;
-import android.text.SpannableString;
-import android.text.Spanned;
-import android.text.TextUtils;
-import android.text.style.ForegroundColorSpan;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import network.loki.messenger.R;
-import org.thoughtcrime.securesms.components.RecyclerViewFastScroller.FastScrollAdapter;
-import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.HeaderViewHolder;
-import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.ViewHolder;
-import org.thoughtcrime.securesms.database.Address;
-import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
-import org.thoughtcrime.securesms.mms.GlideRequests;
-import org.thoughtcrime.securesms.util.StickyHeaderDecoration.StickyHeaderAdapter;
-import org.thoughtcrime.securesms.util.TextSecurePreferences;
-import org.thoughtcrime.securesms.util.Util;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * List adapter to display all contacts and their related information
- *
- * @author Jake McGinty
- */
-public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter
- implements FastScrollAdapter,
- StickyHeaderAdapter
-{
- private final static String TAG = ContactSelectionListAdapter.class.getSimpleName();
-
- private static final int VIEW_TYPE_CONTACT = 0;
- private static final int VIEW_TYPE_DIVIDER = 1;
-
- private final static int STYLE_ATTRIBUTES[] = new int[]{R.attr.contact_selection_push_user,
- R.attr.contact_selection_lay_user};
-
- private final boolean multiSelect;
- private final LayoutInflater li;
- private final TypedArray drawables;
- private final ItemClickListener clickListener;
- private final GlideRequests glideRequests;
-
- private final Set selectedContacts = new HashSet<>();
-
- public abstract static class ViewHolder extends RecyclerView.ViewHolder {
-
- public ViewHolder(View itemView) {
- super(itemView);
- }
-
- public abstract void bind(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect);
- public abstract void unbind(@NonNull GlideRequests glideRequests);
- public abstract void setChecked(boolean checked);
- }
-
- public static class ContactViewHolder extends ViewHolder {
- ContactViewHolder(@NonNull final View itemView,
- @Nullable final ItemClickListener clickListener)
- {
- super(itemView);
- itemView.setOnClickListener(v -> {
- if (clickListener != null) clickListener.onItemClick(getView());
- });
- }
-
- public ContactSelectionListItem getView() {
- return (ContactSelectionListItem) itemView;
- }
-
- public void bind(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect) {
- getView().set(glideRequests, type, name, number, label, color, multiSelect);
- }
-
- @Override
- public void unbind(@NonNull GlideRequests glideRequests) {
- getView().unbind(glideRequests);
- }
-
- @Override
- public void setChecked(boolean checked) {
- getView().setChecked(checked);
- }
- }
-
- public static class DividerViewHolder extends ViewHolder {
-
- private final TextView label;
-
- DividerViewHolder(View itemView) {
- super(itemView);
- this.label = itemView.findViewById(R.id.label);
- }
-
- @Override
- public void bind(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect) {
- this.label.setText(name);
- }
-
- @Override
- public void unbind(@NonNull GlideRequests glideRequests) {}
-
- @Override
- public void setChecked(boolean checked) {}
- }
-
- static class HeaderViewHolder extends RecyclerView.ViewHolder {
- HeaderViewHolder(View itemView) {
- super(itemView);
- }
- }
-
- public ContactSelectionListAdapter(@NonNull Context context,
- @NonNull GlideRequests glideRequests,
- @Nullable Cursor cursor,
- @Nullable ItemClickListener clickListener,
- boolean multiSelect)
- {
- super(context, cursor);
- this.li = LayoutInflater.from(context);
- this.glideRequests = glideRequests;
- this.drawables = context.obtainStyledAttributes(STYLE_ATTRIBUTES);
- this.multiSelect = multiSelect;
- this.clickListener = clickListener;
- }
-
- @Override
- public long getHeaderId(int i) {
- if (!isActiveCursor()) return -1;
-
- int contactType = getContactType(i);
-
- if (contactType == ContactsDatabase.DIVIDER_TYPE) return -1;
- return Util.hashCode(getHeaderString(i), getContactType(i));
- }
-
- @Override
- public ViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) {
- if (viewType == VIEW_TYPE_CONTACT) {
- return new ContactViewHolder(li.inflate(R.layout.contact_selection_list_item, parent, false), clickListener);
- } else {
- return new DividerViewHolder(li.inflate(R.layout.contact_selection_list_divider, parent, false));
- }
- }
-
- @Override
- public void onBindItemViewHolder(ViewHolder viewHolder, @NonNull Cursor cursor) {
- int contactType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.CONTACT_TYPE_COLUMN));
- String name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN));
- String number = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_COLUMN));
- int numberType = cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.NUMBER_TYPE_COLUMN));
- String label = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.LABEL_COLUMN));
- String labelText = ContactsContract.CommonDataKinds.Phone.getTypeLabel(getContext().getResources(),
- numberType, label).toString();
-
- int color = (contactType == ContactsDatabase.PUSH_TYPE) ? drawables.getColor(0, 0xa0000000) :
- drawables.getColor(1, 0xff000000);
-
- viewHolder.unbind(glideRequests);
- viewHolder.bind(glideRequests, contactType, name, number, labelText, color, multiSelect);
- viewHolder.setChecked(selectedContacts.contains(number));
- }
-
- @Override
- public int getItemViewType(@NonNull Cursor cursor) {
- if (cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.CONTACT_TYPE_COLUMN)) == ContactsDatabase.DIVIDER_TYPE) {
- return VIEW_TYPE_DIVIDER;
- } else {
- return VIEW_TYPE_CONTACT;
- }
- }
-
-
- @Override
- public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) {
- return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.contact_selection_recyclerview_header, parent, false));
- }
-
- @Override
- public void onBindHeaderViewHolder(HeaderViewHolder viewHolder, int position) {
- ((TextView)viewHolder.itemView).setText(getSpannedHeaderString(position));
- }
-
- @Override
- public void onItemViewRecycled(ViewHolder holder) {
- holder.unbind(glideRequests);
- }
-
- @Override
- public CharSequence getBubbleText(int position) {
- return getHeaderString(position);
- }
-
- public Set getSelectedContacts() {
- return selectedContacts;
- }
-
- private CharSequence getSpannedHeaderString(int position) {
- final String headerString = getHeaderString(position);
- if (isPush(position)) {
- SpannableString spannable = new SpannableString(headerString);
- spannable.setSpan(new ForegroundColorSpan(getContext().getResources().getColor(R.color.signal_primary)), 0, headerString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- return spannable;
- } else {
- return headerString;
- }
- }
-
- private @NonNull String getHeaderString(int position) {
- int contactType = getContactType(position);
-
- if (contactType == ContactsDatabase.RECENT_TYPE || contactType == ContactsDatabase.DIVIDER_TYPE) {
- return " ";
- }
-
- Cursor cursor = getCursorAtPositionOrThrow(position);
- String letter = cursor.getString(cursor.getColumnIndexOrThrow(ContactsDatabase.NAME_COLUMN));
-
- if (!TextUtils.isEmpty(letter)) {
- String firstChar = letter.trim().substring(0, 1).toUpperCase();
- if (Character.isLetterOrDigit(firstChar.codePointAt(0))) {
- return firstChar;
- }
- }
-
- return "#";
- }
-
- private int getContactType(int position) {
- final Cursor cursor = getCursorAtPositionOrThrow(position);
- return cursor.getInt(cursor.getColumnIndexOrThrow(ContactsDatabase.CONTACT_TYPE_COLUMN));
- }
-
- private boolean isPush(int position) {
- return getContactType(position) == ContactsDatabase.PUSH_TYPE;
- }
-
- public interface ItemClickListener {
- void onItemClick(ContactSelectionListItem item);
- }
-}
diff --git a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java
deleted file mode 100644
index eb41cc5035..0000000000
--- a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListItem.java
+++ /dev/null
@@ -1,161 +0,0 @@
-package org.thoughtcrime.securesms.contacts;
-
-import android.content.Context;
-import android.support.annotation.NonNull;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.CheckBox;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.thoughtcrime.securesms.database.Address;
-import org.thoughtcrime.securesms.database.DatabaseFactory;
-import org.thoughtcrime.securesms.loki.redesign.views.ProfilePictureView;
-import org.thoughtcrime.securesms.mms.GlideRequests;
-import org.thoughtcrime.securesms.recipients.Recipient;
-import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
-import org.thoughtcrime.securesms.util.GroupUtil;
-import org.thoughtcrime.securesms.util.Util;
-import org.thoughtcrime.securesms.util.ViewUtil;
-import org.whispersystems.signalservice.loki.api.LokiAPI;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import network.loki.messenger.R;
-
-public class ContactSelectionListItem extends LinearLayout implements RecipientModifiedListener {
-
- @SuppressWarnings("unused")
- private static final String TAG = ContactSelectionListItem.class.getSimpleName();
-
- private ProfilePictureView profilePictureView;
- private TextView numberView;
- private TextView nameView;
- private TextView labelView;
- private CheckBox checkBox;
-
- private String number;
- private Recipient recipient;
- private GlideRequests glideRequests;
- private long threadID;
-
- public ContactSelectionListItem(Context context) {
- super(context);
- }
-
- public ContactSelectionListItem(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- this.profilePictureView = findViewById(R.id.profilePictureView);
- this.numberView = findViewById(R.id.number);
- this.labelView = findViewById(R.id.label);
- this.nameView = findViewById(R.id.name);
- this.checkBox = findViewById(R.id.check_box);
-
- ViewUtil.setTextViewGravityStart(this.nameView, getContext());
- }
-
- public void set(@NonNull GlideRequests glideRequests, int type, String name, String number, String label, int color, boolean multiSelect) {
- this.glideRequests = glideRequests;
- this.number = number;
-
- if (type == ContactsDatabase.NEW_TYPE) {
- this.recipient = null;
- } else if (!TextUtils.isEmpty(number)) {
- Address address = Address.fromExternal(getContext(), number);
- this.recipient = Recipient.from(getContext(), address, true);
- this.recipient.addListener(this);
-
- if (this.recipient.getName() != null) {
- name = this.recipient.getName();
- }
- }
-
- threadID = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(recipient);
-
- this.numberView.setTextColor(color);
- updateProfilePicture(glideRequests, name, threadID);
-
- if (!multiSelect && recipient != null && recipient.isLocalNumber()) {
- name = getContext().getString(R.string.note_to_self);
- }
-
- setText(type, name, number, label);
-
- if (multiSelect) this.checkBox.setVisibility(View.VISIBLE);
- else this.checkBox.setVisibility(View.GONE);
- }
-
- public void setChecked(boolean selected) {
- this.checkBox.setChecked(selected);
- }
-
- public void unbind(GlideRequests glideRequests) {
- if (recipient != null) {
- recipient.removeListener(this);
- recipient = null;
- }
- }
-
- private void setText(int type, String name, String number, String label) {
- if (number == null || number.isEmpty() || GroupUtil.isEncodedGroup(number)) {
- this.nameView.setEnabled(false);
- this.numberView.setText("");
- this.labelView.setVisibility(View.GONE);
- } else if (type == ContactsDatabase.PUSH_TYPE) {
- this.numberView.setText(number);
- this.nameView.setEnabled(true);
- this.labelView.setVisibility(View.GONE);
- } else {
- this.numberView.setText(number);
- this.nameView.setEnabled(true);
- this.labelView.setText(label);
- this.labelView.setVisibility(View.VISIBLE);
- }
-
- this.nameView.setText(name);
- }
-
- public String getNumber() {
- return number;
- }
-
- @Override
- public void onModified(final Recipient recipient) {
- if (this.recipient == recipient) {
- Util.runOnMain(() -> {
- threadID = DatabaseFactory.getThreadDatabase(getContext()).getThreadIdFor(recipient);
- updateProfilePicture(glideRequests, recipient.getName(), threadID);
- nameView.setText(recipient.toShortString());
- });
- }
- }
-
- private void updateProfilePicture(GlideRequests glide, String name, long threadID) {
- if (this.recipient.isGroupRecipient()) {
- Set usersAsSet = LokiAPI.Companion.getUserHexEncodedPublicKeyCache().get(threadID);
- if (usersAsSet == null) {
- usersAsSet = new HashSet<>();
- }
- ArrayList users = new ArrayList<>(usersAsSet);
- Collections.sort(users); // Sort to provide a level of stability
- profilePictureView.setHexEncodedPublicKey(users.size() > 0 ? users.get(0) : "");
- profilePictureView.setAdditionalHexEncodedPublicKey(users.size() > 1 ? users.get(1) : "");
- profilePictureView.setRSSFeed(name.equals("Loki News") || name.equals("Session Updates"));
- } else {
- profilePictureView.setHexEncodedPublicKey(this.number);
- profilePictureView.setAdditionalHexEncodedPublicKey(null);
- profilePictureView.setRSSFeed(false);
- }
- profilePictureView.glide = glide;
- profilePictureView.update();
- }
-}
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java
index a7ce551c87..1ce8001563 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java
@@ -139,7 +139,6 @@ import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase.RegisteredState;
-import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.identity.IdentityRecordList;
import org.thoughtcrime.securesms.database.model.MessageRecord;
@@ -155,16 +154,17 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.loki.LokiMessageDatabase;
-import org.thoughtcrime.securesms.loki.LokiThreadDatabase;
-import org.thoughtcrime.securesms.loki.LokiThreadDatabaseDelegate;
-import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
-import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIUtilities;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiUserDatabase;
-import org.thoughtcrime.securesms.loki.redesign.views.FriendRequestViewDelegate;
-import org.thoughtcrime.securesms.loki.redesign.views.MentionCandidateSelectionView;
-import org.thoughtcrime.securesms.loki.redesign.views.SessionRestoreBannerView;
+import org.thoughtcrime.securesms.loki.activities.HomeActivity;
+import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiThreadDatabaseDelegate;
+import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
+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.utilities.MentionManagerUtilities;
+import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
+import org.thoughtcrime.securesms.loki.views.MentionCandidateSelectionView;
+import org.thoughtcrime.securesms.loki.views.SessionRestoreBannerView;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mediasend.MediaSendActivity;
import org.thoughtcrime.securesms.mms.AttachmentManager;
@@ -214,7 +214,6 @@ import org.thoughtcrime.securesms.util.Dialogs;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
import org.thoughtcrime.securesms.util.ExpirationUtil;
-import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.ServiceUtil;
@@ -228,13 +227,12 @@ import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.thoughtcrime.securesms.util.views.Stub;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.util.guava.Optional;
-import org.whispersystems.signalservice.loki.api.multidevice.DeviceLink;
-import org.whispersystems.signalservice.loki.api.LokiAPI;
-import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities;
-import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat;
-import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus;
-import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
-import org.whispersystems.signalservice.loki.messaging.Mention;
+import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
+import org.whispersystems.signalservice.loki.protocol.mentions.Mention;
+import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
+import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol;
+import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
+import org.whispersystems.signalservice.loki.protocol.todo.LokiThreadFriendRequestStatus;
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation;
import java.io.IOException;
@@ -242,7 +240,6 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
-import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
@@ -326,7 +323,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private MenuItem searchViewItem;
private ProgressBar messageStatusProgressBar;
private ImageView muteIndicatorImageView;
- private TextView subtitleTextView;
+ private TextView subtitleTextView;
private AttachmentTypeSelector attachmentTypeSelector;
private AttachmentManager attachmentManager;
@@ -357,7 +354,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private final DynamicNoActionBarTheme dynamicTheme = new DynamicNoActionBarTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
- // Message Status Bar
+ // Message status bar
private ArrayList broadcastReceivers = new ArrayList<>();
private String messageStatus = null;
@@ -368,9 +365,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private ArrayList mentions = new ArrayList<>();
private String oldText = "";
- // Multi Device
- private boolean isFriendsWithAnyDevice = false;
-
// Restoration
protected SessionRestoreBannerView sessionRestoreBannerView;
@@ -458,7 +452,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
});
sessionRestoreBannerView.setOnRestore(() -> {
- this.restoreSession();
+ SessionManagementProtocol.startSessionReset(this, recipient, threadId);
+ updateSessionRestoreBanner();
return Unit.INSTANCE;
});
sessionRestoreBannerView.setOnDismiss(() -> {
@@ -468,7 +463,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
return Unit.INSTANCE;
});
- LokiAPIUtilities.INSTANCE.populateUserHexEncodedPublicKeyCacheIfNeeded(threadId, this);
+ MentionManagerUtilities.INSTANCE.populateUserHexEncodedPublicKeyCacheIfNeeded(threadId, this);
LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(this).getPublicChat(threadId);
if (publicChat != null) {
@@ -559,6 +554,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
markThreadAsRead();
DatabaseFactory.getLokiThreadDatabase(this).setDelegate(this);
+
updateInputPanel();
updateSessionRestoreBanner();
@@ -665,9 +661,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
*/
case PICK_GIF:
setMedia(data.getData(),
- MediaType.GIF,
- data.getIntExtra(GiphyActivity.EXTRA_WIDTH, 0),
- data.getIntExtra(GiphyActivity.EXTRA_HEIGHT, 0));
+ MediaType.GIF,
+ data.getIntExtra(GiphyActivity.EXTRA_WIDTH, 0),
+ data.getIntExtra(GiphyActivity.EXTRA_HEIGHT, 0));
break;
case SMS_DEFAULT:
initializeSecurity(isSecureText, isDefaultSms);
@@ -747,7 +743,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
MenuInflater inflater = this.getMenuInflater();
menu.clear();
- boolean isOpenGroupOrRSSFeed = recipient.getAddress().isPublicChat() || recipient.getAddress().isRSSFeed();
+ boolean isOpenGroupOrRSSFeed = recipient.getAddress().isOpenGroup() || recipient.getAddress().isRSSFeed();
if (isSecureText && !isOpenGroupOrRSSFeed) {
if (recipient.getExpireMessages() > 0) {
@@ -887,7 +883,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
case R.id.menu_invite: handleInviteLink(); return true;
case R.id.menu_mute_notifications: handleMuteNotifications(); return true;
case R.id.menu_unmute_notifications: handleUnmuteNotifications(); return true;
- // case R.id.menu_conversation_settings: handleConversationSettings(); return true;
+// case R.id.menu_conversation_settings: handleConversationSettings(); return true;
case R.id.menu_expiring_messages_off:
case R.id.menu_expiring_messages: handleSelectMessageExpiration(); return true;
case android.R.id.home: handleReturnToConversationList(); return true;
@@ -1154,7 +1150,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
builder.setMessage(getString(R.string.ConversationActivity_are_you_sure_you_want_to_leave_this_group));
builder.setPositiveButton(R.string.yes, (dialog, which) -> {
Recipient groupRecipient = getRecipient();
- if (GroupUtil.leaveGroup(this, groupRecipient)) {
+ if (ClosedGroupsProtocol.leaveGroup(this, groupRecipient)) {
initializeEnabledCheck();
} else {
Toast.makeText(this, R.string.ConversationActivity_error_leaving_group, Toast.LENGTH_LONG).show();
@@ -1240,11 +1236,13 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private boolean handleDisplayQuickContact() {
return !recipient.getAddress().isGroup();
-// if (recipient.getContactUri() != null) {
-// ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
-// } else {
-// handleAddToContacts();
-// }
+ /*
+ if (recipient.getContactUri() != null) {
+ ContactsContract.QuickContact.showQuickContact(ConversationActivity.this, titleView, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
+ } else {
+ handleAddToContacts();
+ }
+ */
}
private void handleAddAttachment() {
@@ -1531,7 +1529,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
}
- protected void updateSessionRestoreBanner() {
+ private void updateSessionRestoreBanner() {
Set devices = DatabaseFactory.getLokiThreadDatabase(this).getSessionRestoreDevices(threadId);
if (devices.size() > 0) {
sessionRestoreBannerView.update(recipient);
@@ -1617,30 +1615,30 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void initializeViews() {
- titleTextView = findViewById(R.id.titleTextView);
- buttonToggle = ViewUtil.findById(this, R.id.button_toggle);
- sendButton = ViewUtil.findById(this, R.id.send_button);
- attachButton = ViewUtil.findById(this, R.id.attach_button);
- composeText = ViewUtil.findById(this, R.id.embedded_text_editor);
- charactersLeft = ViewUtil.findById(this, R.id.space_left);
- emojiDrawerStub = ViewUtil.findStubById(this, R.id.emoji_drawer_stub);
- unblockButton = ViewUtil.findById(this, R.id.unblock_button);
- makeDefaultSmsButton = ViewUtil.findById(this, R.id.make_default_sms_button);
- registerButton = ViewUtil.findById(this, R.id.register_button);
- container = ViewUtil.findById(this, R.id.layout_container);
- reminderView = ViewUtil.findStubById(this, R.id.reminder_stub);
- unverifiedBannerView = ViewUtil.findStubById(this, R.id.unverified_banner_stub);
- groupShareProfileView = ViewUtil.findStubById(this, R.id.group_share_profile_view_stub);
- quickAttachmentToggle = ViewUtil.findById(this, R.id.quick_attachment_toggle);
- inlineAttachmentToggle = ViewUtil.findById(this, R.id.inline_attachment_container);
- inputPanel = ViewUtil.findById(this, R.id.bottom_panel);
- searchNav = ViewUtil.findById(this, R.id.conversation_search_nav);
+ titleTextView = findViewById(R.id.titleTextView);
+ buttonToggle = ViewUtil.findById(this, R.id.button_toggle);
+ sendButton = ViewUtil.findById(this, R.id.send_button);
+ attachButton = ViewUtil.findById(this, R.id.attach_button);
+ composeText = ViewUtil.findById(this, R.id.embedded_text_editor);
+ charactersLeft = ViewUtil.findById(this, R.id.space_left);
+ emojiDrawerStub = ViewUtil.findStubById(this, R.id.emoji_drawer_stub);
+ unblockButton = ViewUtil.findById(this, R.id.unblock_button);
+ makeDefaultSmsButton = ViewUtil.findById(this, R.id.make_default_sms_button);
+ registerButton = ViewUtil.findById(this, R.id.register_button);
+ container = ViewUtil.findById(this, R.id.layout_container);
+ reminderView = ViewUtil.findStubById(this, R.id.reminder_stub);
+ unverifiedBannerView = ViewUtil.findStubById(this, R.id.unverified_banner_stub);
+ groupShareProfileView = ViewUtil.findStubById(this, R.id.group_share_profile_view_stub);
+ quickAttachmentToggle = ViewUtil.findById(this, R.id.quick_attachment_toggle);
+ inlineAttachmentToggle = ViewUtil.findById(this, R.id.inline_attachment_container);
+ inputPanel = ViewUtil.findById(this, R.id.bottom_panel);
+ searchNav = ViewUtil.findById(this, R.id.conversation_search_nav);
mentionCandidateSelectionViewContainer = ViewUtil.findById(this, R.id.mentionCandidateSelectionViewContainer);
- mentionCandidateSelectionView = ViewUtil.findById(this, R.id.userSelectionView);
- sessionRestoreBannerView = ViewUtil.findById(this, R.id.sessionRestoreBannerView);
- messageStatusProgressBar = ViewUtil.findById(this, R.id.messageStatusProgressBar);
- muteIndicatorImageView = ViewUtil.findById(this, R.id.muteIndicatorImageView);
- subtitleTextView = ViewUtil.findById(this, R.id.subtitleTextView);
+ mentionCandidateSelectionView = ViewUtil.findById(this, R.id.userSelectionView);
+ sessionRestoreBannerView = ViewUtil.findById(this, R.id.sessionRestoreBannerView);
+ messageStatusProgressBar = ViewUtil.findById(this, R.id.messageStatusProgressBar);
+ muteIndicatorImageView = ViewUtil.findById(this, R.id.muteIndicatorImageView);
+ subtitleTextView = ViewUtil.findById(this, R.id.subtitleTextView);
ImageButton quickCameraToggle = ViewUtil.findById(this, R.id.quick_camera_toggle);
ImageButton inlineAttachmentButton = ViewUtil.findById(this, R.id.inline_attachment_button);
@@ -1897,15 +1895,21 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
case AttachmentTypeSelector.TAKE_PHOTO:
attachmentManager.capturePhoto(this, TAKE_PHOTO); break;
case AttachmentTypeSelector.ADD_GIF:
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle("Search GIFs?");
- builder.setMessage("You will not have full metadata protection when sending GIFs.");
- builder.setPositiveButton("OK", (dialog, which) -> {
+ boolean hasSeenGIFMetaDataWarning = TextSecurePreferences.hasSeenGIFMetaDataWarning(this);
+ if (!hasSeenGIFMetaDataWarning) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle("Search GIFs?");
+ builder.setMessage("You will not have full metadata protection when sending GIFs.");
+ builder.setPositiveButton("OK", (dialog, which) -> {
+ AttachmentManager.selectGif(this, PICK_GIF, !isSecureText);
+ dialog.dismiss();
+ });
+ builder.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss());
+ builder.create().show();
+ TextSecurePreferences.setHasSeenGIFMetaDataWarning(this);
+ } else {
AttachmentManager.selectGif(this, PICK_GIF, !isSecureText);
- dialog.dismiss();
- });
- builder.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss() );
- builder.create().show();
+ }
break;
}
}
@@ -2073,7 +2077,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void setGroupShareProfileReminder(@NonNull Recipient recipient) {
- if (recipient.isPushGroupRecipient() && !recipient.isProfileSharing() && !recipient.getAddress().isPublicChat() && !recipient.getAddress().isRSSFeed()) {
+ if (recipient.isPushGroupRecipient() && !recipient.isProfileSharing() && !recipient.getAddress().isOpenGroup() && !recipient.getAddress().isRSSFeed()) {
groupShareProfileView.get().setRecipient(recipient);
groupShareProfileView.get().setVisibility(View.GONE); // Loki - Always hide for now
} else if (groupShareProfileView.resolved()) {
@@ -2249,72 +2253,39 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void handleThreadFriendRequestStatusChanged(long threadID) {
- if (threadID != this.threadId) {
- Recipient threadRecipient = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID);
- if (threadRecipient != null && !threadRecipient.isGroupRecipient()) {
- LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(threadRecipient.getAddress().serialize()).success(devices -> {
- // We should update our input if this thread is a part of the other threads device
- if (devices.contains(recipient.getAddress().serialize())) {
- this.updateInputPanel();
- }
- return Unit.INSTANCE;
- });
- }
- return;
+ if (recipient.isGroupRecipient()) { return; }
+ boolean isUpdateNeeded = false;
+ if (threadID == this.threadId) {
+ isUpdateNeeded = true;
+ } else {
+ String thisThreadPublicKey = recipient.getAddress().serialize();
+ Set 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; }
}
-
- this.updateInputPanel();
+ }
+ if (isUpdateNeeded) {
+ updateInputPanel();
+ }
}
@Override
- public void handleSessionRestoreDevicesChanged(long threadId) {
- if (threadId == this.threadId) {
+ public void handleSessionRestoreDevicesChanged(long threadID) {
+ if (threadID == this.threadId) {
runOnUiThread(this::updateSessionRestoreBanner);
}
}
private void updateInputPanel() {
- /*
- isFriendsWithAnyDevice reflects whether we are friends with any of the other user's devices.
-
- This fixes the case where the input panel disables and enables rapidly, which can occur when we are
- not friends with the current thread BUT multi device tells us that we are friends with another one of their devices.
- */
- if (recipient.isGroupRecipient() || isNoteToSelf() || isFriendsWithAnyDevice) { setInputPanelEnabled(true); return; }
-
- // Disable the input panel if a friend request is pending
- LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(this).getFriendRequestStatus(threadId);
- boolean isPending = friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENDING || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENT || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED;
- setInputPanelEnabled(!isPending);
-
- // Always enable the input panel if we are friends with the current user
- isFriendsWithAnyDevice = (friendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS);
-
- if (!isFriendsWithAnyDevice) {
- // Enable the input panel if we don't have any pending friend requests OR we are friends with one of the user's linked devices
- MultiDeviceUtilities.hasPendingFriendRequestWithAnyLinkedDevice(this, recipient).success( hasPendingRequests -> {
- if (!hasPendingRequests) {
- setInputPanelEnabled(true);
- } else {
- MultiDeviceUtilities.isFriendsWithAnyLinkedDevice(this, recipient).success( isFriends -> {
- // Enable the input panel if we're friends with any of the user's devices
- isFriendsWithAnyDevice = isFriends;
- setInputPanelEnabled(isFriends);
- return Unit.INSTANCE;
- });
- }
- return Unit.INSTANCE;
- });
- }
- }
-
- private void setInputPanelEnabled(boolean enabled) {
+ boolean shouldInputPanelBeEnabled = FriendRequestProtocol.shouldInputPanelBeEnabled(this, recipient);
Util.runOnMain(() -> {
updateToggleButtonState();
- String hint = enabled ? "Message" : "Pending session request";
+ String hint = shouldInputPanelBeEnabled ? "Message" : "Pending session request";
inputPanel.setHint(hint);
- inputPanel.setEnabled(enabled);
- if (enabled && inputPanel.getVisibility() == View.VISIBLE) {
+ inputPanel.setEnabled(shouldInputPanelBeEnabled);
+ if (shouldInputPanelBeEnabled && inputPanel.getVisibility() == View.VISIBLE) {
inputPanel.composeText.requestFocus();
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(inputPanel.composeText, 0);
@@ -2370,7 +2341,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
Log.w(TAG, ex);
}
- if (messageStatus == null && !isGroupConversation()) {
+ if (messageStatus == null && !isGroupConversation() && !SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize())) {
messageStatus = "calculatingPoW";
updateSubtitleTextView();
updateMessageStatusProgressBar();
@@ -2425,7 +2396,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
// 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; // Needed for stageOutgoingMessage(...)
+ outgoingMessage.isFriendRequest = !isGroupConversation() && friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS
+ && !SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize()); // Needed for stageOutgoingMessage(...)
if (clearComposeBox) {
inputPanel.clearQuote();
@@ -2477,7 +2449,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
// 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; // Needed for stageOutgoingMessage(...)
+ message.isFriendRequest = !isGroupConversation() && friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS
+ && !SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize()); // Needed for stageOutgoingMessage(...)
silentlySetComposeText("");
final long id = fragment.stageOutgoingMessage(message);
@@ -2508,8 +2481,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void updateToggleButtonState() {
- // Don't allow attachments if we're not friends with any of the user's devices
- if (!isNoteToSelf() && !recipient.isGroupRecipient() && !isFriendsWithAnyDevice) {
+ if (!FriendRequestProtocol.shouldAttachmentButtonBeEnabled(this, recipient)) {
buttonToggle.display(sendButton);
quickAttachmentToggle.hide();
inlineAttachmentToggle.hide();
@@ -2745,7 +2717,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private void silentlySetComposeText(String text) {
typingTextWatcher.setEnabled(false);
composeText.setText(text);
- if (text.isEmpty()) resetMentions();
+ if (text.isEmpty()) { resetMentions(); }
typingTextWatcher.setEnabled(true);
}
@@ -2905,7 +2877,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
LokiThreadDatabase threadDatabase = DatabaseFactory.getLokiThreadDatabase(ConversationActivity.this);
LokiUserDatabase userDatabase = DatabaseFactory.getLokiUserDatabase(ConversationActivity.this);
if (lastCharacter == '@' && Character.isWhitespace(secondToLastCharacter)) {
- List mentionCandidates = LokiAPI.Companion.getMentionCandidates("", threadId, userHexEncodedPublicKey, threadDatabase, userDatabase);
+ List mentionCandidates = MentionsManager.shared.getMentionCandidates("", threadId);
currentMentionStartIndex = lastCharacterIndex;
mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE);
mentionCandidateSelectionView.show(mentionCandidates, threadId);
@@ -2916,7 +2888,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} else {
if (currentMentionStartIndex != -1) {
String query = text.substring(currentMentionStartIndex + 1); // + 1 to get rid of the @
- List mentionCandidates = LokiAPI.Companion.getMentionCandidates(query, threadId, userHexEncodedPublicKey, threadDatabase, userDatabase);
+ List mentionCandidates = MentionsManager.shared.getMentionCandidates(query, threadId);
mentionCandidateSelectionViewContainer.setVisibility(View.VISIBLE);
mentionCandidateSelectionView.show(mentionCandidates, threadId);
}
@@ -2960,12 +2932,12 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
inputPanel.setQuote(GlideApp.with(this),
- messageRecord.getDateSent(),
- author,
- body,
- slideDeck,
- recipient,
- threadId);
+ messageRecord.getDateSent(),
+ author,
+ body,
+ slideDeck,
+ recipient,
+ threadId);
} else if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getLinkPreviews().isEmpty()) {
LinkPreview linkPreview = ((MmsMessageRecord) messageRecord).getLinkPreviews().get(0);
@@ -2976,20 +2948,20 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
inputPanel.setQuote(GlideApp.with(this),
- messageRecord.getDateSent(),
- author,
- messageRecord.getBody(),
- slideDeck,
- recipient,
- threadId);
+ messageRecord.getDateSent(),
+ author,
+ messageRecord.getBody(),
+ slideDeck,
+ recipient,
+ threadId);
} else {
inputPanel.setQuote(GlideApp.with(this),
- messageRecord.getDateSent(),
- author,
- messageRecord.getBody(),
- messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck(),
- recipient,
- threadId);
+ messageRecord.getDateSent(),
+ author,
+ messageRecord.getBody(),
+ messageRecord.isMms() ? ((MmsMessageRecord) messageRecord).getSlideDeck() : new SlideDeck(),
+ recipient,
+ threadId);
}
}
@@ -3106,17 +3078,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
// region Loki
private void updateTitleTextView(Recipient recipient) {
- String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
- Set deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userHexEncodedPublicKey);
- HashSet userLinkedDeviceHexEncodedPublicKeys = new HashSet<>();
- for (DeviceLink deviceLink : deviceLinks) {
- userLinkedDeviceHexEncodedPublicKeys.add(deviceLink.getMasterHexEncodedPublicKey().toLowerCase());
- userLinkedDeviceHexEncodedPublicKeys.add(deviceLink.getSlaveHexEncodedPublicKey().toLowerCase());
- }
- userLinkedDeviceHexEncodedPublicKeys.add(userHexEncodedPublicKey.toLowerCase());
+ String userPublicKey = TextSecurePreferences.getLocalNumber(this);
+ Set allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey);
if (recipient == null) {
titleTextView.setText("Compose");
- } else if (userLinkedDeviceHexEncodedPublicKeys.contains(recipient.getAddress().toString().toLowerCase())) {
+ } else if (allUserDevices.contains(recipient.getAddress().toString().toLowerCase())) {
titleTextView.setText("Note to Self");
} else {
boolean hasName = (recipient.getName() != null && !recipient.getName().isEmpty());
@@ -3192,7 +3158,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void handleMessageStatusChanged(String newMessageStatus, long timestamp) {
- if (timestamp == 0) { return; }
+ if (timestamp == 0 || SessionMetaProtocol.shared.isNoteToSelf(recipient.getAddress().serialize()) ) { return; }
updateForNewMessageStatusIfNeeded(newMessageStatus, timestamp);
if (newMessageStatus.equals("messageFailed") || newMessageStatus.equals("messageSent")) {
new Handler().postDelayed(() -> clearMessageStatusIfNeeded(timestamp), 1000);
@@ -3232,48 +3198,16 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void acceptFriendRequest(@NotNull MessageRecord friendRequest) {
- // Send the accept to the original friend request thread ID
- LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(this);
- long originalThreadID = lokiMessageDatabase.getOriginalThreadID(friendRequest.id);
- long threadID = originalThreadID < 0 ? this.threadId : originalThreadID;
- Recipient contact = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID);
- Address address = contact.getAddress();
- String contactHexEncodedPublicKey = address.serialize();
- DatabaseFactory.getLokiThreadDatabase(this).setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS);
- lokiMessageDatabase.setFriendRequestStatus(friendRequest.id, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED);
- DatabaseFactory.getRecipientDatabase(this).setProfileSharing(contact, true);
- MessageSender.sendBackgroundMessageToAllDevices(this, contactHexEncodedPublicKey);
- MessageSender.syncContact(this, address);
+ if (recipient.isGroupRecipient()) { return; }
+ FriendRequestProtocol.acceptFriendRequest(this, recipient);
updateInputPanel();
}
@Override
public void rejectFriendRequest(@NotNull MessageRecord friendRequest) {
- LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(this);
- long originalThreadID = lokiMessageDatabase.getOriginalThreadID(friendRequest.id);
- long threadID = originalThreadID < 0 ? this.threadId : originalThreadID;
- DatabaseFactory.getLokiThreadDatabase(this).setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.NONE);
- String contactID = DatabaseFactory.getThreadDatabase(this).getRecipientForThreadId(threadID).getAddress().toString();
- DatabaseFactory.getLokiPreKeyBundleDatabase(this).removePreKeyBundle(contactID);
- updateInputPanel();
- }
-
- public boolean isNoteToSelf() {
- return TextSecurePreferences.getLocalNumber(this).equals(recipient.getAddress().serialize());
- }
-
- public void restoreSession() {
if (recipient.isGroupRecipient()) { return; }
- LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(this);
- SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(this);
- Set devices = lokiThreadDatabase.getSessionRestoreDevices(threadId);
- for (String device : devices) { MessageSender.sendRestoreSessionMessage(this, device); }
- long messageID = smsDatabase.insertMessageOutbox(threadId, new OutgoingTextMessage(recipient,"", 0, 0), false, System.currentTimeMillis(), null);
- if (messageID > -1) {
- smsDatabase.markAsLokiSessionRestoreSent(messageID);
- }
- lokiThreadDatabase.removeAllSessionRestoreDevices(threadId);
- updateSessionRestoreBanner();
+ FriendRequestProtocol.rejectFriendRequest(this, recipient);
+ updateInputPanel();
}
// endregion
}
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationAdapter.java b/src/org/thoughtcrime/securesms/conversation/ConversationAdapter.java
index 90b0db2fb9..9940097011 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationAdapter.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationAdapter.java
@@ -40,7 +40,7 @@ import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.loki.redesign.views.FriendRequestViewDelegate;
+import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java
index 5978be024e..9d406b88af 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java
@@ -79,7 +79,7 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.loki.redesign.views.FriendRequestViewDelegate;
+import org.thoughtcrime.securesms.loki.views.FriendRequestViewDelegate;
import org.thoughtcrime.securesms.longmessage.LongMessageActivity;
import org.thoughtcrime.securesms.mediasend.Media;
import org.thoughtcrime.securesms.mms.GlideApp;
@@ -101,8 +101,8 @@ import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.whispersystems.libsignal.util.guava.Optional;
-import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat;
-import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChatAPI;
+import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
+import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI;
import java.io.IOException;
import java.io.InputStream;
@@ -375,9 +375,9 @@ public class ConversationFragment extends Fragment
}
if (messageRecords.size() > 1) {
- // menu.findItem(R.id.menu_context_forward).setVisible(false);
+// menu.findItem(R.id.menu_context_forward).setVisible(false);
menu.findItem(R.id.menu_context_reply).setVisible(false);
- // menu.findItem(R.id.menu_context_details).setVisible(false);
+// menu.findItem(R.id.menu_context_details).setVisible(false);
menu.findItem(R.id.menu_context_save_attachment).setVisible(false);
menu.findItem(R.id.menu_context_resend).setVisible(false);
} else {
@@ -726,7 +726,7 @@ public class ConversationFragment extends Fragment
}
if (!loader.hasSent() && !recipient.isSystemContact() && !recipient.isGroupRecipient() && recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
- // adapter.setHeaderView(unknownSenderView);
+// adapter.setHeaderView(unknownSenderView);
} else {
clearHeaderIfNotTyping(adapter);
}
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java
index c4bc562d79..5d46c0fb6c 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java
@@ -86,11 +86,11 @@ import org.thoughtcrime.securesms.jobs.SmsSendJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.loki.LokiMessageDatabase;
-import org.thoughtcrime.securesms.loki.redesign.utilities.MentionUtilities;
-import org.thoughtcrime.securesms.loki.redesign.views.FriendRequestView;
-import org.thoughtcrime.securesms.loki.redesign.views.FriendRequestViewDelegate;
-import org.thoughtcrime.securesms.loki.redesign.views.ProfilePictureView;
+import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
+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.mms.GlideRequests;
import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.PartAuthority;
@@ -112,8 +112,8 @@ import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.views.Stub;
import org.whispersystems.libsignal.util.guava.Optional;
-import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat;
-import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChatAPI;
+import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
+import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChatAPI;
import java.util.Collections;
import java.util.HashSet;
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationPopupActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationPopupActivity.java
index c42c6467ef..b332171e8a 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationPopupActivity.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationPopupActivity.java
@@ -49,8 +49,6 @@ public class ConversationPopupActivity extends ConversationActivity {
else getWindow().setLayout((int) (width * .7), (int) (height * .75));
super.onCreate(bundle, ready);
-
-// titleView.setOnClickListener(null);
}
@Override
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/src/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java
index 1228f85cd3..42b10d38ba 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java
@@ -105,16 +105,16 @@ public class ConversationUpdateItem extends LinearLayout
this.sender.addListener(this);
- if (messageRecord.isGroupAction()) setGroupRecord(messageRecord);
- else if (messageRecord.isCallLog()) setCallRecord(messageRecord);
- else if (messageRecord.isJoined()) setJoinedRecord(messageRecord);
- else if (messageRecord.isExpirationTimerUpdate()) setTimerRecord(messageRecord);
- else if (messageRecord.isEndSession()) setEndSessionRecord(messageRecord);
- else if (messageRecord.isIdentityUpdate()) setIdentityRecord(messageRecord);
+ if (messageRecord.isGroupAction()) setGroupRecord(messageRecord);
+ else if (messageRecord.isCallLog()) setCallRecord(messageRecord);
+ else if (messageRecord.isJoined()) setJoinedRecord(messageRecord);
+ else if (messageRecord.isExpirationTimerUpdate()) setTimerRecord(messageRecord);
+ else if (messageRecord.isEndSession()) setEndSessionRecord(messageRecord);
+ else if (messageRecord.isIdentityUpdate()) setIdentityRecord(messageRecord);
else if (messageRecord.isIdentityVerified() ||
- messageRecord.isIdentityDefault()) setIdentityVerifyUpdate(messageRecord);
+ messageRecord.isIdentityDefault()) setIdentityVerifyUpdate(messageRecord);
else if (messageRecord.isLokiSessionRestoreSent()) setTextMessageRecord(messageRecord);
- else throw new AssertionError("Neither group nor log nor joined.");
+ else throw new AssertionError("Neither group nor log nor joined.");
if (batchSelected.contains(messageRecord)) setSelected(true);
else setSelected(false);
diff --git a/src/org/thoughtcrime/securesms/crypto/PreKeyUtil.java b/src/org/thoughtcrime/securesms/crypto/PreKeyUtil.java
index 7f7d939395..856a0aa52d 100644
--- a/src/org/thoughtcrime/securesms/crypto/PreKeyUtil.java
+++ b/src/org/thoughtcrime/securesms/crypto/PreKeyUtil.java
@@ -43,7 +43,7 @@ public class PreKeyUtil {
private static final int BATCH_SIZE = 100;
- public synchronized static List generatePreKeys(Context context) {
+ public synchronized static List generatePreKeyRecords(Context context) {
PreKeyStore preKeyStore = new TextSecurePreKeyStore(context);
List records = new LinkedList<>();
int preKeyIdOffset = TextSecurePreferences.getNextPreKeyId(context);
@@ -101,7 +101,7 @@ public class PreKeyUtil {
}
}
- public synchronized static List generatePreKeys(Context context, int amount) {
+ public synchronized static List generatePreKeyRecords(Context context, int amount) {
List records = new LinkedList<>();
int preKeyIDOffset = TextSecurePreferences.getNextPreKeyId(context);
for (int i = 0; i < amount; i++) {
diff --git a/src/org/thoughtcrime/securesms/crypto/UnidentifiedAccessUtil.java b/src/org/thoughtcrime/securesms/crypto/UnidentifiedAccessUtil.java
index 7618e9db71..02b9c1f316 100644
--- a/src/org/thoughtcrime/securesms/crypto/UnidentifiedAccessUtil.java
+++ b/src/org/thoughtcrime/securesms/crypto/UnidentifiedAccessUtil.java
@@ -9,22 +9,15 @@ import android.support.annotation.WorkerThread;
import org.signal.libsignal.metadata.SignalProtos;
import org.signal.libsignal.metadata.certificate.CertificateValidator;
import org.signal.libsignal.metadata.certificate.InvalidCertificateException;
-import network.loki.messenger.BuildConfig;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
-import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
-import org.whispersystems.libsignal.InvalidKeyException;
-import org.whispersystems.libsignal.ecc.Curve;
-import org.whispersystems.libsignal.ecc.ECPublicKey;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
-import java.io.IOException;
-
public class UnidentifiedAccessUtil {
private static final String TAG = UnidentifiedAccessUtil.class.getSimpleName();
@@ -116,9 +109,9 @@ public class UnidentifiedAccessUtil {
String ourNumber = TextSecurePreferences.getLocalNumber(context);
if (ourNumber != null) {
SignalProtos.SenderCertificate certificate = SignalProtos.SenderCertificate.newBuilder()
- .setSender(ourNumber)
- .setSenderDevice(SignalServiceAddress.DEFAULT_DEVICE_ID)
- .build();
+ .setSender(ourNumber)
+ .setSenderDevice(SignalServiceAddress.DEFAULT_DEVICE_ID)
+ .build();
return certificate.toByteArray();
}
diff --git a/src/org/thoughtcrime/securesms/database/Address.java b/src/org/thoughtcrime/securesms/database/Address.java
index d777b6e86b..bf7457cdc3 100644
--- a/src/org/thoughtcrime/securesms/database/Address.java
+++ b/src/org/thoughtcrime/securesms/database/Address.java
@@ -105,11 +105,11 @@ public class Address implements Parcelable, Comparable {
public boolean isGroup() { return GroupUtil.isEncodedGroup(address); }
- public boolean isSignalGroup() { return GroupUtil.isSignalGroup(address); }
+ public boolean isClosedGroup() { return GroupUtil.isClosedGroup(address); }
- public boolean isPublicChat() { return GroupUtil.isPublicChat(address); }
+ public boolean isOpenGroup() { return GroupUtil.isOpenGroup(address); }
- public boolean isRSSFeed() { return GroupUtil.isRssFeed(address); }
+ public boolean isRSSFeed() { return GroupUtil.isRSSFeed(address); }
public boolean isMmsGroup() { return GroupUtil.isMmsGroup(address); }
@@ -127,7 +127,7 @@ public class Address implements Parcelable, Comparable {
}
public @NonNull String toPhoneString() {
- if (!isPhone() && !isPublicChat()) {
+ if (!isPhone() && !isOpenGroup()) {
if (isEmail()) throw new AssertionError("Not e164, is email");
if (isGroup()) throw new AssertionError("Not e164, is group");
throw new AssertionError("Not e164, unknown");
diff --git a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java
index 5df509fc18..4465a5c1c0 100644
--- a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java
+++ b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java
@@ -31,11 +31,12 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.helpers.ClassicOpenHelper;
import org.thoughtcrime.securesms.database.helpers.SQLCipherMigrationHelper;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
-import org.thoughtcrime.securesms.loki.*;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIDatabase;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyBundleDatabase;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyRecordDatabase;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiUserDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiPreKeyBundleDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiPreKeyRecordDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
public class DatabaseFactory {
diff --git a/src/org/thoughtcrime/securesms/database/GroupDatabase.java b/src/org/thoughtcrime/securesms/database/GroupDatabase.java
index 502a566f02..f873bfa97d 100644
--- a/src/org/thoughtcrime/securesms/database/GroupDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/GroupDatabase.java
@@ -160,14 +160,15 @@ public class GroupDatabase extends Database {
return recipients;
}
- public boolean signalGroupsHaveMember(String hexEncodedPublicKey) {
+ public boolean isClosedGroupMember(String hexEncodedPublicKey) {
try {
Address address = Address.fromSerialized(hexEncodedPublicKey);
Reader reader = DatabaseFactory.getGroupDatabase(context).getGroups();
+
GroupRecord record;
while ((record = reader.getNext()) != null) {
- if (record.isSignalGroup() && record.members.contains(address)) {
- return true;
+ if (record.isClosedGroup() && record.members.contains(address)) {
+ return true;
}
}
@@ -292,8 +293,7 @@ public class GroupDatabase extends Database {
contents.put(ADMINS, Address.toSerializedList(admins, ','));
contents.put(ACTIVE, 1);
- databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
- new String[] {groupId});
+ databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", new String[] {groupId});
}
public void remove(String groupId, Address source) {
@@ -489,11 +489,11 @@ public class GroupDatabase extends Database {
return mms;
}
- public boolean isPublicChat() { return Address.fromSerialized(id).isPublicChat(); }
+ public boolean isOpenGroup() { return Address.fromSerialized(id).isOpenGroup(); }
public boolean isRSSFeed() { return Address.fromSerialized(id).isRSSFeed(); }
- public boolean isSignalGroup() { return Address.fromSerialized(id).isSignalGroup(); }
+ public boolean isClosedGroup() { return Address.fromSerialized(id).isClosedGroup(); }
public String getUrl() { return url; }
diff --git a/src/org/thoughtcrime/securesms/database/SmsDatabase.java b/src/org/thoughtcrime/securesms/database/SmsDatabase.java
index 2dcc72d5ea..0c2200688c 100644
--- a/src/org/thoughtcrime/securesms/database/SmsDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/SmsDatabase.java
@@ -47,7 +47,6 @@ import org.whispersystems.libsignal.util.guava.Optional;
import java.io.IOException;
import java.security.SecureRandom;
-import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -252,7 +251,7 @@ public class SmsDatabase extends MessagingDatabase {
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_REMOTE_NO_SESSION_BIT);
}
- public void markAsLokiSessionRestoreSent(long id) {
+ public void markAsSentLokiSessionRestorationRequest(long id) {
updateTypeBitmask(id, Types.ENCRYPTION_MASK, Types.ENCRYPTION_LOKI_SESSION_RESTORE_SENT_BIT);
}
@@ -342,12 +341,12 @@ public class SmsDatabase extends MessagingDatabase {
try {
cursor = database.query(TABLE_NAME, new String[] { ID, THREAD_ID, ADDRESS, TYPE },
- DATE_SENT + " = ?", new String[] { String.valueOf(timestamp) },
- null, null, null, null);
+ DATE_SENT + " = ?", new String[] { String.valueOf(timestamp) },
+ null, null, null, null);
while (cursor.moveToNext()) {
if (Types.isOutgoingMessageType(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) {
- isOutgoing = true;
+ isOutgoing = true;
}
}
} finally {
diff --git a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java
index 261abbdb4f..d11e3c9573 100644
--- a/src/org/thoughtcrime/securesms/database/ThreadDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/ThreadDatabase.java
@@ -537,7 +537,7 @@ public class ThreadDatabase extends Database {
}
public @Nullable Recipient getRecipientForThreadId(long threadId) {
- // Loki - Cache the address.
+ // Loki - Cache the address
if (addressCache.containsKey(threadId) && addressCache.get(threadId) != null) {
return Recipient.from(context, addressCache.get(threadId), false);
}
diff --git a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java
index a593c57943..e47442c04c 100644
--- a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java
+++ b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java
@@ -35,17 +35,17 @@ import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.loki.LokiMessageDatabase;
-import org.thoughtcrime.securesms.loki.LokiThreadDatabase;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIDatabase;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyBundleDatabase;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyRecordDatabase;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiUserDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiPreKeyBundleDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiPreKeyRecordDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiUserDatabase;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
-import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat;
+import org.whispersystems.signalservice.loki.api.opengroups.LokiPublicChat;
import java.io.File;
@@ -81,8 +81,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int lokiV5 = 26;
private static final int lokiV6 = 27;
private static final int lokiV7 = 28;
+ private static final int lokiV8 = 29;
- private static final int DATABASE_VERSION = lokiV7; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
+ private static final int DATABASE_VERSION = lokiV8; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
private static final String DATABASE_NAME = "signal.db";
private final Context context;
@@ -138,6 +139,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(LokiAPIDatabase.getCreateLastDeletionServerIDTableCommand());
db.execSQL(LokiAPIDatabase.getCreateDeviceLinkTableCommand());
db.execSQL(LokiAPIDatabase.getCreateUserCountTableCommand());
+ db.execSQL(LokiAPIDatabase.getCreateSessionRequestTimestampTableCommand());
db.execSQL(LokiPreKeyBundleDatabase.getCreateTableCommand());
db.execSQL(LokiPreKeyRecordDatabase.getCreateTableCommand());
db.execSQL(LokiMessageDatabase.getCreateMessageFriendRequestTableCommand());
@@ -181,8 +183,8 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
@Override
public void onConfigure(SQLiteDatabase db) {
super.onConfigure(db);
- // Loki: Enable Write Ahead Logging Mode, increase the cache size
- // This should be disabled if we ever run into serious race condition bugs
+ // Loki - Enable write ahead logging mode and increase the cache size.
+ // This should be disabled if we ever run into serious race condition bugs.
db.enableWriteAheadLogging();
db.execSQL("PRAGMA cache_size = 10000");
}
@@ -544,7 +546,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
if (publicChat != null) {
byte[] groupId = publicChat.getId().getBytes();
String oldId = GroupUtil.getEncodedId(groupId, false);
- String newId = GroupUtil.getEncodedPublicChatId(groupId);
+ String newId = GroupUtil.getEncodedOpenGroupId(groupId);
ContentValues threadUpdate = new ContentValues();
threadUpdate.put("recipient_ids", newId);
db.update("thread", threadUpdate, "recipient_ids = ?", new String[]{ oldId });
@@ -555,7 +557,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
}
}
- // Migrate rss feeds from __textsecure_group__ to __loki_rss_feed_group__
+ // Migrate RSS feeds from __textsecure_group__ to __loki_rss_feed_group__
String[] rssFeedIds = new String[] { "loki.network.feed", "loki.network.messenger-updates.feed" };
for (String groupId : rssFeedIds) {
String oldId = GroupUtil.getEncodedId(groupId.getBytes(), false);
@@ -576,6 +578,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
db.execSQL(LokiMessageDatabase.getCreateErrorMessageTableCommand());
}
+ if (oldVersion < lokiV8) {
+ db.execSQL(LokiAPIDatabase.getCreateSessionRequestTimestampTableCommand());
+ }
+
db.setTransactionSuccessful();
} finally {
db.endTransaction();
diff --git a/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java b/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java
index 87df483995..596d4f84f0 100644
--- a/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java
+++ b/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java
@@ -8,11 +8,11 @@ import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.devicelist.Device;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities;
+import org.thoughtcrime.securesms.loki.utilities.MnemonicUtilities;
import org.thoughtcrime.securesms.util.AsyncLoader;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
-import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec;
+import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
import java.io.File;
import java.util.Collections;
@@ -33,9 +33,9 @@ public class DeviceListLoader extends AsyncLoader> {
@Override
public List loadInBackground() {
try {
- String ourPublicKey = TextSecurePreferences.getLocalNumber(getContext());
- Set secondaryDevicePublicKeys = LokiDeviceLinkUtilities.INSTANCE.getSlaveHexEncodedPublicKeys(ourPublicKey).get();
- List devices = Stream.of(secondaryDevicePublicKeys).map(this::mapToDevice).toList();
+ String userPublicKey = TextSecurePreferences.getLocalNumber(getContext());
+ Set slaveDevicePublicKeys = MultiDeviceProtocol.shared.getSlaveDevices(userPublicKey);
+ List devices = Stream.of(slaveDevicePublicKeys).map(this::mapToDevice).toList();
Collections.sort(devices, new DeviceComparator());
return devices;
} catch (Exception e) {
diff --git a/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java b/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java
index fb7c4b2311..52c9700ebe 100644
--- a/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/SmsMessageRecord.java
@@ -21,7 +21,6 @@ import android.content.Context;
import android.support.annotation.NonNull;
import android.text.SpannableString;
-import network.loki.messenger.R;
import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
@@ -30,6 +29,8 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import java.util.LinkedList;
import java.util.List;
+import network.loki.messenger.R;
+
/**
* The message record model which represents standard SMS messages.
*
@@ -67,9 +68,9 @@ public class SmsMessageRecord extends MessageRecord {
int readReceiptCount, boolean unidentified, boolean isFriendRequest)
{
super(id, body, recipient, individualRecipient, recipientDeviceId,
- dateSent, dateReceived, threadId, status, deliveryReceiptCount, type,
- mismatches, new LinkedList<>(), subscriptionId,
- expiresIn, expireStarted, readReceiptCount, unidentified);
+ dateSent, dateReceived, threadId, status, deliveryReceiptCount, type,
+ mismatches, new LinkedList<>(), subscriptionId,
+ expiresIn, expireStarted, readReceiptCount, unidentified);
this.isFriendRequest = isFriendRequest;
}
diff --git a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
index 1e7a3dfb4a..b74af10069 100644
--- a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
+++ b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
@@ -45,9 +45,8 @@ import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
import org.thoughtcrime.securesms.jobs.TypingSendJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.loki.LokiSessionResetImplementation;
-import org.thoughtcrime.securesms.loki.MultiDeviceOpenGroupUpdateJob;
-import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob;
+import org.thoughtcrime.securesms.loki.protocol.LokiSessionResetImplementation;
+import org.thoughtcrime.securesms.loki.protocol.MultiDeviceOpenGroupUpdateJob;
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
import org.thoughtcrime.securesms.push.MessageSenderEventListener;
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
@@ -112,7 +111,6 @@ import network.loki.messenger.BuildConfig;
MultiDeviceStickerPackOperationJob.class,
MultiDeviceStickerPackSyncJob.class,
LinkPreviewRepository.class,
- PushMessageSyncSendJob.class,
MultiDeviceOpenGroupUpdateJob.class})
public class SignalCommunicationModule {
@@ -154,6 +152,7 @@ public class SignalCommunicationModule {
Optional.fromNullable(IncomingMessageObserver.getUnidentifiedPipe()),
Optional.of(new MessageSenderEventListener(context)),
TextSecurePreferences.getLocalNumber(context),
+ TextSecurePreferences.getMasterHexEncodedPublicKey(context),
DatabaseFactory.getLokiAPIDatabase(context),
DatabaseFactory.getLokiThreadDatabase(context),
DatabaseFactory.getLokiMessageDatabase(context),
diff --git a/src/org/thoughtcrime/securesms/groups/GroupManager.java b/src/org/thoughtcrime/securesms/groups/GroupManager.java
index bcd4521bb8..ee37c5e4f6 100644
--- a/src/org/thoughtcrime/securesms/groups/GroupManager.java
+++ b/src/org/thoughtcrime/securesms/groups/GroupManager.java
@@ -36,18 +36,18 @@ import java.util.Set;
public class GroupManager {
- public static long getPublicChatThreadId(String id, @NonNull Context context) {
- final String groupId = GroupUtil.getEncodedPublicChatId(id.getBytes());
- return getThreadIdFromGroupId(groupId, context);
+ public static long getOpenGroupThreadID(String id, @NonNull Context context) {
+ final String groupID = GroupUtil.getEncodedOpenGroupId(id.getBytes());
+ return getThreadIDFromGroupID(groupID, context);
}
- public static long getRSSFeedThreadId(String id, @NonNull Context context) {
- final String groupId = GroupUtil.getEncodedRSSFeedId(id.getBytes());
- return getThreadIdFromGroupId(groupId, context);
+ public static long getRSSFeedThreadID(String id, @NonNull Context context) {
+ final String groupID = GroupUtil.getEncodedRSSFeedId(id.getBytes());
+ return getThreadIDFromGroupID(groupID, context);
}
- public static long getThreadIdFromGroupId(String groupId, @NonNull Context context) {
- final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupId), false);
+ public static long getThreadIDFromGroupID(String groupID, @NonNull Context context) {
+ final Recipient groupRecipient = Recipient.from(context, Address.fromSerialized(groupID), false);
return DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient);
}
@@ -78,10 +78,10 @@ public class GroupManager {
final Set memberAddresses = getMemberAddresses(members);
final Set adminAddresses = getMemberAddresses(admins);
- String masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
- String ourNumber = masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : TextSecurePreferences.getLocalNumber(context);
+ String masterPublicKeyOrNull = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
+ String masterPublicKey = masterPublicKeyOrNull != null ? masterPublicKeyOrNull : TextSecurePreferences.getLocalNumber(context);
- memberAddresses.add(Address.fromSerialized(ourNumber));
+ memberAddresses.add(Address.fromSerialized(masterPublicKey));
groupDatabase.create(groupId, name, new LinkedList<>(memberAddresses), null, null, new LinkedList<>(adminAddresses));
if (!mms) {
@@ -94,28 +94,28 @@ public class GroupManager {
}
}
- public static @NonNull GroupActionResult createPublicChatGroup(@NonNull String id,
- @NonNull Context context,
- @Nullable Bitmap avatar,
- @Nullable String name)
+ public static @NonNull GroupActionResult createOpenGroup(@NonNull String id,
+ @NonNull Context context,
+ @Nullable Bitmap avatar,
+ @Nullable String name)
{
- final String groupId = GroupUtil.getEncodedPublicChatId(id.getBytes());
- return createLokiGroup(groupId, context, avatar, name);
+ final String groupID = GroupUtil.getEncodedOpenGroupId(id.getBytes());
+ return createLokiGroup(groupID, context, avatar, name);
}
- public static @NonNull GroupActionResult createRSSFeedGroup(@NonNull String id,
- @NonNull Context context,
- @Nullable Bitmap avatar,
- @Nullable String name)
+ public static @NonNull GroupActionResult createRSSFeed(@NonNull String id,
+ @NonNull Context context,
+ @Nullable Bitmap avatar,
+ @Nullable String name)
{
- final String groupId = GroupUtil.getEncodedRSSFeedId(id.getBytes());
- return createLokiGroup(groupId, context, avatar, name);
+ final String groupID = GroupUtil.getEncodedRSSFeedId(id.getBytes());
+ return createLokiGroup(groupID, context, avatar, name);
}
- private static @NonNull GroupActionResult createLokiGroup(@NonNull String groupId,
- @NonNull Context context,
- @Nullable Bitmap avatar,
- @Nullable String name)
+ private static @NonNull GroupActionResult createLokiGroup(@NonNull String groupId,
+ @NonNull Context context,
+ @Nullable Bitmap avatar,
+ @Nullable String name)
{
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
@@ -127,8 +127,8 @@ public class GroupManager {
groupDatabase.updateAvatar(groupId, avatarBytes);
- long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
- return new GroupActionResult(groupRecipient, threadId);
+ long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
+ return new GroupActionResult(groupRecipient, threadID);
}
public static GroupActionResult updateGroup(@NonNull Context context,
diff --git a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java
index 3711ddb8b0..463093b5ca 100644
--- a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java
+++ b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java
@@ -8,7 +8,6 @@ import android.support.annotation.Nullable;
import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.ApplicationContext;
-import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
@@ -18,26 +17,23 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.jobs.AvatarDownloadJob;
import org.thoughtcrime.securesms.jobs.PushGroupUpdateJob;
import org.thoughtcrime.securesms.logging.Log;
+import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
-import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
-import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type;
-import org.whispersystems.signalservice.api.push.SignalServiceAddress;
-import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities;
-import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
+import org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol;
import java.util.Collections;
import java.util.HashSet;
@@ -45,8 +41,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Set;
-import kotlin.Unit;
-
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.AttachmentPointer;
import static org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
@@ -95,7 +89,7 @@ public class GroupMessageProcessor {
builder.setType(GroupContext.Type.UPDATE);
SignalServiceAttachment avatar = group.getAvatar().orNull();
- List members = group.getMembers().isPresent() ? new LinkedList() : null;
+ List members = group.getMembers().isPresent() ? new LinkedList<>() : null;
List admins = group.getAdmins().isPresent() ? new LinkedList<>() : null;
if (group.getMembers().isPresent()) {
@@ -104,13 +98,12 @@ public class GroupMessageProcessor {
}
}
- // We should only create the group if we are part of the member list
- String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, TextSecurePreferences.getLocalNumber(context));
- if (members == null || !members.contains(Address.fromSerialized(hexEncodedPublicKey))) {
- Log.d("Loki - Group Message", "Received a group create message which doesn't include us in the member list. Ignoring.");
+ // Loki - Ignore message if needed
+ if (ClosedGroupsProtocol.shouldIgnoreGroupCreatedMessage(context, group)) {
return null;
}
+ // Loki - Parse admins
if (group.getAdmins().isPresent()) {
for (String admin : group.getAdmins().get()) {
admins.add(Address.fromExternal(context, admin));
@@ -121,7 +114,7 @@ public class GroupMessageProcessor {
avatar != null && avatar.isPointer() ? avatar.asPointer() : null, null, admins);
if (group.getMembers().isPresent()) {
- establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
+ ClosedGroupsProtocol.establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
}
return storeMessage(context, content, group, builder.build(), outgoing);
@@ -137,21 +130,21 @@ public class GroupMessageProcessor {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
String id = GroupUtil.getEncodedId(group);
- String ourHexEncodedPublicKey = getMasterHexEncodedPublicKey(context, TextSecurePreferences.getLocalNumber(context));
+ String userMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) {
- // Only update group if the group admin sent the message
- String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, content.getSender());
- if (!groupRecord.getAdmins().contains(Address.fromSerialized(hexEncodedPublicKey))) {
- Log.d("Loki - Group Message", "Received a group update message from a non-admin user for " + id +". Ignoring.");
+ // Loki - Only update the group if the group admin sent the message
+ String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender());
+ if (!groupRecord.getAdmins().contains(Address.fromSerialized(masterDevice))) {
+ Log.d("Loki", "Received a group update message from a non-admin user for: " + id +"; ignoring.");
return null;
}
- // We should only process update messages if we're in the group
- Address ourAddress = Address.fromSerialized(ourHexEncodedPublicKey);
- if (!groupRecord.getMembers().contains(ourAddress) &&
- !group.getMembers().or(Collections.emptyList()).contains(ourHexEncodedPublicKey)) {
- Log.d("Loki - Group Message", "Received a group update message from a group we are not a member of: " + id + "; ignoring.");
+ // Loki - Only process update messages if we're part of the group
+ Address userMasterDeviceAddress = Address.fromSerialized(userMasterDevice);
+ if (!groupRecord.getMembers().contains(userMasterDeviceAddress) &&
+ !group.getMembers().or(Collections.emptyList()).contains(userMasterDevice)) {
+ Log.d("Loki", "Received a group update message from a group we're not a member of: " + id + "; ignoring.");
database.setActive(id, false);
return null;
}
@@ -180,8 +173,8 @@ public class GroupMessageProcessor {
database.updateMembers(id, new LinkedList<>(newMembers));
}
- // We add any new or removed members to the group context
- // This will allow us later to iterate over them to check if they left or were added for UI purposes
+ // Add any new or removed members to the group context.
+ // This will allow us later to iterate over them to check if they left or were added for UI purposes.
for (Address addedMember : addedMembers) {
builder.addNewMembers(addedMember.serialize());
}
@@ -200,13 +193,13 @@ public class GroupMessageProcessor {
}
// If we were removed then we need to disable the chat
- if (removedMembers.contains(Address.fromSerialized(ourHexEncodedPublicKey))) {
+ if (removedMembers.contains(Address.fromSerialized(userMasterDevice))) {
database.setActive(id, false);
} else {
if (!groupRecord.isActive()) database.setActive(id, true);
if (group.getMembers().isPresent()) {
- establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
+ ClosedGroupsProtocol.establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
}
}
@@ -218,8 +211,8 @@ public class GroupMessageProcessor {
@NonNull SignalServiceGroup group,
@NonNull GroupRecord record)
{
- String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, content.getSender());
- if (record.getMembers().contains(Address.fromSerialized(hexEncodedPublicKey))) {
+ String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender());
+ if (record.getMembers().contains(Address.fromSerialized(masterDevice))) {
ApplicationContext.getInstance(context)
.getJobManager()
.add(new PushGroupUpdateJob(content.getSender(), group.getGroupId()));
@@ -240,9 +233,9 @@ public class GroupMessageProcessor {
GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.QUIT);
- String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, content.getSender());
- if (members.contains(Address.fromExternal(context, hexEncodedPublicKey))) {
- database.remove(id, Address.fromExternal(context, hexEncodedPublicKey));
+ String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender());
+ if (members.contains(Address.fromExternal(context, masterDevice))) {
+ database.remove(id, Address.fromExternal(context, masterDevice));
if (outgoing) database.setActive(id, false);
return storeMessage(context, content, group, builder.build(), outgoing);
@@ -322,32 +315,4 @@ public class GroupMessageProcessor {
return builder;
}
-
- private static String getMasterHexEncodedPublicKey(Context context, String hexEncodedPublicKey) {
- String ourPublicKey = TextSecurePreferences.getLocalNumber(context);
- try {
- String masterHexEncodedPublicKey = hexEncodedPublicKey.equalsIgnoreCase(ourPublicKey)
- ? TextSecurePreferences.getMasterHexEncodedPublicKey(context)
- : PromiseUtil.timeout(LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(hexEncodedPublicKey), 5000).get();
- return masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : hexEncodedPublicKey;
- } catch (Exception e) {
- return hexEncodedPublicKey;
- }
- }
-
- private static void establishSessionsWithMembersIfNeeded(Context context, List members) {
- String ourNumber = TextSecurePreferences.getLocalNumber(context);
- for (String member : members) {
- // Make sure we have session with all of the members secondary devices
- LokiDeviceLinkUtilities.INSTANCE.getAllLinkedDeviceHexEncodedPublicKeys(member).success(devices -> {
- if (devices.contains(ourNumber)) { return Unit.INSTANCE; }
- for (String device : devices) {
- SignalProtocolAddress protocolAddress = new SignalProtocolAddress(device, SignalServiceAddress.DEFAULT_DEVICE_ID);
- boolean haveSession = new TextSecureSessionStore(context).containsSession(protocolAddress);
- if (!haveSession) { MessageSender.sendBackgroundSessionRequest(context, device); }
- }
- return Unit.INSTANCE;
- });
- }
- }
}
diff --git a/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java b/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java
index 5f62d06a3d..5b4b7809ca 100644
--- a/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/AttachmentDownloadJob.java
@@ -196,14 +196,17 @@ public class AttachmentDownloadJob extends BaseJob implements InjectableType {
try {
long id = Long.parseLong(attachment.getLocation());
if (isPublicAttachment) {
- return new SignalServiceAttachmentPointer(id, null, new byte[0],
- Optional.of(Util.toIntExact(attachment.getSize())),
- Optional.absent(),
- 0, 0,
- Optional.fromNullable(attachment.getDigest()),
- Optional.fromNullable(attachment.getFileName()),
- attachment.isVoiceNote(),
- Optional.absent(), attachment.getUrl());
+ return new SignalServiceAttachmentPointer(id,
+ null,
+ new byte[0],
+ Optional.of(Util.toIntExact(attachment.getSize())),
+ Optional.absent(),
+ 0,
+ 0,
+ Optional.fromNullable(attachment.getDigest()),
+ Optional.fromNullable(attachment.getFileName()),
+ attachment.isVoiceNote(),
+ Optional.absent(), attachment.getUrl());
}
byte[] key = Base64.decode(attachment.getKey());
diff --git a/src/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java b/src/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java
index 464d6e6a25..987ce6e787 100644
--- a/src/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/CleanPreKeysJob.java
@@ -40,7 +40,7 @@ public class CleanPreKeysJob extends BaseJob implements InjectableType {
public CleanPreKeysJob() {
this(new Job.Parameters.Builder()
.setQueue("CleanPreKeysJob")
- .setMaxAttempts(5)
+ .setMaxAttempts(3)
.build());
}
diff --git a/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java
index 5300f461fd..a366bafe8c 100644
--- a/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java
+++ b/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java
@@ -13,9 +13,8 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraintObserver;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
-import org.thoughtcrime.securesms.loki.MultiDeviceOpenGroupUpdateJob;
-import org.thoughtcrime.securesms.loki.PushBackgroundMessageSendJob;
-import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob;
+import org.thoughtcrime.securesms.loki.protocol.MultiDeviceOpenGroupUpdateJob;
+import org.thoughtcrime.securesms.loki.protocol.PushEphemeralMessageSendJob;
import java.util.Arrays;
import java.util.HashMap;
@@ -71,8 +70,7 @@ public final class JobManagerFactories {
put(TrimThreadJob.KEY, new TrimThreadJob.Factory());
put(TypingSendJob.KEY, new TypingSendJob.Factory());
put(UpdateApkJob.KEY, new UpdateApkJob.Factory());
- put(PushMessageSyncSendJob.KEY, new PushMessageSyncSendJob.Factory());
- put(PushBackgroundMessageSendJob.KEY, new PushBackgroundMessageSendJob.Factory());
+ put(PushEphemeralMessageSendJob.KEY, new PushEphemeralMessageSendJob.Factory());
put(MultiDeviceOpenGroupUpdateJob.KEY, new MultiDeviceOpenGroupUpdateJob.Factory());
}};
}
diff --git a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java
index d3112ef61c..315e13ad64 100644
--- a/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MmsSendJob.java
@@ -3,11 +3,6 @@ package org.thoughtcrime.securesms.jobs;
import android.content.Context;
import android.support.annotation.NonNull;
import android.text.TextUtils;
-
-import org.thoughtcrime.securesms.jobmanager.Data;
-import org.thoughtcrime.securesms.jobmanager.Job;
-import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
-import org.thoughtcrime.securesms.logging.Log;
import android.webkit.MimeTypeMap;
import com.android.mms.dom.smil.parser.SmilXmlSerializer;
@@ -30,6 +25,10 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.ThreadDatabase;
+import org.thoughtcrime.securesms.jobmanager.Data;
+import org.thoughtcrime.securesms.jobmanager.Job;
+import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
+import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.CompatMmsConnection;
import org.thoughtcrime.securesms.mms.MediaConstraints;
import org.thoughtcrime.securesms.mms.MmsException;
@@ -64,7 +63,7 @@ public class MmsSendJob extends SendJob {
this(new Job.Parameters.Builder()
.setQueue("mms-operation")
.addConstraint(NetworkConstraint.KEY)
- .setMaxAttempts(15)
+ .setMaxAttempts(25)
.build(),
messageId);
}
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java
index 13565bcc1d..ce7e9c53e2 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceBlockedUpdateJob.java
@@ -84,8 +84,7 @@ public class MultiDeviceBlockedUpdateJob extends BaseJob implements InjectableTy
}
}
- // TODO: Message ID
- messageSender.sendMessage(0, SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blockedIndividuals, blockedGroups)),
+ messageSender.sendMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blockedIndividuals, blockedGroups)),
UnidentifiedAccessUtil.getAccessForSync(context));
}
}
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java
index 25b75aeb09..d0da9d6bc9 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java
@@ -91,8 +91,7 @@ public class MultiDeviceConfigurationUpdateJob extends BaseJob implements Inject
return;
}
- // TODO: Message ID
- messageSender.sendMessage(0, SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(readReceiptsEnabled),
+ messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(readReceiptsEnabled),
Optional.of(unidentifiedDeliveryIndicatorsEnabled),
Optional.of(typingIndicatorsEnabled),
Optional.of(linkPreviewsEnabled))),
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java
index 7fde014469..2c18845211 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java
@@ -1,21 +1,16 @@
package org.thoughtcrime.securesms.jobs;
-import android.Manifest;
import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.ApplicationContext;
-import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
-import org.thoughtcrime.securesms.database.Database;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.dependencies.InjectableType;
@@ -23,13 +18,12 @@ import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.permissions.Permissions;
+import org.thoughtcrime.securesms.loki.protocol.SyncMessagesProtocol;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
-import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
@@ -39,18 +33,13 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
-import org.whispersystems.signalservice.api.push.SignalServiceAddress;
-import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
-import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
+import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation;
-import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -65,57 +54,47 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
private static final long FULL_SYNC_TIME = TimeUnit.HOURS.toMillis(6);
private static final String KEY_ADDRESS = "address";
- private static final String KEY_RECIPIENT = "recipient";
private static final String KEY_FORCE_SYNC = "force_sync";
@Inject SignalServiceMessageSender messageSender;
private @Nullable String address;
- // The recipient of this sync message. If null then we send to all devices
- private @Nullable String recipient;
-
private boolean forceSync;
/**
- * Create a full contact sync job which syncs across to all other devices
+ * Create a full contact sync job that syncs to all linked devices.
*/
public MultiDeviceContactUpdateJob(@NonNull Context context) {
this(context, false);
}
- public MultiDeviceContactUpdateJob(@NonNull Context context, boolean forceSync) { this(context, null, forceSync); }
- /**
- * Create a full contact sync job which only gets sent to `recipient`
- */
- public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address recipient, boolean forceSync) {
- this(context, recipient, null, forceSync);
+ public MultiDeviceContactUpdateJob(@NonNull Context context, boolean forceSync) {
+ this(context, null, forceSync);
}
/**
- * Create a single contact sync job which syncs across `address` to the all other devices
+ * Create a single contact sync job that syncs `address` to all linked devices.
*/
public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address) {
- this(context, null, address, true);
+ this(context, address, true);
}
- private MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address recipient, @Nullable Address address, boolean forceSync) {
+ public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address, boolean forceSync) {
this(new Job.Parameters.Builder()
.addConstraint(NetworkConstraint.KEY)
.setQueue("MultiDeviceContactUpdateJob")
.setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(1)
.build(),
- recipient,
address,
forceSync);
}
- private MultiDeviceContactUpdateJob(@NonNull Job.Parameters parameters, @Nullable Address recipient, @Nullable Address address, boolean forceSync) {
+ private MultiDeviceContactUpdateJob(@NonNull Job.Parameters parameters, @Nullable Address address, boolean forceSync) {
super(parameters);
this.forceSync = forceSync;
- this.recipient = (recipient != null) ? recipient.serialize() : null;
if (address != null) this.address = address.serialize();
else this.address = null;
@@ -125,7 +104,6 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
public @NonNull Data serialize() {
return new Data.Builder().putString(KEY_ADDRESS, address)
.putBoolean(KEY_FORCE_SYNC, forceSync)
- .putString(KEY_RECIPIENT, recipient)
.build();
}
@@ -144,14 +122,14 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
}
if (address == null) generateFullContactUpdate();
- else if (!address.equals(TextSecurePreferences.getMasterHexEncodedPublicKey(context))) generateSingleContactUpdate(Address.fromSerialized(address));
+ else if (SyncMessagesProtocol.shouldSyncContact(context, Address.fromSerialized(address))) generateSingleContactUpdate(Address.fromSerialized(address));
}
private void generateSingleContactUpdate(@NonNull Address address)
throws IOException, UntrustedIdentityException, NetworkException
{
// Loki - Only sync regular contacts
- if (!address.isPhone()) { return; }
+ if (!PublicKeyValidation.isValid(address.serialize())) { return; }
File contactDataFile = createTempFile("multidevice-contact-update");
@@ -162,17 +140,15 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
Optional verifiedMessage = getVerifiedMessage(recipient, identityRecord);
// Loki - Only sync contacts we are friends with
- if (getFriendRequestStatus(recipient) == LokiThreadFriendRequestStatus.FRIENDS) {
+ if (SyncMessagesProtocol.shouldSyncContact(context, address)) {
out.write(new DeviceContact(address.toPhoneString(),
- Optional.fromNullable(recipient.getName()),
- getAvatar(recipient.getContactUri()),
- Optional.fromNullable(recipient.getColor().serialize()),
- verifiedMessage,
- Optional.fromNullable(recipient.getProfileKey()),
- recipient.isBlocked(),
- recipient.getExpireMessages() > 0 ?
- Optional.of(recipient.getExpireMessages()) :
- Optional.absent()));
+ Optional.fromNullable(recipient.getName()),
+ getAvatar(recipient.getContactUri()),
+ Optional.fromNullable(recipient.getColor().serialize()),
+ verifiedMessage,
+ Optional.fromNullable(recipient.getProfileKey()),
+ recipient.isBlocked(),
+ recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent()));
}
out.close();
@@ -206,7 +182,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
try {
DeviceContactsOutputStream out = new DeviceContactsOutputStream(new FileOutputStream(contactDataFile));
- List contacts = getAllContacts();
+ List contacts = SyncMessagesProtocol.getContactsToSync(context);
for (ContactData contactData : contacts) {
Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, String.valueOf(contactData.id));
@@ -220,10 +196,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
boolean blocked = recipient.isBlocked();
Optional expireTimer = recipient.getExpireMessages() > 0 ? Optional.of(recipient.getExpireMessages()) : Optional.absent();
- // Loki - Only sync contacts we are friends with
- if (getFriendRequestStatus(recipient) == LokiThreadFriendRequestStatus.FRIENDS) {
- out.write(new DeviceContact(address.toPhoneString(), name, getAvatar(contactUri), color, verified, profileKey, blocked, expireTimer));
- }
+ out.write(new DeviceContact(address.toPhoneString(), name, getAvatar(contactUri), color, verified, profileKey, blocked, expireTimer));
}
if (ProfileKeyUtil.hasProfileKey(context)) {
@@ -244,29 +217,9 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
}
}
- private List getAllContacts() {
- List contactAddresses = new ArrayList<>(DatabaseFactory.getRecipientDatabase(context).getAllAddresses());
- List contacts = new ArrayList<>(contactAddresses.size());
- for (Address address : contactAddresses) {
- if (!address.isPhone()) { continue; }
- long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, address, false));
- String name = DatabaseFactory.getLokiUserDatabase(context).getDisplayName(address.serialize());
- ContactData contactData = new ContactData(threadId, name);
- contactData.numbers.add(new ContactAccessor.NumberData("TextSecure", address.serialize()));
- contacts.add(contactData);
- }
- return contacts;
- }
-
- private LokiThreadFriendRequestStatus getFriendRequestStatus(Recipient recipient) {
- long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient);
- return DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId);
- }
-
@Override
public boolean onShouldRetry(@NonNull Exception exception) {
- // Loki - Disabled because we have our own retrying
- // if (exception instanceof PushNetworkException) return true;
+ // Loki - Disabled since we have our own retrying
return false;
}
@@ -286,13 +239,10 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
.withLength(contactsFile.length())
.build();
- SignalServiceAddress messageRecipient = recipient != null ? new SignalServiceAddress(recipient) : null;
- Address address = recipient != null ? Address.fromSerialized(recipient) : null;
-
- Optional unidentifiedAccess = address != null ? UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, address, false)) : Optional.absent();
+ Optional unidentifiedAccess = address != null ? UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)) : Optional.absent();
try {
- messageSender.sendMessage(0, SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)), unidentifiedAccess, Optional.fromNullable(messageRecipient));
+ messageSender.sendMessage(SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)), unidentifiedAccess);
} catch (IOException ioe) {
throw new NetworkException(ioe);
}
@@ -302,7 +252,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
private Optional getAvatar(@Nullable Uri uri) throws IOException {
return Optional.absent();
- /* Loki - Disabled until we support custom avatars. This will need to be reworked
+ /* Loki - Disabled until we support custom profile pictures. This will then need to be reworked.
if (uri == null) {
return Optional.absent();
}
@@ -397,10 +347,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
String serialized = data.getString(KEY_ADDRESS);
Address address = serialized != null ? Address.fromSerialized(serialized) : null;
- String recipientSerialized = data.getString(KEY_RECIPIENT);
- Address recipient = recipientSerialized != null ? Address.fromSerialized(recipientSerialized) : null;
-
- return new MultiDeviceContactUpdateJob(parameters, recipient, address, data.getBoolean(KEY_FORCE_SYNC));
+ return new MultiDeviceContactUpdateJob(parameters, address, data.getBoolean(KEY_FORCE_SYNC));
}
}
}
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java
index 48afa32221..c35d6b8e3a 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java
@@ -23,7 +23,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentStre
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
-import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -85,7 +84,7 @@ public class MultiDeviceGroupUpdateJob extends BaseJob implements InjectableType
reader = DatabaseFactory.getGroupDatabase(context).getGroups();
while ((record = reader.getNext()) != null) {
- if (record.isSignalGroup()) {
+ if (record.isClosedGroup()) {
List members = new LinkedList<>();
List admins = new LinkedList<>();
@@ -125,8 +124,7 @@ public class MultiDeviceGroupUpdateJob extends BaseJob implements InjectableType
@Override
public boolean onShouldRetry(@NonNull Exception exception) {
- // Loki - Disabled because we have our own retrying
- // if (exception instanceof PushNetworkException) return true;
+ // Loki - Disabled since we have our own retrying
return false;
}
@@ -145,7 +143,7 @@ public class MultiDeviceGroupUpdateJob extends BaseJob implements InjectableType
.withLength(contactsFile.length())
.build();
- messageSender.sendMessage(0, SignalServiceSyncMessage.forGroups(attachmentStream),
+ messageSender.sendMessage(SignalServiceSyncMessage.forGroups(attachmentStream),
UnidentifiedAccessUtil.getAccessForSync(context));
}
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob.java
index 61c081226d..dfcee1093b 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceProfileKeyUpdateJob.java
@@ -87,10 +87,9 @@ public class MultiDeviceProfileKeyUpdateJob extends BaseJob implements Injectabl
.withLength(baos.toByteArray().length)
.build();
- SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, false));
+ SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, false));
- // TODO: Message ID
- messageSender.sendMessage(0, syncMessage, UnidentifiedAccessUtil.getAccessForSync(context));
+ messageSender.sendMessage(syncMessage, UnidentifiedAccessUtil.getAccessForSync(context));
}
@Override
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java
index f83e23e5be..ecf18ec362 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceReadUpdateJob.java
@@ -94,8 +94,7 @@ public class MultiDeviceReadUpdateJob extends BaseJob implements InjectableType
readMessages.add(new ReadMessage(messageId.sender, messageId.timestamp));
}
- // TODO: Message ID
- messageSender.sendMessage(0, SignalServiceSyncMessage.forRead(readMessages), UnidentifiedAccessUtil.getAccessForSync(context));
+ messageSender.sendMessage(SignalServiceSyncMessage.forRead(readMessages), UnidentifiedAccessUtil.getAccessForSync(context));
}
@Override
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackOperationJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackOperationJob.java
index 3a81fb0cdb..f30a353f5e 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackOperationJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackOperationJob.java
@@ -2,20 +2,14 @@ package org.thoughtcrime.securesms.jobs;
import android.support.annotation.NonNull;
-import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.util.Hex;
-import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
-import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
-import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
-import java.util.Collections;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
@@ -76,6 +70,7 @@ public class MultiDeviceStickerPackOperationJob extends BaseJob implements Injec
@Override
protected void onRun() throws Exception {
+ /*
if (!TextSecurePreferences.isMultiDevice(context)) {
Log.i(TAG, "Not multi device, aborting...");
return;
@@ -94,8 +89,9 @@ public class MultiDeviceStickerPackOperationJob extends BaseJob implements Injec
StickerPackOperationMessage stickerPackOperation = new StickerPackOperationMessage(packIdBytes, packKeyBytes, remoteType);
- messageSender.sendMessage(0, SignalServiceSyncMessage.forStickerPackOperations(Collections.singletonList(stickerPackOperation)), // The message ID doesn't matter
+ messageSender.sendMessage(SignalServiceSyncMessage.forStickerPackOperations(Collections.singletonList(stickerPackOperation)),
UnidentifiedAccessUtil.getAccessForSync(context));
+ */
}
@Override
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackSyncJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackSyncJob.java
index 66c12dc190..f918659446 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackSyncJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackSyncJob.java
@@ -2,24 +2,14 @@ package org.thoughtcrime.securesms.jobs;
import android.support.annotation.NonNull;
-import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
-import org.thoughtcrime.securesms.database.DatabaseFactory;
-import org.thoughtcrime.securesms.database.StickerDatabase.StickerPackRecordReader;
-import org.thoughtcrime.securesms.database.model.StickerPackRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.util.Hex;
-import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
-import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
-import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
-import java.util.LinkedList;
-import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
@@ -59,6 +49,8 @@ public class MultiDeviceStickerPackSyncJob extends BaseJob implements Injectable
@Override
protected void onRun() throws Exception {
+ return;
+ /*
if (!TextSecurePreferences.isMultiDevice(context)) {
Log.i(TAG, "Not multi device, aborting...");
return;
@@ -76,8 +68,9 @@ public class MultiDeviceStickerPackSyncJob extends BaseJob implements Injectable
}
}
- messageSender.sendMessage(0, SignalServiceSyncMessage.forStickerPackOperations(operations), // The message ID doesn't matter
+ messageSender.sendMessage(SignalServiceSyncMessage.forStickerPackOperations(operations),
UnidentifiedAccessUtil.getAccessForSync(context));
+ */
}
@Override
diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java
index 975ea65e64..81e6299e2c 100644
--- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceVerifiedUpdateJob.java
@@ -3,23 +3,16 @@ package org.thoughtcrime.securesms.jobs;
import android.support.annotation.NonNull;
-import org.thoughtcrime.securesms.jobmanager.Data;
-import org.thoughtcrime.securesms.jobmanager.Job;
-import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
-import org.thoughtcrime.securesms.logging.Log;
-
-import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.IdentityDatabase.VerifiedStatus;
import org.thoughtcrime.securesms.dependencies.InjectableType;
+import org.thoughtcrime.securesms.jobmanager.Data;
+import org.thoughtcrime.securesms.jobmanager.Job;
+import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.util.Base64;
-import org.thoughtcrime.securesms.recipients.Recipient;
-import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libsignal.IdentityKey;
-import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
-import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
@@ -89,6 +82,7 @@ public class MultiDeviceVerifiedUpdateJob extends BaseJob implements InjectableT
@Override
public void onRun() throws IOException, UntrustedIdentityException {
+ /*
try {
if (!TextSecurePreferences.isMultiDevice(context)) {
Log.i(TAG, "Not multi device...");
@@ -104,12 +98,12 @@ public class MultiDeviceVerifiedUpdateJob extends BaseJob implements InjectableT
VerifiedMessage.VerifiedState verifiedState = getVerifiedState(verifiedStatus);
VerifiedMessage verifiedMessage = new VerifiedMessage(canonicalDestination.toPhoneString(), new IdentityKey(identityKey, 0), verifiedState, timestamp);
- // TODO: Message ID
- messageSender.sendMessage(0, SignalServiceSyncMessage.forVerified(verifiedMessage),
+ messageSender.sendMessage(SignalServiceSyncMessage.forVerified(verifiedMessage),
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(destination), false)));
} catch (InvalidKeyException e) {
throw new IOException(e);
}
+ */
}
private VerifiedMessage.VerifiedState getVerifiedState(VerifiedStatus status) {
diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
index f91b97f574..a91487c717 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
@@ -4,7 +4,6 @@ import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.os.AsyncTask;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -38,7 +37,6 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.thoughtcrime.securesms.crypto.SecurityEvent;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
-import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.database.DatabaseFactory;
@@ -56,10 +54,8 @@ import org.thoughtcrime.securesms.database.StickerDatabase;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
-import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.database.model.StickerRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType;
-import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.groups.GroupMessageProcessor;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
@@ -68,16 +64,17 @@ import org.thoughtcrime.securesms.linkpreview.Link;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.loki.FriendRequestHandler;
-import org.thoughtcrime.securesms.loki.LokiMessageDatabase;
-import org.thoughtcrime.securesms.loki.LokiSessionResetImplementation;
-import org.thoughtcrime.securesms.loki.LokiThreadDatabase;
-import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
-import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIUtilities;
-import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyBundleDatabase;
-import org.thoughtcrime.securesms.loki.redesign.utilities.Broadcaster;
-import org.thoughtcrime.securesms.loki.redesign.utilities.OpenGroupUtilities;
+import org.thoughtcrime.securesms.loki.activities.HomeActivity;
+import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
+import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
+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.SessionManagementProtocol;
+import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol;
+import org.thoughtcrime.securesms.loki.protocol.SyncMessagesProtocol;
+import org.thoughtcrime.securesms.loki.utilities.MentionManagerUtilities;
+import org.thoughtcrime.securesms.loki.utilities.PromiseUtilities;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
@@ -93,7 +90,6 @@ import org.thoughtcrime.securesms.service.WebRtcCallService;
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
-import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage;
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
@@ -103,14 +99,10 @@ import org.thoughtcrime.securesms.util.Hex;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
-import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.loki.LokiSessionResetProtocol;
-import org.whispersystems.libsignal.loki.LokiSessionResetStatus;
-import org.whispersystems.libsignal.state.PreKeyBundle;
import org.whispersystems.libsignal.state.SignalProtocolStore;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
-import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage.Preview;
@@ -124,11 +116,6 @@ import org.whispersystems.signalservice.api.messages.calls.HangupMessage;
import org.whispersystems.signalservice.api.messages.calls.IceUpdateMessage;
import org.whispersystems.signalservice.api.messages.calls.OfferMessage;
import org.whispersystems.signalservice.api.messages.calls.SignalServiceCallMessage;
-import org.whispersystems.signalservice.api.messages.multidevice.ContactsMessage;
-import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact;
-import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsInputStream;
-import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
-import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream;
import org.whispersystems.signalservice.api.messages.multidevice.ReadMessage;
import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
@@ -137,31 +124,23 @@ import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOper
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
-import org.whispersystems.signalservice.loki.api.multidevice.DeviceLink;
-import org.whispersystems.signalservice.loki.api.multidevice.DeviceLinkingSession;
-import org.whispersystems.signalservice.loki.api.LokiAPI;
-import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
-import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat;
import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher;
-import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus;
-import org.whispersystems.signalservice.loki.messaging.LokiServiceMessage;
-import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
-import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
+import org.whispersystems.signalservice.loki.protocol.mentions.MentionsManager;
+import org.whispersystems.signalservice.loki.protocol.meta.LokiServiceMessage;
+import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation;
-import java.io.InputStream;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
import javax.inject.Inject;
-import kotlin.Unit;
import network.loki.messenger.R;
-import nl.komponents.kovenant.Promise;
public class PushDecryptJob extends BaseJob implements InjectableType {
@@ -228,7 +207,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
SignalServiceEnvelope envelope = database.get(messageId);
Optional optionalSmsMessageId = smsMessageId > 0 ? Optional.of(smsMessageId) : Optional.absent();
- handleMessage(envelope, optionalSmsMessageId);
+ handleMessage(envelope, optionalSmsMessageId, false);
database.delete(messageId);
}
}
@@ -243,7 +222,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
}
- public void processMessage(@NonNull SignalServiceEnvelope envelope) {
+ public void processMessage(@NonNull SignalServiceEnvelope envelope, boolean isPushNotification) {
synchronized (PushReceivedJob.RECEIVE_LOCK) {
if (needsMigration()) {
Log.w(TAG, "Skipping and storing envelope, waiting for migration...");
@@ -252,7 +231,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
return;
}
- handleMessage(envelope, Optional.absent());
+ handleMessage(envelope, Optional.absent(), isPushNotification);
}
}
@@ -274,20 +253,19 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
}
- private void handleMessage(@NonNull SignalServiceEnvelope envelope, @NonNull Optional smsMessageId) {
+ private void handleMessage(@NonNull SignalServiceEnvelope envelope, @NonNull Optional smsMessageId, boolean isPushNotification) {
try {
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context);
- LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context);
LokiSessionResetProtocol lokiSessionResetProtocol = new LokiSessionResetImplementation(context);
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, lokiSessionResetProtocol, UnidentifiedAccessUtil.getCertificateValidator());
SignalServiceContent content = cipher.decrypt(envelope);
- // Loki - Ignore any friend requests that we got before restoration
- if (content.isFriendRequest() && content.getTimestamp() < TextSecurePreferences.getRestorationTime(context)) {
- Log.d("Loki", "Ignoring friend request received before restoration.");
+ // Loki - Ignore any friend requests from before restoration
+ if (FriendRequestProtocol.isFriendRequestFromBeforeRestoration(context, content)) {
+ Log.d("Loki", "Ignoring friend request from before restoration.");
return;
}
@@ -297,18 +275,15 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
}
// Loki - Handle friend request acceptance if needed
- if (!content.isFriendRequest() && !isGroupChatMessage(content)) {
- becomeFriendsWithContactIfNeeded(content.getSender(), true, false);
- }
+ FriendRequestProtocol.handleFriendRequestAcceptanceIfNeeded(context, content.getSender(), content);
+
+ // Loki - Handle pre key bundle message if needed
+ SessionManagementProtocol.handlePreKeyBundleMessageIfNeeded(context, content);
// Loki - Handle session request if needed
- handleSessionRequestIfNeeded(content);
-
- // Loki - Store pre key bundle if needed
- if (!content.getDeviceLink().isPresent()) {
- storePreKeyBundleIfNeeded(content);
- }
+ SessionManagementProtocol.handleSessionRequestIfNeeded(context, content);
+ // Loki - Handle address message if needed
if (content.lokiServiceMessage.isPresent()) {
LokiServiceMessage lokiMessage = content.lokiServiceMessage.get();
if (lokiMessage.getAddressMessage() != null) {
@@ -316,47 +291,33 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
}
}
- // Loki - Store the sender display name if needed
- Optional rawSenderDisplayName = content.senderDisplayName;
- if (rawSenderDisplayName.isPresent() && rawSenderDisplayName.get().length() > 0) {
- // If we got a name from our master device then set our display name to match
- String ourMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
- if (ourMasterDevice != null && content.getSender().equals(ourMasterDevice)) {
- TextSecurePreferences.setProfileName(context, rawSenderDisplayName.get());
- }
-
- // If we receive a message from our device then don't set the display name in the database (as we probably have a alias set for them)
- MultiDeviceUtilities.isOneOfOurDevices(context, Address.fromSerialized(content.getSender())).success( isOneOfOurDevices -> {
- if (!isOneOfOurDevices) { setDisplayName(content.getSender(), rawSenderDisplayName.get()); }
- return Unit.INSTANCE;
- });
- }
+ // Loki - Handle profile update if needed
+ SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content);
if (content.getDeviceLink().isPresent()) {
- handleDeviceLinkMessage(content.getDeviceLink().get(), content);
+ MultiDeviceProtocol.handleDeviceLinkMessageIfNeeded(context, content.getDeviceLink().get(), content);
} else if (content.getDataMessage().isPresent()) {
SignalServiceDataMessage message = content.getDataMessage().get();
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
- if (!content.isFriendRequest() && message.isUnlinkingRequest()) {
- // Make sure we got the request from our master device
- String ourMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
- if (ourMasterDevice != null && ourMasterDevice.equals(content.getSender())) {
- TextSecurePreferences.setDatabaseResetFromUnpair(context, true);
- MultiDeviceUtilities.checkIsRevokedSlaveDevice(context);
- }
+ // Loki - Handle unlinking request if needed
+ if (message.isUnlinkingRequest()) {
+ MultiDeviceProtocol.handleUnlinkingRequestIfNeeded(context, content);
} else {
- // Loki - Don't process session restore message any further
+ // Loki - Don't process session restoration requests or session requests any further
if (message.isSessionRestorationRequest() || message.isSessionRequest()) { return; }
- if (message.isEndSession()) handleEndSessionMessage(content, smsMessageId);
- else if (message.isGroupUpdate()) handleGroupMessage(content, message, smsMessageId);
- else if (message.isExpirationUpdate())
+ if (message.isEndSession()) {
+ handleEndSessionMessage(content, smsMessageId);
+ } else if (message.isGroupUpdate()) {
+ handleGroupMessage(content, message, smsMessageId);
+ } else if (message.isExpirationUpdate()) {
handleExpirationUpdate(content, message, smsMessageId);
- else if (isMediaMessage)
+ } else if (isMediaMessage) {
handleMediaMessage(content, message, smsMessageId, Optional.absent());
- else if (message.getBody().isPresent())
+ } else if (message.getBody().isPresent()) {
handleTextMessage(content, message, smsMessageId, Optional.absent());
+ }
if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get()))) {
handleUnknownGroupMessage(content, message.getGroupInfo().get());
@@ -366,25 +327,12 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
handleProfileKey(content, message);
}
- // Loki - This doesn't get invoked for group chats
if (content.isNeedsReceipt()) {
handleNeedsDeliveryReceipt(content, message);
}
- // If we received a friend request, but we were already friends with the user, reset the session
- if (content.isFriendRequest() && !message.isGroupMessage()) {
- Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
- ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
- long threadID = threadDatabase.getThreadIdIfExistsFor(sender);
- if (lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS) {
- resetSession(content.getSender());
- // Let our other devices know that we have reset the session
- MessageSender.syncContact(context, sender.getAddress());
- }
- }
-
- // Loki - Handle friend request logic if needed
- updateFriendRequestStatusIfNeeded(content, message);
+ // Loki - Handle friend request message if needed
+ FriendRequestProtocol.handleFriendRequestMessageIfNeeded(context, content.getSender(), content);
}
} else if (content.getSyncMessage().isPresent()) {
TextSecurePreferences.setMultiDevice(context, true);
@@ -396,9 +344,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), content.getTimestamp());
else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get());
else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get());
- else if (syncMessage.getContacts().isPresent()) handleContactSyncMessage(syncMessage.getContacts().get());
- else if (syncMessage.getGroups().isPresent()) handleGroupSyncMessage(content, syncMessage.getGroups().get());
- else if (syncMessage.getOpenGroups().isPresent()) handleOpenGroupSyncMessage(syncMessage.getOpenGroups().get());
+ else if (syncMessage.getContacts().isPresent()) SyncMessagesProtocol.handleContactSyncMessage(context, content, syncMessage.getContacts().get());
+ else if (syncMessage.getGroups().isPresent()) SyncMessagesProtocol.handleClosedGroupSyncMessage(context, content, syncMessage.getGroups().get());
+ else if (syncMessage.getOpenGroups().isPresent()) SyncMessagesProtocol.handleOpenGroupSyncMessage(context, content, syncMessage.getOpenGroups().get());
else Log.w(TAG, "Contains no known sync types...");
} else if (content.getCallMessage().isPresent()) {
Log.i(TAG, "Got call message...");
@@ -430,7 +378,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
handleInvalidVersionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
} catch (ProtocolInvalidMessageException e) {
Log.w(TAG, e);
- handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
+ if (!isPushNotification) { // This can be triggered if a PN encrypted with an old session comes in after the user performed a session reset
+ handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
+ }
} catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) {
Log.w(TAG, e);
handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId);
@@ -557,26 +507,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
}
if (threadId != null) {
- resetSession(content.getSender());
+ SessionManagementProtocol.handleEndSessionMessageIfNeeded(context, content);
MessageNotifier.updateNotification(context, threadId);
}
}
- private void resetSession(String hexEncodedPublicKey) {
- TextSecureSessionStore sessionStore = new TextSecureSessionStore(context);
- LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context);
-
- Log.d("Loki", "Received a session reset request from: " + hexEncodedPublicKey + "; archiving the session.");
-
- sessionStore.archiveAllSessions(hexEncodedPublicKey);
- lokiThreadDatabase.setSessionResetStatus(hexEncodedPublicKey, LokiSessionResetStatus.REQUEST_RECEIVED);
-
- Log.d("Loki", "Sending a ping back to " + hexEncodedPublicKey + ".");
- MessageSender.sendBackgroundMessage(context, hexEncodedPublicKey);
-
- SecurityEvent.broadcastSecurityUpdateEvent(context);
- }
-
private long handleSynchronizeSentEndSessionMessage(@NonNull SentTranscriptMessage message)
{
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
@@ -611,7 +546,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
{
GroupMessageProcessor.process(context, content, message, false);
- if (message.getExpiresInSeconds() != 0 && message.getExpiresInSeconds() != getRecipientForMessage(content, message).getExpireMessages()) {
+ if (message.getExpiresInSeconds() != 0 && message.getExpiresInSeconds() != getMessageDestination(content, message).getExpireMessages()) {
handleExpirationUpdate(content, message, Optional.absent());
}
@@ -637,8 +572,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
{
try {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
- Recipient recipient = getRecipientForMessage(content, message);
- IncomingMediaMessage mediaMessage = new IncomingMediaMessage(Address.fromSerialized(content.getSender()),
+ Recipient recipient = getMessageMasterDestination(content.getSender());
+ IncomingMediaMessage mediaMessage = new IncomingMediaMessage(getMessageMasterDestination(content.getSender()).getAddress(),
message.getTimestamp(), -1,
message.getExpiresInSeconds() * 1000L, true,
content.isNeedsReceipt(),
@@ -688,92 +623,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
}
}
- private void handleContactSyncMessage(@NonNull ContactsMessage contactsMessage) {
- if (!contactsMessage.getContactsStream().isStream()) { return; }
- Log.d("Loki", "Received contact sync message.");
-
- try {
- InputStream in = contactsMessage.getContactsStream().asStream().getInputStream();
- DeviceContactsInputStream contactsInputStream = new DeviceContactsInputStream(in);
- List deviceContacts = contactsInputStream.readAll();
- for (DeviceContact deviceContact : deviceContacts) {
- // Check if we have the contact as a friend and that we're not trying to sync our own device
- String hexEncodedPublicKey = deviceContact.getNumber();
- Address address = Address.fromSerialized(hexEncodedPublicKey);
- if (!address.isPhone() || address.toPhoneString().equals(TextSecurePreferences.getLocalNumber(context))) { continue; }
-
- /*
- If we're not friends with the contact we received or our friend request expired then we should send them a friend request.
- Otherwise, if we have received a friend request from them, automatically accept the friend request.
- */
- Recipient recipient = Recipient.from(context, address, false);
- long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
- LokiThreadFriendRequestStatus status = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID);
- if (status == LokiThreadFriendRequestStatus.NONE || status == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) {
- // TODO: We should ensure that our mapping has been uploaded to the server before sending out this message
- MessageSender.sendBackgroundFriendRequest(context, hexEncodedPublicKey, "Please accept to enable messages to be synced across devices");
- Log.d("Loki", "Sent friend request to " + hexEncodedPublicKey);
- } else if (status == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
- // Accept the incoming friend request
- becomeFriendsWithContactIfNeeded(hexEncodedPublicKey, false, false);
- // Send them an accept message back
- MessageSender.sendBackgroundMessage(context, hexEncodedPublicKey);
- Log.d("Loki", "Became friends with " + deviceContact.getNumber());
- }
-
- // TODO: Handle blocked - If user is not blocked then we should do the friend request logic otherwise add them to our block list
- // TODO: Handle expiration timer - Update expiration timer?
- // TODO: Handle avatar - Download and set avatar?
- }
- } catch (Exception e) {
- Log.d("Loki", "Failed to sync contact: " + e + ".");
- }
- }
-
- private void handleGroupSyncMessage(@NonNull SignalServiceContent content, @NonNull SignalServiceAttachment groupMessage) {
- if (groupMessage.isStream()) {
- Log.d("Loki", "Received a group sync message.");
- try {
- InputStream in = groupMessage.asStream().getInputStream();
- DeviceGroupsInputStream groupsInputStream = new DeviceGroupsInputStream(in);
- List groups = groupsInputStream.readAll();
- for (DeviceGroup group : groups) {
- SignalServiceGroup serviceGroup = new SignalServiceGroup(
- SignalServiceGroup.Type.UPDATE,
- group.getId(),
- SignalServiceGroup.GroupType.SIGNAL,
- group.getName().orNull(),
- group.getMembers(),
- group.getAvatar().orNull(),
- group.getAdmins()
- );
- SignalServiceDataMessage dataMessage = new SignalServiceDataMessage(content.getTimestamp(), serviceGroup, null, null);
- GroupMessageProcessor.process(context, content, dataMessage, false);
- }
- } catch (Exception e) {
- Log.d("Loki", "Failed to sync group due to error: " + e + ".");
- }
- }
- }
-
- private void handleOpenGroupSyncMessage(@NonNull List openGroups) {
- try {
- for (LokiPublicChat openGroup : openGroups) {
- long threadID = GroupManager.getPublicChatThreadId(openGroup.getId(), context);
- if (threadID > -1) continue;
-
- String url = openGroup.getServer();
- long channel = openGroup.getChannel();
- OpenGroupUtilities.addGroup(context, url, channel).fail(e -> {
- Log.d("Loki", "Failed to sync open group: " + url + " due to error: " + e + ".");
- return Unit.INSTANCE;
- });
- }
- } catch (Exception e) {
- Log.d("Loki", "Failed to sync open groups due to error: " + e + ".");
- }
- }
-
private void handleSynchronizeSentMessage(@NonNull SignalServiceContent content,
@NonNull SentTranscriptMessage message)
throws StorageFailedException
@@ -796,12 +645,12 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
threadId = handleSynchronizeSentTextMessage(message);
}
+ if (threadId == -1L) { threadId = null; }
+
if (message.getMessage().getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get()))) {
handleUnknownGroupMessage(content, message.getMessage().getGroupInfo().get());
}
- String ourMasterDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
- boolean isSenderMasterDevice = ourMasterDevice != null && ourMasterDevice.equals(content.getSender());
if (message.getMessage().getProfileKey().isPresent()) {
Recipient recipient = null;
@@ -813,16 +662,12 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(recipient, true);
}
- // Loki - If we received a sync message from our master device then we need to extract the profile picture url
- if (isSenderMasterDevice) {
- handleProfileKey(content, message.getMessage());
- }
+ // Loki - Handle profile key update if needed
+ handleProfileKey(content, message.getMessage());
}
- // Loki - Update display name from master device
- if (isSenderMasterDevice && content.senderDisplayName.isPresent() && content.senderDisplayName.get().length() > 0) {
- TextSecurePreferences.setProfileName(context, content.senderDisplayName.get());
- }
+ // Loki - Update profile if needed
+ SessionMetaProtocol.handleProfileUpdateIfNeeded(context, content);
if (threadId != null) {
DatabaseFactory.getThreadDatabase(context).setRead(threadId, true);
@@ -903,8 +748,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
@NonNull Optional messageServerIDOrNull)
throws StorageFailedException
{
- Recipient originalRecipient = getRecipientForMessage(content, message);
- Recipient masterRecipient = getMasterRecipientForMessage(content, message);
+ Recipient originalRecipient = getMessageDestination(content, message);
+ Recipient masterRecipient = getMessageMasterDestination(content.getSender());
notifyTypingStoppedFromIncomingMessage(masterRecipient, content.getSender(), content.getSenderDevice());
@@ -913,25 +758,21 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
Optional> linkPreviews = getLinkPreviews(message.getPreviews(), message.getBody().or(""));
Optional sticker = getStickerAttachment(message.getSticker());
- Address sender = masterRecipient.getAddress();
+ Address masterAddress = masterRecipient.getAddress();
- // If message is from group then we need to map it to get the sender of the message
if (message.isGroupMessage()) {
- sender = getMasterRecipient(content.getSender()).getAddress();
+ masterAddress = getMessageMasterDestination(content.getSender()).getAddress();
}
- // Ignore messages from ourselves
- if (sender.serialize().equalsIgnoreCase(TextSecurePreferences.getLocalNumber(context))) { return; }
-
- IncomingMediaMessage mediaMessage = new IncomingMediaMessage(sender, message.getTimestamp(), -1,
- message.getExpiresInSeconds() * 1000L, false, content.isNeedsReceipt(), message.getBody(), message.getGroupInfo(), message.getAttachments(),
- quote, sharedContacts, linkPreviews, sticker);
+ IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterAddress, message.getTimestamp(), -1,
+ message.getExpiresInSeconds() * 1000L, false, content.isNeedsReceipt(), message.getBody(), message.getGroupInfo(), message.getAttachments(),
+ quote, sharedContacts, linkPreviews, sticker);
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
database.beginTransaction();
- // Ignore message if it has no body and no attachments or anything
- if (mediaMessage.getBody().isEmpty() && mediaMessage.getAttachments().isEmpty() && mediaMessage.getSharedContacts().isEmpty() && mediaMessage.getLinkPreviews().isEmpty()) {
+ // Loki - Ignore message if it has no body and no attachments
+ if (mediaMessage.getBody().isEmpty() && mediaMessage.getAttachments().isEmpty() && mediaMessage.getLinkPreviews().isEmpty()) {
return;
}
@@ -967,24 +808,26 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
}
- // Loki - Run database updates in the background, we should look into fixing this in the future
- AsyncTask.execute(() -> {
- // Loki - Store message server ID
- updateGroupChatMessageServerID(messageServerIDOrNull, insertResult);
+ // Loki - Store message open group server ID if needed
+ if (insertResult.isPresent() && messageServerIDOrNull.isPresent()) {
+ long messageID = insertResult.get().getMessageId();
+ long messageServerID = messageServerIDOrNull.get();
+ LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
+ lokiMessageDatabase.setServerID(messageID, messageServerID);
+ }
- // Loki - Update mapping of message to original thread ID
- if (insertResult.isPresent()) {
- ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
- LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
- long originalThreadId = threadDatabase.getThreadIdFor(originalRecipient);
- lokiMessageDatabase.setOriginalThreadID(insertResult.get().getMessageId(), originalThreadId);
- }
- });
+ // Loki - Update mapping of message ID to original thread ID
+ if (insertResult.isPresent()) {
+ ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
+ LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
+ long originalThreadId = threadDatabase.getThreadIdFor(originalRecipient);
+ lokiMessageDatabase.setOriginalThreadID(insertResult.get().getMessageId(), originalThreadId);
+ }
}
private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message) throws MmsException {
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
- Recipient recipient = getSyncMessagePrimaryDestination(message);
+ Recipient recipient = getSyncMessageMasterDestination(message);
OutgoingExpirationUpdateMessage expirationUpdateMessage = new OutgoingExpirationUpdateMessage(recipient,
message.getTimestamp(),
@@ -1004,7 +847,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
throws MmsException
{
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
- Recipient recipients = getSyncMessagePrimaryDestination(message);
+ Recipient recipients = getSyncMessageMasterDestination(message);
Optional quote = getValidatedQuote(message.getMessage().getQuote());
Optional sticker = getStickerAttachment(message.getMessage().getSticker());
Optional> sharedContacts = getContacts(message.getMessage().getSharedContacts());
@@ -1092,8 +935,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
{
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
String body = message.getBody().isPresent() ? message.getBody().get() : "";
- Recipient originalRecipient = getRecipientForMessage(content, message);
- Recipient masterRecipient = getMasterRecipientForMessage(content, message);
+ Recipient originalRecipient = getMessageDestination(content, message);
+ Recipient masterRecipient = getMessageMasterDestination(content.getSender());
if (message.getExpiresInSeconds() != originalRecipient.getExpireMessages()) {
handleExpirationUpdate(content, message, Optional.absent());
@@ -1106,17 +949,13 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} else {
notifyTypingStoppedFromIncomingMessage(masterRecipient, content.getSender(), content.getSenderDevice());
- Address sender = masterRecipient.getAddress();
+ Address masterAddress = masterRecipient.getAddress();
- // If message is from group then we need to map it to get the sender of the message
if (message.isGroupMessage()) {
- sender = getMasterRecipient(content.getSender()).getAddress();
+ masterAddress = getMessageMasterDestination(content.getSender()).getAddress();
}
- // Ignore messages from ourselves
- if (sender.serialize().equalsIgnoreCase(TextSecurePreferences.getLocalNumber(context))) { return; }
-
- IncomingTextMessage tm = new IncomingTextMessage(sender,
+ IncomingTextMessage tm = new IncomingTextMessage(masterAddress,
content.getSenderDevice(),
message.getTimestamp(), body,
message.getGroupInfo(),
@@ -1125,7 +964,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
IncomingEncryptedMessage textMessage = new IncomingEncryptedMessage(tm, body);
- // Ignore the message if the body is empty
+ // Ignore the message if it has no body
if (textMessage.getMessageBody().length() == 0) { return; }
// Insert the message into the database
@@ -1141,249 +980,28 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
MessageNotifier.updateNotification(context, threadId);
}
- // Loki - Run database updates in background, we should look into fixing this in the future
- AsyncTask.execute(() -> {
- if (insertResult.isPresent()) {
- InsertResult result = insertResult.get();
- // Loki - Cache the user hex encoded public key (for mentions)
- LokiAPIUtilities.INSTANCE.populateUserHexEncodedPublicKeyCacheIfNeeded(result.getThreadId(), context);
- LokiAPI.Companion.cache(textMessage.getSender().serialize(), result.getThreadId());
+ if (insertResult.isPresent()) {
+ InsertResult result = insertResult.get();
- // Loki - Store message server ID
- updateGroupChatMessageServerID(messageServerIDOrNull, insertResult);
+ // Loki - Cache the user hex encoded public key (for mentions)
+ MentionManagerUtilities.INSTANCE.populateUserHexEncodedPublicKeyCacheIfNeeded(result.getThreadId(), context);
+ MentionsManager.shared.cache(textMessage.getSender().serialize(), result.getThreadId());
- // Loki - Update mapping of message to original thread ID
- if (result.getMessageId() > -1) {
- ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
- LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
- long originalThreadId = threadDatabase.getThreadIdFor(originalRecipient);
- lokiMessageDatabase.setOriginalThreadID(result.getMessageId(), originalThreadId);
- }
+ // Loki - Store message server ID
+ if (insertResult.isPresent() && messageServerIDOrNull.isPresent()) {
+ long messageID = insertResult.get().getMessageId();
+ long messageServerID = messageServerIDOrNull.get();
+ LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
+ lokiMessageDatabase.setServerID(messageID, messageServerID);
}
- });
- }
- }
- private boolean isValidDeviceLinkMessage(@NonNull DeviceLink authorisation) {
- boolean isSecondaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null;
- String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
- boolean isRequest = (authorisation.getType() == DeviceLink.Type.REQUEST);
- if (authorisation.getRequestSignature() == null) {
- Log.d("Loki", "Ignoring pairing request message without a request signature.");
- return false;
- } else if (isRequest && isSecondaryDevice) {
- Log.d("Loki", "Ignoring unexpected pairing request message (the device is already paired as a secondary device).");
- return false;
- } else if (isRequest && !authorisation.getMasterHexEncodedPublicKey().equals(userHexEncodedPublicKey)) {
- Log.d("Loki", "Ignoring pairing request message addressed to another user.");
- return false;
- } else if (isRequest && authorisation.getSlaveHexEncodedPublicKey().equals(userHexEncodedPublicKey)) {
- Log.d("Loki", "Ignoring pairing request message from self.");
- return false;
- }
- return authorisation.verify();
- }
-
- private void handleDeviceLinkMessage(@NonNull DeviceLink deviceLink, @NonNull SignalServiceContent content) {
- String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
- if (deviceLink.getType() == DeviceLink.Type.REQUEST) {
- handleDeviceLinkRequestMessage(deviceLink, content);
- } else if (deviceLink.getSlaveHexEncodedPublicKey().equals(userHexEncodedPublicKey)) {
- handleDeviceLinkAuthorizedMessage(deviceLink, content);
- }
- }
-
- private void handleDeviceLinkRequestMessage(@NonNull DeviceLink deviceLink, @NonNull SignalServiceContent content) {
- DeviceLinkingSession linkingSession = DeviceLinkingSession.Companion.getShared();
- if (!linkingSession.isListeningForLinkingRequests()) {
- new Broadcaster(context).broadcast("unexpectedDeviceLinkRequestReceived");
- return;
- }
- boolean isValid = isValidDeviceLinkMessage(deviceLink);
- if (!isValid) { return; }
- storePreKeyBundleIfNeeded(content);
- linkingSession.processLinkingRequest(deviceLink);
- }
-
- private void handleDeviceLinkAuthorizedMessage(@NonNull DeviceLink deviceLink, @NonNull SignalServiceContent content) {
- // Check preconditions
- boolean hasExistingDeviceLink = TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null;
- if (hasExistingDeviceLink) {
- Log.d("Loki", "Ignoring unexpected device link message (the device is already linked as a slave device).");
- return;
- }
- boolean isValid = isValidDeviceLinkMessage(deviceLink);
- if (!isValid) {
- Log.d("Loki", "Ignoring invalid device link message.");
- return;
- }
- if (!DeviceLinkingSession.Companion.getShared().isListeningForLinkingRequests()) {
- Log.d("Loki", "Ignoring device link message.");
- return;
- }
- if (deviceLink.getType() != DeviceLink.Type.AUTHORIZATION) { return; }
- Log.d("Loki", "Received device link authorized message from: " + deviceLink.getMasterHexEncodedPublicKey() + ".");
- // Save pre key bundle if we somehow got one
- storePreKeyBundleIfNeeded(content);
- // Process
- DeviceLinkingSession.Companion.getShared().processLinkingAuthorization(deviceLink);
- // Store the master device's ID
- String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
- DatabaseFactory.getLokiAPIDatabase(context).clearDeviceLinks(userHexEncodedPublicKey);
- DatabaseFactory.getLokiAPIDatabase(context).addDeviceLink(deviceLink);
- TextSecurePreferences.setMasterHexEncodedPublicKey(context, deviceLink.getMasterHexEncodedPublicKey());
- TextSecurePreferences.setMultiDevice(context, true);
- // Send a background message to the master device
- MessageSender.sendBackgroundMessage(context, deviceLink.getMasterHexEncodedPublicKey());
- /*
- Update device link on the file server.
- We put this here because after receiving the authorisation message, we will also receive all sync messages.
- If these sync messages are contact syncs then we need to send them friend requests so that we can establish multi-device communication.
- If our device mapping is not stored on the server before the other party receives our message, they will think that they got a friend request from a non-multi-device user.
- */
- try {
- PromiseUtil.timeout(LokiFileServerAPI.shared.addDeviceLink(deviceLink), 8000).get();
- } catch (Exception e) {
- Log.w("Loki", "Failed to upload device links to the file server! " + e);
- }
- // Update display name if needed
- if (content.senderDisplayName.isPresent() && content.senderDisplayName.get().length() > 0) {
- TextSecurePreferences.setProfileName(context, content.senderDisplayName.get());
- }
- // Update profile picture if needed
- if (content.getDataMessage().isPresent()) {
- handleProfileKey(content, content.getDataMessage().get());
- }
- // Handle contact sync if needed
- if (content.getSyncMessage().isPresent() && content.getSyncMessage().get().getContacts().isPresent()) {
- handleContactSyncMessage(content.getSyncMessage().get().getContacts().get());
- }
- }
-
- private void setDisplayName(String hexEncodedPublicKey, String profileName) {
- String displayName = profileName + " (..." + hexEncodedPublicKey.substring(hexEncodedPublicKey.length() - 8) + ")";
- DatabaseFactory.getLokiUserDatabase(context).setDisplayName(hexEncodedPublicKey, displayName);
- }
-
- private void updateGroupChatMessageServerID(Optional messageServerIDOrNull, Optional insertResult) {
- if (!insertResult.isPresent() || !messageServerIDOrNull.isPresent()) { return; }
- long messageID = insertResult.get().getMessageId();
- long messageServerID = messageServerIDOrNull.get();
- DatabaseFactory.getLokiMessageDatabase(context).setServerID(messageID, messageServerID);
- }
-
- private void storePreKeyBundleIfNeeded(@NonNull SignalServiceContent content) {
- Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
- if (sender.isGroupRecipient() || !content.lokiServiceMessage.isPresent()) { return; }
- LokiServiceMessage lokiMessage = content.lokiServiceMessage.get();
- if (lokiMessage.getPreKeyBundleMessage() == null) { return; }
- int registrationID = TextSecurePreferences.getLocalRegistrationId(context);
- LokiPreKeyBundleDatabase lokiPreKeyBundleDatabase = DatabaseFactory.getLokiPreKeyBundleDatabase(context);
- if (registrationID <= 0) { return; }
- Log.d("Loki", "Received a pre key bundle from: " + content.getSender() + ".");
- PreKeyBundle preKeyBundle = lokiMessage.getPreKeyBundleMessage().getPreKeyBundle(registrationID);
- lokiPreKeyBundleDatabase.setPreKeyBundle(content.getSender(), preKeyBundle);
-
- }
-
- private void handleSessionRequestIfNeeded(@NonNull SignalServiceContent content) {
- if (!content.isFriendRequest() || !isSessionRequest(content)) { return; }
- // Check if the session request came from a member in one of our groups or one of our friends
- LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(content.getSender()).success( masterHexEncodedPublicKey -> {
- String sender = masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : content.getSender();
- long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(sender), false));
- LokiThreadFriendRequestStatus threadFriendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID);
- boolean isOurFriend = threadFriendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS;
- boolean isInOneOfOurGroups = DatabaseFactory.getGroupDatabase(context).signalGroupsHaveMember(sender);
- boolean shouldAcceptSessionRequest = isOurFriend || isInOneOfOurGroups;
- if (shouldAcceptSessionRequest) {
- MessageSender.sendBackgroundMessage(context, content.getSender()); // Send a background message to acknowledge
- }
- return Unit.INSTANCE;
- });
- }
-
- private void becomeFriendsWithContactIfNeeded(String hexEncodedPublicKey, boolean requiresContactSync, boolean canSkip) {
- // Ignore friend requests to group recipients
- LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context);
- Recipient contactID = Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false);
- if (contactID.isGroupRecipient()) return;
- // Ignore friend requests to recipients we're already friends with
- long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(contactID);
- LokiThreadFriendRequestStatus threadFriendRequestStatus = lokiThreadDatabase.getFriendRequestStatus(threadID);
- if (threadFriendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS) { return; }
- // We shouldn't be able to skip from NONE to FRIENDS under normal circumstances.
- // Multi-device is the one exception to this rule because we want to automatically become friends with slave devices.
- if (!canSkip && threadFriendRequestStatus == LokiThreadFriendRequestStatus.NONE) { return; }
- // If the thread's friend request status is not `FRIENDS` or `NONE`, but we're receiving a message,
- // it must be a friend request accepted message. Declining a friend request doesn't send a message.
- lokiThreadDatabase.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS);
- // Send out a contact sync message if needed
- if (requiresContactSync) {
- MessageSender.syncContact(context, contactID.getAddress());
- }
- // Enable profile sharing with the recipient
- DatabaseFactory.getRecipientDatabase(context).setProfileSharing(contactID, true);
- // Update the last message if needed
- LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(hexEncodedPublicKey).success( masterHexEncodedPublicKey -> {
- Util.runOnMain(() -> {
- long masterThreadID = (masterHexEncodedPublicKey == null) ? threadID : DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(masterHexEncodedPublicKey), false));
- FriendRequestHandler.updateLastFriendRequestMessage(context, masterThreadID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED);
- });
- return Unit.INSTANCE;
- });
- }
-
- private void updateFriendRequestStatusIfNeeded(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) {
- if (!content.isFriendRequest() || message.isGroupMessage() || message.isSessionRequest()) { return; }
- Promise promise = PromiseUtil.timeout(MultiDeviceUtilities.shouldAutomaticallyBecomeFriendsWithDevice(content.getSender(), context), 8000);
- boolean shouldBecomeFriends = PromiseUtil.get(promise, false);
- if (shouldBecomeFriends) {
- // Become friends AND update the message they sent
- becomeFriendsWithContactIfNeeded(content.getSender(), true, true);
- // Send them an accept message back
- MessageSender.sendBackgroundMessage(context, content.getSender());
- } else {
- // Do regular friend request logic checks
- Recipient originalRecipient = getRecipientForMessage(content, message);
- Recipient masterRecipient = getMasterRecipientForMessage(content, message);
- LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context);
-
- // Loki - Friend requests only work in direct chats
- if (!originalRecipient.getAddress().isPhone()) { return; }
-
- long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(originalRecipient);
- long primaryDeviceThreadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(masterRecipient);
- LokiThreadFriendRequestStatus threadFriendRequestStatus = lokiThreadDatabase.getFriendRequestStatus(threadID);
-
- if (threadFriendRequestStatus == 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 succeeded. 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
- // `REQUEST_SENT`.
- lokiThreadDatabase.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.FRIENDS);
- // Since messages are forwarded to the primary device thread, we need to update it there
- FriendRequestHandler.updateLastFriendRequestMessage(context, primaryDeviceThreadID, LokiMessageFriendRequestStatus.REQUEST_ACCEPTED);
- // Accept the friend request
- MessageSender.sendBackgroundMessage(context, content.getSender());
- // Send contact sync message
- MessageSender.syncContact(context, originalRecipient.getAddress());
- } else if (threadFriendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS) {
- // 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
- // `REQUEST_RECEIVED`.
- lokiThreadDatabase.setFriendRequestStatus(threadID, LokiThreadFriendRequestStatus.REQUEST_RECEIVED);
-
- // Since messages are forwarded to the primary device thread, we need to update it there
- FriendRequestHandler.receivedIncomingFriendRequestMessage(context, primaryDeviceThreadID);
+ // Loki - Update mapping of message to original thread ID
+ if (result.getMessageId() > -1) {
+ ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
+ LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
+ long originalThreadId = threadDatabase.getThreadIdFor(originalRecipient);
+ lokiMessageDatabase.setOriginalThreadID(result.getMessageId(), originalThreadId);
+ }
}
}
}
@@ -1392,10 +1010,13 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
throws MmsException
{
- Recipient recipient = getSyncMessagePrimaryDestination(message);
+ Recipient recipient = getSyncMessageMasterDestination(message);
String body = message.getMessage().getBody().or("");
long expiresInMillis = message.getMessage().getExpiresInSeconds() * 1000L;
+ // Ignore the message if it has no body
+ if (body.isEmpty()) { return -1; }
+
if (recipient.getExpireMessages() != message.getMessage().getExpiresInSeconds()) {
handleSynchronizeSentExpirationUpdate(message);
}
@@ -1464,40 +1085,22 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
}
}
- private SmsMessageRecord getLastMessage(String sender) {
- try {
- SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
- Recipient recipient = Recipient.from(context, Address.fromSerialized(sender), false);
- long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient);
- if (threadID < 0) { return null; }
- int messageCount = smsDatabase.getMessageCountForThread(threadID);
- if (messageCount <= 0) { return null; }
- long lastMessageID = smsDatabase.getIDForMessageAtIndex(threadID, messageCount - 1);
- return smsDatabase.getMessage(lastMessageID);
- } catch (Exception e) {
- return null;
- }
- }
-
private void handleCorruptMessage(@NonNull String sender, int senderDevice, long timestamp,
@NonNull Optional smsMessageId)
{
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
if (!smsMessageId.isPresent()) {
- SmsMessageRecord lastMessage = getLastMessage(sender);
- if (lastMessage == null || !SmsDatabase.Types.isFailedDecryptType(lastMessage.getType())) {
- Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp);
+ Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp);
- if (insertResult.isPresent()) {
- smsDatabase.markAsDecryptFailed(insertResult.get().getMessageId());
- MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
- }
+ if (insertResult.isPresent()) {
+ smsDatabase.markAsDecryptFailed(insertResult.get().getMessageId());
+ MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
}
} else {
smsDatabase.markAsDecryptFailed(smsMessageId.get());
}
- triggerSessionRestorePrompt(sender);
+ SessionManagementProtocol.triggerSessionRestorationUI(context, sender);
}
private void handleNoSessionMessage(@NonNull String sender, int senderDevice, long timestamp,
@@ -1506,27 +1109,16 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
if (!smsMessageId.isPresent()) {
- SmsMessageRecord lastMessage = getLastMessage(sender);
- if (lastMessage == null || !SmsDatabase.Types.isNoRemoteSessionType(lastMessage.getType())) {
- Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp);
+ Optional insertResult = insertPlaceholder(sender, senderDevice, timestamp);
- if (insertResult.isPresent()) {
- smsDatabase.markAsNoSession(insertResult.get().getMessageId());
- MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
- }
+ if (insertResult.isPresent()) {
+ smsDatabase.markAsNoSession(insertResult.get().getMessageId());
+ MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
}
} else {
smsDatabase.markAsNoSession(smsMessageId.get());
}
- triggerSessionRestorePrompt(sender);
- }
-
- private void triggerSessionRestorePrompt(@NonNull String sender) {
- Recipient primaryRecipient = getMasterRecipient(sender);
- if (!primaryRecipient.isGroupRecipient()) {
- long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(primaryRecipient);
- DatabaseFactory.getLokiThreadDatabase(context).addSessionRestoreDevice(threadID, sender);
- }
+ SessionManagementProtocol.triggerSessionRestorationUI(context, sender);
}
private void handleLegacyMessage(@NonNull String sender, int senderDevice, long timestamp,
@@ -1580,10 +1172,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
String url = content.senderProfilePictureURL.or("");
ApplicationContext.getInstance(context).getJobManager().add(new RetrieveProfileAvatarJob(recipient, url));
- // Loki - If the recipient is our master device then we need to go and update our avatar mappings on the public chats
- if (recipient.isOurMasterDevice()) {
- ApplicationContext.getInstance(context).updatePublicChatProfilePictureIfNeeded();
- }
+ SessionMetaProtocol.handleProfileKeyUpdateIfNeeded(context, content);
}
}
@@ -1600,16 +1189,17 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
@NonNull SignalServiceReceiptMessage message)
{
// Redirect message to master device conversation
- Address sender = Address.fromSerialized(content.getSender());
- if (sender.isPhone()) {
- Recipient masterDevice = getMasterRecipient(content.getSender());
- sender = masterDevice.getAddress();
+ Address masterAddress = Address.fromSerialized(content.getSender());
+
+ if (masterAddress.isPhone()) {
+ Recipient masterRecipient = getMessageMasterDestination(content.getSender());
+ masterAddress = masterRecipient.getAddress();
}
for (long timestamp : message.getTimestamps()) {
Log.i(TAG, String.format("Received encrypted delivery receipt: (XXXXX, %d)", timestamp));
DatabaseFactory.getMmsSmsDatabase(context)
- .incrementDeliveryReceiptCount(new SyncMessageId(sender, timestamp), System.currentTimeMillis());
+ .incrementDeliveryReceiptCount(new SyncMessageId(masterAddress, timestamp), System.currentTimeMillis());
}
}
@@ -1620,17 +1210,18 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
if (TextSecurePreferences.isReadReceiptsEnabled(context)) {
// Redirect message to master device conversation
- Address sender = Address.fromSerialized(content.getSender());
- if (sender.isPhone()) {
- Recipient masterDevice = getMasterRecipient(content.getSender());
- sender = masterDevice.getAddress();
+ Address masterAddress = Address.fromSerialized(content.getSender());
+
+ if (masterAddress.isPhone()) {
+ Recipient masterRecipient = getMessageMasterDestination(content.getSender());
+ masterAddress = masterRecipient.getAddress();
}
for (long timestamp : message.getTimestamps()) {
Log.i(TAG, String.format("Received encrypted read receipt: (XXXXX, %d)", timestamp));
DatabaseFactory.getMmsSmsDatabase(context)
- .incrementReadReceiptCount(new SyncMessageId(sender, timestamp), content.getTimestamp());
+ .incrementReadReceiptCount(new SyncMessageId(masterAddress, timestamp), content.getTimestamp());
}
}
}
@@ -1647,14 +1238,14 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
long threadId;
if (typingMessage.getGroupId().isPresent()) {
- // Typing messages should only apply to signal groups, thus we use `getEncodedId`
+ // Typing messages should only apply to closed groups, thus we use `getEncodedId`
Address groupAddress = Address.fromSerialized(GroupUtil.getEncodedId(typingMessage.getGroupId().get(), false));
Recipient groupRecipient = Recipient.from(context, groupAddress, false);
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(groupRecipient);
} else {
// See if we need to redirect the message
- author = getMasterRecipient(content.getSender());
+ author = getMessageMasterDestination(content.getSender());
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(author);
}
@@ -1788,11 +1379,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
}
private Optional insertPlaceholder(@NonNull String sender, int senderDevice, long timestamp) {
- Recipient masterDevice = getMasterRecipient(sender);
- SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
- IncomingTextMessage textMessage = new IncomingTextMessage(masterDevice.getAddress(),
- senderDevice, timestamp, "",
- Optional.absent(), 0, false);
+ Recipient masterRecipient = getMessageMasterDestination(sender);
+ SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
+ IncomingTextMessage textMessage = new IncomingTextMessage(masterRecipient.getAddress(),
+ senderDevice, timestamp, "",
+ Optional.absent(), 0, false);
textMessage = new IncomingEncryptedMessage(textMessage, "");
return database.insertMessageInbox(textMessage);
@@ -1806,50 +1397,60 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
}
}
- private Recipient getSyncMessagePrimaryDestination(SentTranscriptMessage message) {
+ private Recipient getSyncMessageMasterDestination(SentTranscriptMessage message) {
if (message.getMessage().isGroupMessage()) {
- return getSyncMessageDestination(message);
+ return Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getMessage().getGroupInfo().get())), false);
} else {
- return getMasterRecipient(message.getDestination().get());
- }
- }
-
- private Recipient getRecipientForMessage(SignalServiceContent content, SignalServiceDataMessage message) {
- if (message.isGroupMessage()) {
- return Recipient.from(context, Address.fromSerialized(GroupUtil.getEncodedId(message.getGroupInfo().get())), false);
- } else {
- return Recipient.from(context, Address.fromSerialized(content.getSender()), false);
- }
- }
-
- private Recipient getMasterRecipientForMessage(SignalServiceContent content, SignalServiceDataMessage message) {
- if (message.isGroupMessage()) {
- return getRecipientForMessage(content, message);
- } else {
- return getMasterRecipient(content.getSender());
- }
- }
-
- /**
- * Get the master device recipient of the provided device.
- *
- * If the device doesn't have a master device this will return the same device.
- * If the device is our master device then it will return our current device.
- * Otherwise it will return the master device.
- */
- private Recipient getMasterRecipient(String hexEncodedPublicKey) {
- try {
- String masterHexEncodedPublicKey = PromiseUtil.timeout(LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(hexEncodedPublicKey), 5000).get();
- String targetHexEncodedPublicKey = (masterHexEncodedPublicKey != null) ? masterHexEncodedPublicKey : hexEncodedPublicKey;
- // If the public key matches our master device then we need to forward the message to ourselves (note to self)
- String ourMasterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
- if (ourMasterHexEncodedPublicKey != null && ourMasterHexEncodedPublicKey.equals(targetHexEncodedPublicKey)) {
- targetHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
+ String publicKey = message.getDestination().get();
+ String userPublicKey = TextSecurePreferences.getLocalNumber(context);
+ Set allUserDevices = org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey);
+ if (allUserDevices.contains(publicKey)) {
+ return Recipient.from(context, Address.fromSerialized(userPublicKey), false);
+ } else {
+ try {
+ // TODO: Burn this with fire when we can
+ PromiseUtilities.timeout(LokiFileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get();
+ String masterPublicKey = org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey);
+ if (masterPublicKey == null) {
+ masterPublicKey = publicKey;
+ }
+ return Recipient.from(context, Address.fromSerialized(masterPublicKey), false);
+ } catch (Exception e) {
+ return Recipient.from(context, Address.fromSerialized(publicKey), false);
+ }
+ }
+ }
+ }
+
+ private Recipient getMessageDestination(SignalServiceContent content, SignalServiceDataMessage message) {
+ if (message.getGroupInfo().isPresent()) {
+ return Recipient.from(context, Address.fromExternal(context, GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false)), false);
+ } else {
+ return Recipient.from(context, Address.fromExternal(context, content.getSender()), false);
+ }
+ }
+
+ private Recipient getMessageMasterDestination(String publicKey) {
+ if (!PublicKeyValidation.isValid(publicKey)) {
+ return Recipient.from(context, Address.fromSerialized(publicKey), false);
+ } else {
+ String userPublicKey = TextSecurePreferences.getLocalNumber(context);
+ Set allUserDevices = org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey);
+ if (allUserDevices.contains(publicKey)) {
+ return Recipient.from(context, Address.fromSerialized(userPublicKey), false);
+ } else {
+ try {
+ // TODO: Burn this with fire when we can
+ PromiseUtilities.timeout(LokiFileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get();
+ String masterPublicKey = org.whispersystems.signalservice.loki.protocol.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey);
+ if (masterPublicKey == null) {
+ masterPublicKey = publicKey;
+ }
+ return Recipient.from(context, Address.fromSerialized(masterPublicKey), false);
+ } catch (Exception e) {
+ return Recipient.from(context, Address.fromSerialized(publicKey), false);
+ }
}
- return Recipient.from(context, Address.fromSerialized(targetHexEncodedPublicKey), false);
- } catch (Exception e) {
- Log.d("Loki", "Failed to get master device for: " + hexEncodedPublicKey + ". " + e.getMessage());
- return Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false);
}
}
@@ -1869,13 +1470,18 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
return true;
}
+ if (SessionMetaProtocol.shouldIgnoreMessage(content)) {
+ Log.d("Loki", "Ignoring duplicate message.");
+ return true;
+ }
+
Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
if (content.getDeviceLink().isPresent()) {
return false;
} else if (content.getDataMessage().isPresent()) {
SignalServiceDataMessage message = content.getDataMessage().get();
- Recipient conversation = getRecipientForMessage(content, message);
+ Recipient conversation = getMessageDestination(content, message);
if (conversation.isGroupRecipient() && conversation.isBlocked()) {
return true;
@@ -1895,53 +1501,20 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
boolean isGroupActive = groupId.isPresent() && groupDatabase.isActive(groupId.get());
boolean isLeaveMessage = message.getGroupInfo().isPresent() && message.getGroupInfo().get().getType() == SignalServiceGroup.Type.QUIT;
- boolean isClosedGroup = conversation.getAddress().isSignalGroup();
- boolean isGroupMember = true;
-
- // Only allow messages from group members
- if (isClosedGroup) {
- String senderHexEncodedPublicKey = content.getSender();
-
- try {
- String masterHexEncodedPublicKey = PromiseUtil.timeout(LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(content.getSender()), 5000).get();
- if (masterHexEncodedPublicKey != null) {
- senderHexEncodedPublicKey = masterHexEncodedPublicKey;
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- Recipient senderMasterAddress = Recipient.from(context, Address.fromSerialized(senderHexEncodedPublicKey), false);
-
- isGroupMember = groupId.isPresent() && groupDatabase.getGroupMembers(groupId.get(), true).contains(senderMasterAddress);
- }
-
- return (isContentMessage && !isGroupActive) || (sender.isBlocked() && !isLeaveMessage) || (isContentMessage && !isGroupMember);
+ boolean shouldIgnoreContentMessage = ClosedGroupsProtocol.shouldIgnoreContentMessage(context, conversation, groupId.orNull(), content);
+ return (isContentMessage && !isGroupActive) || (sender.isBlocked() && !isLeaveMessage) || (isContentMessage && shouldIgnoreContentMessage);
} else {
return sender.isBlocked();
}
} else if (content.getCallMessage().isPresent() || content.getTypingMessage().isPresent()) {
return sender.isBlocked();
} else if (content.getSyncMessage().isPresent()) {
- try {
- // We should ignore a sync message if the sender is not one of our devices
- boolean isOurDevice = PromiseUtil.timeout(MultiDeviceUtilities.isOneOfOurDevices(context, sender.getAddress()), 5000).get();
- if (!isOurDevice) {
- Log.w(TAG, "Got a sync message from a device that is not ours!.");
- }
- return !isOurDevice;
- } catch (Exception e) {
- return true;
- }
+ return SyncMessagesProtocol.shouldIgnoreSyncMessage(context, sender);
}
return false;
}
- private boolean isSessionRequest(SignalServiceContent content) {
- return content.getDataMessage().isPresent() && content.getDataMessage().get().isSessionRequest();
- }
-
private boolean isGroupChatMessage(SignalServiceContent content) {
return content.getDataMessage().isPresent() && content.getDataMessage().get().isGroupMessage();
}
diff --git a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java
index 7d497dea60..0a98ebddc0 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java
@@ -12,31 +12,26 @@ import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
-import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
-import org.thoughtcrime.securesms.database.GroupReceiptDatabase.GroupReceiptInfo;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
import org.thoughtcrime.securesms.dependencies.InjectableType;
-import org.thoughtcrime.securesms.groups.GroupManager;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
+import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocol;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
-import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.transport.RetryLaterException;
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
import org.thoughtcrime.securesms.util.GroupUtil;
-import org.thoughtcrime.securesms.util.TextSecurePreferences;
-import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
@@ -50,14 +45,10 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup;
import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
-import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities;
-import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat;
-import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -159,36 +150,13 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
try {
log(TAG, "Sending message: " + messageId);
- List target;
+ List targets;
- if (filterAddress != null) target = Collections.singletonList(Address.fromSerialized(filterAddress));
- else if (!existingNetworkFailures.isEmpty()) target = Stream.of(existingNetworkFailures).map(NetworkFailure::getAddress).toList();
- else target = getGroupMessageRecipients(message.getRecipient().getAddress().toGroupString(), messageId);
+ if (filterAddress != null) targets = Collections.singletonList(Address.fromSerialized(filterAddress));
+ else if (!existingNetworkFailures.isEmpty()) targets = Stream.of(existingNetworkFailures).map(NetworkFailure::getAddress).toList();
+ else targets = ClosedGroupsProtocol.getDestinations(message.getRecipient().getAddress().toGroupString(), context).get();
- String localNumber = TextSecurePreferences.getLocalNumber(context);
-
- // Only send messages to the contacts we have sessions with
- List validTargets = Stream.of(target).filter(member -> {
- if (member.isPublicChat()) { return true; }
-
- // Our device is always valid
- if (member.serialize().equalsIgnoreCase(localNumber)) { return true; }
-
- SignalProtocolAddress protocolAddress = new SignalProtocolAddress(member.toPhoneString(), SignalServiceAddress.DEFAULT_DEVICE_ID);
- boolean hasSession = new TextSecureSessionStore(context).containsSession(protocolAddress);
- if (hasSession) { return true; }
-
- // We should allow sending if we have a prekeybundle for the contact
- return DatabaseFactory.getLokiPreKeyBundleDatabase(context).hasPreKeyBundle(member.toPhoneString());
- }).toList();
-
- // Send a session request to the other devices
- List others = Stream.of(target).filter(t -> !validTargets.contains(t)).toList();
- for (Address device : others) {
- MessageSender.sendBackgroundSessionRequest(context, device.toPhoneString());
- }
-
- List results = deliver(message, validTargets);
+ List results = deliver(message, targets);
List networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Address.fromSerialized(result.getAddress().getNumber()))).toList();
List identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(Address.fromSerialized(result.getAddress().getNumber()), result.getIdentityFailure().getIdentityKey())).toList();
Set successAddresses = Stream.of(results).filter(result -> result.getSuccess() != null).map(result -> Address.fromSerialized(result.getAddress().getNumber())).collect(Collectors.toSet());
@@ -237,7 +205,7 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
database.markAsSentFailed(messageId);
notifyMediaMessageDeliveryFailed(context, messageId);
}
- } catch (UntrustedIdentityException | UndeliverableMessageException e) {
+ } catch (Exception e) {
warn(TAG, e);
database.markAsSentFailed(messageId);
notifyMediaMessageDeliveryFailed(context, messageId);
@@ -246,10 +214,8 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
@Override
public boolean onShouldRetry(@NonNull Exception exception) {
- if (exception instanceof IOException) return true;
-
+ if (exception instanceof IOException) return true;
// Loki - Disable since we have our own retrying
- // if (exception instanceof RetryLaterException) return true;
return false;
}
@@ -260,17 +226,18 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
private List deliver(OutgoingMediaMessage message, @NonNull List destinations)
throws IOException, UntrustedIdentityException, UndeliverableMessageException {
- // rotateSenderCertificateIfNecessary();
- // Messages shouldn't be able to be sent to RSS Feeds
- Address groupAddress = message.getRecipient().getAddress();
- if (groupAddress.isRSSFeed()) {
+ // Loki - The user shouldn't be able to message RSS feeds
+ Address address = message.getRecipient().getAddress();
+ if (address.isRSSFeed()) {
List results = new ArrayList<>();
- for (Address destination : destinations) results.add(SendMessageResult.networkFailure(new SignalServiceAddress(destination.toPhoneString())));
+ for (Address destination : destinations) {
+ results.add(SendMessageResult.networkFailure(new SignalServiceAddress(destination.toPhoneString())));
+ }
return results;
}
- String groupId = groupAddress.toGroupString();
+ String groupId = address.toGroupString();
Optional profileKey = getProfileKey(message.getRecipient());
Optional quote = getQuoteFor(message);
Optional sticker = getStickerFor(message);
@@ -281,18 +248,15 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
List attachmentPointers = getAttachmentPointersFor(attachments);
List> unidentifiedAccess = Stream.of(addresses)
- .map(address -> Address.fromSerialized(address.getNumber()))
- .map(address -> Recipient.from(context, address, false))
+ .map(a -> Address.fromSerialized(a.getNumber()))
+ .map(a -> Recipient.from(context, a, false))
.map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient))
.toList();
- SignalServiceGroup.GroupType groupType = SignalServiceGroup.GroupType.SIGNAL;
- if (groupAddress.isPublicChat()) {
- groupType = SignalServiceGroup.GroupType.PUBLIC_CHAT;
- }
+ SignalServiceGroup.GroupType groupType = address.isOpenGroup() ? SignalServiceGroup.GroupType.PUBLIC_CHAT : SignalServiceGroup.GroupType.SIGNAL;
- if (message.isGroup() && groupAddress.isSignalGroup()) {
- // Loki - Only send GroupUpdate or GroupQuit to signal groups
+ if (message.isGroup() && address.isClosedGroup()) {
+ // Loki - Only send GroupUpdate or GroupQuit messages to closed groups
OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message;
GroupContext groupContext = groupMessage.getGroupContext();
SignalServiceAttachment avatar = attachmentPointers.isEmpty() ? null : attachmentPointers.get(0);
@@ -301,7 +265,6 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getSentTimeMillis())
.withExpiration(message.getRecipient().getExpireMessages())
- .withBody(message.getBody())
.asGroupMessage(group)
.build();
@@ -326,65 +289,6 @@ public class PushGroupSendJob extends PushSendJob implements InjectableType {
}
}
- private @NonNull List getGroupMessageRecipients(String groupId, long messageId) {
- if (GroupUtil.isRssFeed(groupId)) { return new ArrayList<>(); }
-
- // Loki - All public chat group messages should be directed to their respective servers
- if (GroupUtil.isPublicChat(groupId)) {
- ArrayList result = new ArrayList<>();
- long threadID = GroupManager.getThreadIdFromGroupId(groupId, context);
- LokiPublicChat publicChat = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID);
- if (publicChat != null) {
- result.add(Address.fromSerialized(groupId));
- }
- return result;
- } else {
- /*
- Our biggest assumption here is that group members will only consist of primary devices.
- No secondary device should be able to be added to a group.
- */
- List destinations = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageId);
-
- Set memberSet = new HashSet<>();
- if (destinations.isEmpty()) {
- List groupMembers = DatabaseFactory.getGroupDatabase(context).getGroupMembers(groupId, false);
- memberSet.addAll(Stream.of(groupMembers).map(Recipient::getAddress).toList());
- } else {
- memberSet.addAll(Stream.of(destinations).map(GroupReceiptInfo::getAddress).toList());
- }
-
- // Replace primary device public key with ours so message syncing works correctly
- String masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
- String localNumber = TextSecurePreferences.getLocalNumber(context);
- if (masterHexEncodedPublicKey != null && memberSet.contains(Address.fromSerialized(masterHexEncodedPublicKey))) {
- memberSet.remove(Address.fromSerialized(masterHexEncodedPublicKey));
- memberSet.add(Address.fromSerialized(localNumber));
- }
-
- // Add secondary devices to the list. We shouldn't add our secondary devices
- try {
- Set originalMemberSet = new HashSet<>(memberSet);
- for (Address member : originalMemberSet) {
- if (!member.isPhone() || member.serialize().equalsIgnoreCase(localNumber)) { continue; }
-
- try {
- Set secondaryDevices = PromiseUtil.timeout(LokiDeviceLinkUtilities.INSTANCE.getSlaveHexEncodedPublicKeys(member.serialize()), 5000).get();
- memberSet.addAll(Stream.of(secondaryDevices).map(string -> {
- // Loki - Calling .map(Address::fromSerialized) is causing errors, thus we use the long method :(
- return Address.fromSerialized(string);
- }).toList());
- } catch (Exception e) {
- // Timed out, go to the next member
- }
- }
- } catch (Exception e) {
- Log.e("PushGroupSend", "Error occurred while adding secondary devices: " + e);
- }
-
- return new LinkedList<>(memberSet);
- }
- }
-
public static class Factory implements Job.Factory {
@Override
public @NonNull PushGroupSendJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) {
diff --git a/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java
index 460b028932..adb49eba35 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java
@@ -123,7 +123,6 @@ public class PushGroupUpdateJob extends BaseJob implements InjectableType {
.withExpiration(groupRecipient.getExpireMessages())
.build();
- // TODO: Message ID
messageSender.sendMessage(0, new SignalServiceAddress(source),
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(source), false)),
message);
diff --git a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java
index 0ce7521000..3779252021 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java
@@ -22,7 +22,7 @@ import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.loki.LokiMessageDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -45,9 +45,7 @@ import org.whispersystems.signalservice.api.messages.shared.SharedContact;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.loki.api.LokiAPI;
-import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities;
-import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage;
-import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
+import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -66,9 +64,8 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
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_DESTINATION = "destination";
- private static final String KEY_IS_FRIEND_REQUEST = "is_friend_request";
+ 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";
- private static final String KEY_SHOULD_SEND_SYNC_MESSAGE = "should_send_sync_message";
@Inject SignalServiceMessageSender messageSender;
@@ -76,33 +73,37 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
private long templateMessageId; // The message ID of the message to template this send job from
// Loki - Multi device
- private Address destination; // Destination to check whether this is another device we're sending to
- private boolean isFriendRequest; // Whether this is a friend request message
+ 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
- private boolean shouldSendSyncMessage;
- public PushMediaSendJob(long messageId, Address destination) { this(messageId, messageId, destination); }
- public PushMediaSendJob(long templateMessageId, long messageId, Address destination) { this(templateMessageId, messageId, destination, false, null, false); }
- public PushMediaSendJob(long templateMessageId, long messageId, Address destination, boolean isFriendRequest, String customFriendRequestMessage, boolean shouldSendSyncMessage) {
- this(constructParameters(destination), templateMessageId, messageId, destination, isFriendRequest, customFriendRequestMessage, shouldSendSyncMessage);
+ public PushMediaSendJob(long messageId, Address destination) {
+ this(messageId, messageId, destination);
}
- private PushMediaSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination, boolean isFriendRequest, String customFriendRequestMessage, boolean shouldSendSyncMessage) {
+ public PushMediaSendJob(long templateMessageId, long messageId, Address destination) {
+ this(templateMessageId, messageId, destination, false, null);
+ }
+
+ public PushMediaSendJob(long templateMessageId, long messageId, Address destination, boolean isLokiPreKeyBundleMessage, String customFriendRequestMessage) {
+ 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);
this.templateMessageId = templateMessageId;
this.messageId = messageId;
this.destination = destination;
- this.isFriendRequest = isFriendRequest;
+ this.isLokiPreKeyBundleMessage = isLokiPreKeyBundleMessage;
this.customFriendRequestMessage = customFriendRequestMessage;
- this.shouldSendSyncMessage = shouldSendSyncMessage;
}
- public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Address destination, boolean shouldSendSyncMessage) {
- enqueue(context, jobManager, messageId, messageId, destination, false, null, shouldSendSyncMessage);
+ public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Address destination) {
+ enqueue(context, jobManager, messageId, messageId, destination, false, null);
}
- public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long templateMessageId, long messageId, @NonNull Address destination, Boolean isFriendRequest, @Nullable String customFriendRequestMessage, boolean shouldSendSyncMessage) {
- enqueue(context, jobManager, Collections.singletonList(new PushMediaSendJob(templateMessageId, messageId, destination, isFriendRequest, customFriendRequestMessage, shouldSendSyncMessage)));
+ public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long templateMessageId, long messageId, @NonNull Address destination, Boolean isFriendRequest, @Nullable String customFriendRequestMessage) {
+ enqueue(context, jobManager, Collections.singletonList(new PushMediaSendJob(templateMessageId, messageId, destination, isFriendRequest, customFriendRequestMessage)));
}
@WorkerThread
@@ -144,11 +145,10 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
@Override
public @NonNull Data serialize() {
Data.Builder builder = new Data.Builder()
- .putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId)
- .putLong(KEY_MESSAGE_ID, messageId)
- .putString(KEY_DESTINATION, destination.serialize())
- .putBoolean(KEY_IS_FRIEND_REQUEST, isFriendRequest)
- .putBoolean(KEY_SHOULD_SEND_SYNC_MESSAGE, shouldSendSyncMessage);
+ .putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId)
+ .putLong(KEY_MESSAGE_ID, messageId)
+ .putString(KEY_DESTINATION, destination.serialize())
+ .putBoolean(KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE, isLokiPreKeyBundleMessage);
if (customFriendRequestMessage != null) { builder.putString(KEY_CUSTOM_FR_MESSAGE, customFriendRequestMessage); }
return builder.build();
@@ -233,15 +233,17 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
}
} catch (LokiAPI.Error e) {
Log.d("Loki", "Couldn't send message due to error: " + e.getDescription());
- if (messageId < 0) { return; }
- LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
- lokiMessageDatabase.setErrorMessage(messageId, e.getDescription());
- database.markAsSentFailed(messageId);
+ if (messageId >= 0) {
+ LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
+ lokiMessageDatabase.setErrorMessage(messageId, e.getDescription());
+ database.markAsSentFailed(messageId);
+ }
}
}
@Override
public boolean onShouldRetry(@NonNull Exception exception) {
+ // Loki - Disable since we have our own retrying
return false;
}
@@ -268,9 +270,15 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
List sharedContacts = getSharedContactsFor(message);
List previews = getPreviewsFor(message);
- // Loki - Include a pre key bundle if the message is a friend request or an end session message
- PreKeyBundle preKeyBundle = isFriendRequest ? DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.getNumber()) : null;
- String body = (isFriendRequest && customFriendRequestMessage != null) ? customFriendRequestMessage : message.getBody();
+ // 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()
.withBody(body)
.withAttachments(serviceAttachments)
@@ -283,25 +291,18 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
.withPreviews(previews)
.asExpirationUpdate(message.isExpirationUpdate())
.withPreKeyBundle(preKeyBundle)
- .asFriendRequest(isFriendRequest)
+ .asFriendRequest(isLokiPreKeyBundleMessage)
.build();
- if (address.getNumber().equals(TextSecurePreferences.getLocalNumber(context))) {
+ if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) {
+ // Loki - Device link messages don't go through here
Optional syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, mediaMessage, syncAccess);
- messageSender.sendMessage(templateMessageId, syncMessage, syncAccess);
+ messageSender.sendMessage(syncMessage, syncAccess);
return syncAccess.isPresent();
} else {
- LokiSyncMessage syncMessage = null;
- if (shouldSendSyncMessage) {
- // Set the sync message destination to the master device, this way it will show that we sent a message to the master device and not the slave device
- String masterDevice = PromiseUtil.get(LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(address.getNumber()), null);
- SignalServiceAddress masterAddress = masterDevice == null ? address : new SignalServiceAddress(masterDevice);
- // We also need to use the original message ID and not -1
- syncMessage = new LokiSyncMessage(masterAddress, templateMessageId);
- }
- SendMessageResult result = messageSender.sendMessage(messageId, address, UnidentifiedAccessUtil.getAccessFor(context, recipient), mediaMessage, Optional.fromNullable(syncMessage));
+ SendMessageResult result = messageSender.sendMessage(messageId, address, UnidentifiedAccessUtil.getAccessFor(context, recipient), mediaMessage);
if (result.getLokiAPIError() != null) {
throw result.getLokiAPIError();
} else {
@@ -326,10 +327,9 @@ public class PushMediaSendJob extends PushSendJob implements InjectableType {
long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID);
long messageID = data.getLong(KEY_MESSAGE_ID);
Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION));
- boolean isFriendRequest = data.getBoolean(KEY_IS_FRIEND_REQUEST);
- boolean shouldSendSyncMessage = data.getBoolean(KEY_SHOULD_SEND_SYNC_MESSAGE);
- String frMessage = data.hasString(KEY_CUSTOM_FR_MESSAGE) ? data.getString(KEY_CUSTOM_FR_MESSAGE) : null;
- return new PushMediaSendJob(parameters, templateMessageID, messageID, destination, isFriendRequest, frMessage, shouldSendSyncMessage);
+ boolean isLokiPreKeyBundleMessage = data.getBoolean(KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE);
+ String customFRMessage = data.hasString(KEY_CUSTOM_FR_MESSAGE) ? data.getString(KEY_CUSTOM_FR_MESSAGE) : null;
+ return new PushMediaSendJob(parameters, templateMessageID, messageID, destination, isLokiPreKeyBundleMessage, customFRMessage);
}
}
}
diff --git a/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java b/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java
index dcc5c706c0..3d38c1c833 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java
@@ -57,7 +57,7 @@ public class PushNotificationReceiveJob extends PushReceivedJob implements Injec
synchronized (PushReceivedJob.RECEIVE_LOCK) {
receiver.retrieveMessages(envelope -> {
Log.i(tag, "Retrieved an envelope." + timeSuffix(startTime));
- processEnvelope(envelope);
+ processEnvelope(envelope, false);
Log.i(tag, "Successfully processed an envelope." + timeSuffix(startTime));
});
TextSecurePreferences.setNeedsMessagePull(context, false);
diff --git a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java
index 053ab5d79e..c93ee94cd0 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java
@@ -22,7 +22,7 @@ public abstract class PushReceivedJob extends BaseJob {
super(parameters);
}
- public void processEnvelope(@NonNull SignalServiceEnvelope envelope) {
+ public void processEnvelope(@NonNull SignalServiceEnvelope envelope, boolean isPushNotification) {
synchronized (RECEIVE_LOCK) {
try {
if (envelope.hasSource()) {
@@ -37,18 +37,18 @@ public abstract class PushReceivedJob extends BaseJob {
if (envelope.isReceipt()) {
handleReceipt(envelope);
} else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage() || envelope.isUnidentifiedSender() || envelope.isFriendRequest()) {
- handleMessage(envelope);
+ handleMessage(envelope, isPushNotification);
} else {
Log.w(TAG, "Received envelope of unknown type: " + envelope.getType());
}
} catch (Exception e) {
- Log.d("Loki", "Failed to process envelope: " + e);
+ Log.d("Loki", "Failed to process envelope due to error: " + e);
}
}
}
- private void handleMessage(SignalServiceEnvelope envelope) {
- new PushDecryptJob(context).processMessage(envelope);
+ private void handleMessage(SignalServiceEnvelope envelope, boolean isPushNotification) {
+ new PushDecryptJob(context).processMessage(envelope, isPushNotification);
}
@SuppressLint("DefaultLocale")
diff --git a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java
index 591e3f43f8..3d0eb2e259 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushSendJob.java
@@ -9,8 +9,6 @@ import android.text.TextUtils;
import com.annimon.stream.Stream;
import org.greenrobot.eventbus.EventBus;
-import org.signal.libsignal.metadata.certificate.InvalidCertificateException;
-import org.signal.libsignal.metadata.certificate.SenderCertificate;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.TextSecureExpiredException;
import org.thoughtcrime.securesms.attachments.Attachment;
@@ -68,7 +66,7 @@ public abstract class PushSendJob extends SendJob {
.setQueue(destination.serialize())
.addConstraint(NetworkConstraint.KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1))
- .setMaxAttempts(3)
+ .setMaxAttempts(1)
.build();
}
@@ -284,13 +282,13 @@ public abstract class PushSendJob extends SendJob {
}
protected SignalServiceSyncMessage buildSelfSendSyncMessage(@NonNull Context context, @NonNull SignalServiceDataMessage message, Optional syncAccess) {
- String primary = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
- String localNumber = primary != null ? primary : TextSecurePreferences.getLocalNumber(context);
- SentTranscriptMessage transcript = new SentTranscriptMessage(localNumber,
- message.getTimestamp(),
- message,
- message.getExpiresInSeconds(),
- Collections.singletonMap(localNumber, syncAccess.isPresent()));
+ String masterPublicKeyOrNull = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
+ String masterPublicKey = masterPublicKeyOrNull != null ? masterPublicKeyOrNull : TextSecurePreferences.getLocalNumber(context);
+ SentTranscriptMessage transcript = new SentTranscriptMessage(masterPublicKey,
+ message.getTimestamp(),
+ message,
+ message.getExpiresInSeconds(),
+ Collections.singletonMap(masterPublicKey, syncAccess.isPresent()));
return SignalServiceSyncMessage.forSentTranscript(transcript);
}
diff --git a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java
index 2b9e034c4f..3d018d14dc 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java
@@ -15,7 +15,7 @@ import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
-import org.thoughtcrime.securesms.loki.LokiMessageDatabase;
+import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
@@ -33,9 +33,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSy
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
import org.whispersystems.signalservice.loki.api.LokiAPI;
-import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities;
-import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage;
-import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
+import org.whispersystems.signalservice.loki.protocol.meta.SessionMetaProtocol;
import java.io.IOException;
@@ -50,9 +48,8 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
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_DESTINATION = "destination";
- private static final String KEY_IS_FRIEND_REQUEST = "is_friend_request";
+ 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";
- private static final String KEY_SHOULD_SEND_SYNC_MESSAGE = "should_send_sync_message";
@Inject SignalServiceMessageSender messageSender;
@@ -60,35 +57,38 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
private long templateMessageId; // The message ID of the message to template this send job from
// Loki - Multi device
- private Address destination; // Destination to check whether this is another device we're sending to
- private boolean isFriendRequest; // Whether this is a friend request message
+ 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
- private boolean shouldSendSyncMessage;
- public PushTextSendJob(long messageId, Address destination) { this(messageId, messageId, destination, false); }
- public PushTextSendJob(long templateMessageId, long messageId, Address destination, boolean shouldSendSyncMessage) { this(templateMessageId, messageId, destination, false, null, shouldSendSyncMessage); }
- public PushTextSendJob(long templateMessageId, long messageId, Address destination, boolean isFriendRequest, String customFriendRequestMessage, boolean shouldSendSyncMessage) {
- this(constructParameters(destination), templateMessageId, messageId, destination, isFriendRequest, customFriendRequestMessage, shouldSendSyncMessage);
+ public PushTextSendJob(long messageId, Address destination) {
+ this(messageId, messageId, destination);
}
- private PushTextSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination, boolean isFriendRequest, String customFriendRequestMessage, boolean shouldSendSyncMessage) {
+ public PushTextSendJob(long templateMessageId, long messageId, Address destination) {
+ this(templateMessageId, messageId, destination, false, null);
+ }
+
+ public PushTextSendJob(long templateMessageId, long messageId, Address destination, boolean isLokiPreKeyBundleMessage, String customFriendRequestMessage) {
+ 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);
this.templateMessageId = templateMessageId;
this.messageId = messageId;
this.destination = destination;
- this.isFriendRequest = isFriendRequest;
+ this.isLokiPreKeyBundleMessage = isLokiPreKeyBundleMessage;
this.customFriendRequestMessage = customFriendRequestMessage;
- this.shouldSendSyncMessage = shouldSendSyncMessage;
}
@Override
public @NonNull Data serialize() {
Data.Builder builder = new Data.Builder()
- .putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId)
- .putLong(KEY_MESSAGE_ID, messageId)
- .putString(KEY_DESTINATION, destination.serialize())
- .putBoolean(KEY_IS_FRIEND_REQUEST, isFriendRequest)
- .putBoolean(KEY_SHOULD_SEND_SYNC_MESSAGE, shouldSendSyncMessage);
+ .putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId)
+ .putLong(KEY_MESSAGE_ID, messageId)
+ .putString(KEY_DESTINATION, destination.serialize())
+ .putBoolean(KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE, isLokiPreKeyBundleMessage);
if (customFriendRequestMessage != null) { builder.putString(KEY_CUSTOM_FR_MESSAGE, customFriendRequestMessage); }
return builder.build();
@@ -116,14 +116,14 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
boolean hasSameDestination = destination.equals(recordRecipient.getAddress());
if (hasSameDestination && !record.isPending() && !record.isFailed()) {
- warn(TAG, "Message " + templateMessageId + " was already sent. Ignoring.");
+ Log.d("Loki", "Message with ID: " + templateMessageId + " was already sent; ignoring.");
return;
}
try {
- log(TAG, "Sending message: " + templateMessageId + (hasSameDestination ? "" : "to another device."));
+ log(TAG, "Sending message: " + templateMessageId + (hasSameDestination ? "" : "to a linked device."));
- Recipient recipient = Recipient.from(context, destination, false);
+ Recipient recipient = Recipient.from(context, destination, false);
byte[] profileKey = recipient.getProfileKey();
UnidentifiedAccessMode accessMode = recipient.getUnidentifiedAccessMode();
@@ -158,16 +158,16 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
expirationManager.scheduleDeletion(record.getId(), record.isMms(), record.getExpiresIn());
}
- log(TAG, "Sent message: " + templateMessageId + (hasSameDestination ? "" : "to another device."));
+ log(TAG, "Sent message: " + templateMessageId + (hasSameDestination ? "" : "to a linked device."));
} catch (InsecureFallbackApprovalException e) {
- warn(TAG, "Failure", e);
+ warn(TAG, "Couldn't send message due to error: ", e);
if (messageId >= 0) {
database.markAsPendingInsecureSmsFallback(record.getId());
MessageNotifier.notifyMessageDeliveryFailed(context, record.getRecipient(), record.getThreadId());
}
} catch (UntrustedIdentityException e) {
- warn(TAG, "Failure", e);
+ warn(TAG, "Couldn't send message due to error: ", e);
if (messageId >= 0) {
database.addMismatchedIdentity(record.getId(), Address.fromSerialized(e.getE164Number()), e.getIdentityKey());
database.markAsSentFailed(record.getId());
@@ -175,18 +175,17 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
}
} catch (LokiAPI.Error e) {
Log.d("Loki", "Couldn't send message due to error: " + e.getDescription());
- if (messageId < 0) { return; }
- LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
- lokiMessageDatabase.setErrorMessage(record.getId(), e.getDescription());
- database.markAsSentFailed(record.getId());
+ if (messageId >= 0) {
+ LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context);
+ lokiMessageDatabase.setErrorMessage(record.getId(), e.getDescription());
+ database.markAsSentFailed(record.getId());
+ }
}
}
@Override
public boolean onShouldRetry(@NonNull Exception exception) {
// Loki - Disable since we have our own retrying
- // if (exception instanceof RetryLaterException) return true;
-
return false;
}
@@ -208,7 +207,6 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
throws UntrustedIdentityException, InsecureFallbackApprovalException, RetryLaterException, LokiAPI.Error
{
try {
- // rotateSenderCertificateIfNecessary();
Recipient recipient = Recipient.from(context, destination, false);
SignalServiceAddress address = getPushAddress(recipient.getAddress());
Optional profileKey = getProfileKey(recipient);
@@ -216,41 +214,34 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
log(TAG, "Have access key to use: " + unidentifiedAccess.isPresent());
- // Loki - Include a pre key bundle if the message is a friend request or an end session message
+ // Loki - Include a pre key bundle if needed
PreKeyBundle preKeyBundle;
- if (isFriendRequest || message.isEndSession()) {
+ if (isLokiPreKeyBundleMessage || message.isEndSession()) {
preKeyBundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(address.getNumber());
} else {
preKeyBundle = null;
}
- String body = (isFriendRequest && customFriendRequestMessage != null) ? customFriendRequestMessage : message.getBody();
+ String body = (isLokiPreKeyBundleMessage && customFriendRequestMessage != null) ? customFriendRequestMessage : message.getBody();
SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(message.getDateSent())
.withBody(body)
.withExpiration((int)(message.getExpiresIn() / 1000))
.withProfileKey(profileKey.orNull())
.asEndSessionMessage(message.isEndSession())
- .asFriendRequest(isFriendRequest)
+ .asFriendRequest(isLokiPreKeyBundleMessage)
.withPreKeyBundle(preKeyBundle)
.build();
- if (address.getNumber().equals(TextSecurePreferences.getLocalNumber(context))) {
+ if (SessionMetaProtocol.shared.isNoteToSelf(address.getNumber())) {
+ // Loki - Device link messages don't go through here
Optional syncAccess = UnidentifiedAccessUtil.getAccessForSync(context);
- SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, textSecureMessage, syncAccess);
+ SignalServiceSyncMessage syncMessage = buildSelfSendSyncMessage(context, textSecureMessage, syncAccess);
- messageSender.sendMessage(templateMessageId, syncMessage, syncAccess);
+ messageSender.sendMessage(syncMessage, syncAccess);
return syncAccess.isPresent();
} else {
- LokiSyncMessage syncMessage = null;
- if (shouldSendSyncMessage) {
- // Set the sync message destination to the master device, this way it will show that we sent a message to the master device and not the slave device
- String masterDevice = PromiseUtil.get(LokiDeviceLinkUtilities.INSTANCE.getMasterHexEncodedPublicKey(address.getNumber()), null);
- SignalServiceAddress masterAddress = masterDevice == null ? address : new SignalServiceAddress(masterDevice);
- // We also need to use the original message ID and not -1
- syncMessage = new LokiSyncMessage(masterAddress, templateMessageId);
- }
- SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccess, textSecureMessage, Optional.fromNullable(syncMessage));
+ SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccess, textSecureMessage);
if (result.getLokiAPIError() != null) {
throw result.getLokiAPIError();
} else {
@@ -272,10 +263,9 @@ public class PushTextSendJob extends PushSendJob implements InjectableType {
long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID);
long messageID = data.getLong(KEY_MESSAGE_ID);
Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION));
- boolean isFriendRequest = data.getBoolean(KEY_IS_FRIEND_REQUEST);
- String frMessage = data.hasString(KEY_CUSTOM_FR_MESSAGE) ? data.getString(KEY_CUSTOM_FR_MESSAGE) : null;
- boolean shouldSendSyncMessage = data.getBoolean(KEY_SHOULD_SEND_SYNC_MESSAGE);
- return new PushTextSendJob(parameters, templateMessageID, messageID, destination, isFriendRequest, frMessage, shouldSendSyncMessage);
+ boolean isLokiPreKeyBundleMessage = data.getBoolean(KEY_IS_LOKI_PRE_KEY_BUNDLE_MESSAGE);
+ String customFRMessage = data.hasString(KEY_CUSTOM_FR_MESSAGE) ? data.getString(KEY_CUSTOM_FR_MESSAGE) : null;
+ return new PushTextSendJob(parameters, templateMessageID, messageID, destination, isLokiPreKeyBundleMessage, customFRMessage);
}
}
}
diff --git a/src/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java b/src/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java
index 8afc280327..24518c5eb9 100644
--- a/src/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/RefreshPreKeysJob.java
@@ -2,16 +2,11 @@ package org.thoughtcrime.securesms.jobs;
import android.support.annotation.NonNull;
-import org.thoughtcrime.securesms.ApplicationContext;
-import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
-import org.thoughtcrime.securesms.crypto.PreKeyUtil;
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
-import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.util.TextSecurePreferences;
-import org.whispersystems.libsignal.IdentityKeyPair;
+import org.thoughtcrime.securesms.loki.protocol.SessionManagementProtocol;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException;
import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException;
@@ -34,7 +29,7 @@ public class RefreshPreKeysJob extends BaseJob implements InjectableType {
this(new Job.Parameters.Builder()
.setQueue("RefreshPreKeysJob")
.addConstraint(NetworkConstraint.KEY)
- .setMaxAttempts(5)
+ .setMaxAttempts(3)
.build());
}
@@ -54,15 +49,7 @@ public class RefreshPreKeysJob extends BaseJob implements InjectableType {
@Override
public void onRun() throws IOException {
- if (TextSecurePreferences.isSignedPreKeyRegistered(context)) {
- Log.d("Loki", "A signed pre key has already been registered.");
- } else {
- Log.d("Loki", "Registering a new signed pre key.");
- IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context);
- PreKeyUtil.generateSignedPreKey(context, identityKey, true);
- TextSecurePreferences.setSignedPreKeyRegistered(context, true);
- ApplicationContext.getInstance(context).getJobManager().add(new CleanPreKeysJob());
- }
+ SessionManagementProtocol.refreshSignedPreKey(context);
}
/* Loki - Original code
@@ -77,7 +64,7 @@ public class RefreshPreKeysJob extends BaseJob implements InjectableType {
return;
}
- List preKeyRecords = PreKeyUtil.generatePreKeys(context);
+ List preKeyRecords = PreKeyUtil.generatePreKeyRecords(context);
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context);
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKey, false);
diff --git a/src/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java b/src/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java
index 795e1060c0..bb9cc72d91 100644
--- a/src/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java
@@ -79,7 +79,6 @@ public class RequestGroupInfoJob extends BaseJob implements InjectableType {
.withTimestamp(System.currentTimeMillis())
.build();
- // TODO: Message ID
messageSender.sendMessage(0, new SignalServiceAddress(source),
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromExternal(context, source), false)),
message);
diff --git a/src/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java b/src/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java
index ecfa3ecdac..9c574bd6ea 100644
--- a/src/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/RetrieveProfileAvatarJob.java
@@ -49,7 +49,7 @@ public class RetrieveProfileAvatarJob extends BaseJob implements InjectableType
.setQueue("RetrieveProfileAvatarJob" + recipient.getAddress().serialize())
.addConstraint(NetworkConstraint.KEY)
.setLifespan(TimeUnit.HOURS.toMillis(1))
- .setMaxAttempts(2)
+ .setMaxAttempts(3)
.build(),
recipient,
profileAvatar);
@@ -99,8 +99,8 @@ public class RetrieveProfileAvatarJob extends BaseJob implements InjectableType
File downloadDestination = File.createTempFile("avatar", "jpg", context.getCacheDir());
try {
- InputStream avatarStream = receiver.retrieveProfileAvatar(profileAvatar, downloadDestination, profileKey, MAX_PROFILE_SIZE_BYTES);
- File decryptDestination = File.createTempFile("avatar", "jpg", context.getCacheDir());
+ InputStream avatarStream = receiver.retrieveProfileAvatar(profileAvatar, downloadDestination, profileKey, MAX_PROFILE_SIZE_BYTES);
+ File decryptDestination = File.createTempFile("avatar", "jpg", context.getCacheDir());
Util.copy(avatarStream, new FileOutputStream(decryptDestination));
decryptDestination.renameTo(AvatarHelper.getAvatarFile(context, recipient.getAddress()));
diff --git a/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java b/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java
index 43d3fdde7d..25ed74dbb0 100644
--- a/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java
@@ -77,7 +77,7 @@ public class RetrieveProfileJob extends BaseJob implements InjectableType {
@Override
public void onRun() throws IOException, InvalidKeyException {
- // Loki - Disable retrieve profile
+ // Loki - Do nothing
/*
try {
if (recipient.isGroupRecipient()) handleGroupRecipient(recipient);
diff --git a/src/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java b/src/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java
index 9dba4c7dd9..3caf337f5f 100644
--- a/src/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/RotateCertificateJob.java
@@ -56,10 +56,12 @@ public class RotateCertificateJob extends BaseJob implements InjectableType {
@Override
public void onRun() throws IOException {
// Loki - Do nothing
-// synchronized (RotateCertificateJob.class) {
-// byte[] certificate = accountManager.getSenderCertificate();
-// TextSecurePreferences.setUnidentifiedAccessCertificate(context, certificate);
-// }
+ /*
+ synchronized (RotateCertificateJob.class) {
+ byte[] certificate = accountManager.getSenderCertificate();
+ TextSecurePreferences.setUnidentifiedAccessCertificate(context, certificate);
+ }
+ */
}
@Override
diff --git a/src/org/thoughtcrime/securesms/jobs/RotateSignedPreKeyJob.java b/src/org/thoughtcrime/securesms/jobs/RotateSignedPreKeyJob.java
index 363fb2c8b8..82b721c845 100644
--- a/src/org/thoughtcrime/securesms/jobs/RotateSignedPreKeyJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/RotateSignedPreKeyJob.java
@@ -30,7 +30,7 @@ public class RotateSignedPreKeyJob extends BaseJob implements InjectableType {
public RotateSignedPreKeyJob() {
this(new Job.Parameters.Builder()
.addConstraint(NetworkConstraint.KEY)
- .setMaxAttempts(5)
+ .setMaxAttempts(3)
.build());
}
@@ -55,6 +55,7 @@ public class RotateSignedPreKeyJob extends BaseJob implements InjectableType {
IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context);
SignedPreKeyRecord signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(context, identityKey, false);
+ // Loki - Don't upload the new signed pre key
// accountManager.setSignedPreKey(signedPreKeyRecord);
PreKeyUtil.setActiveSignedPreKeyId(context, signedPreKeyRecord.getId());
diff --git a/src/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java b/src/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java
index 1ffc2f7c80..b008edb95e 100644
--- a/src/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java
@@ -83,8 +83,7 @@ public class SendDeliveryReceiptJob extends BaseJob implements InjectableType {
Collections.singletonList(messageId),
timestamp);
- // TODO: Message ID
- messageSender.sendReceipt(0, remoteAddress,
+ messageSender.sendReceipt(remoteAddress,
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)),
receiptMessage);
}
diff --git a/src/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java b/src/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java
index 85bbe65d4c..da08f5aa1f 100644
--- a/src/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java
@@ -89,7 +89,7 @@ public class SendReadReceiptJob extends BaseJob implements InjectableType {
SignalServiceAddress remoteAddress = new SignalServiceAddress(address);
SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp);
- messageSender.sendReceipt(0, remoteAddress, // The message ID doesn't matter
+ messageSender.sendReceipt(remoteAddress,
UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)),
receiptMessage);
}
diff --git a/src/org/thoughtcrime/securesms/jobs/ServiceOutageDetectionJob.java b/src/org/thoughtcrime/securesms/jobs/ServiceOutageDetectionJob.java
index e5d9120040..1a83a580e3 100644
--- a/src/org/thoughtcrime/securesms/jobs/ServiceOutageDetectionJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/ServiceOutageDetectionJob.java
@@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.jobs;
import android.support.annotation.NonNull;
import org.greenrobot.eventbus.EventBus;
-import network.loki.messenger.BuildConfig;
import org.thoughtcrime.securesms.events.ReminderUpdateEvent;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
@@ -15,6 +14,8 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import network.loki.messenger.BuildConfig;
+
public class ServiceOutageDetectionJob extends BaseJob {
public static final String KEY = "ServiceOutageDetectionJob";
@@ -29,7 +30,7 @@ public class ServiceOutageDetectionJob extends BaseJob {
this(new Job.Parameters.Builder()
.setQueue("ServiceOutageDetectionJob")
.addConstraint(NetworkConstraint.KEY)
- .setMaxAttempts(5)
+ .setMaxAttempts(3)
.setMaxInstances(1)
.build());
}
diff --git a/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java b/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java
index 0f33579374..282099393b 100644
--- a/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/SmsSendJob.java
@@ -32,7 +32,7 @@ public class SmsSendJob extends SendJob {
public static final String KEY = "SmsSendJob";
private static final String TAG = SmsSendJob.class.getSimpleName();
- private static final int MAX_ATTEMPTS = 15;
+ private static final int MAX_ATTEMPTS = 25;
private static final String KEY_MESSAGE_ID = "message_id";
private static final String KEY_RUN_ATTEMPT = "run_attempt";
diff --git a/src/org/thoughtcrime/securesms/jobs/TypingSendJob.java b/src/org/thoughtcrime/securesms/jobs/TypingSendJob.java
index c6ecc30e2f..51015297f7 100644
--- a/src/org/thoughtcrime/securesms/jobs/TypingSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/TypingSendJob.java
@@ -10,7 +10,6 @@ import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.logging.Log;
-import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
@@ -20,8 +19,6 @@ import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage.Action;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
-import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
-import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
import java.util.Collections;
import java.util.List;
@@ -99,17 +96,7 @@ public class TypingSendJob extends BaseJob implements InjectableType {
List> unidentifiedAccess = Stream.of(recipients).map(r -> UnidentifiedAccessUtil.getAccessFor(context, r)).toList();
SignalServiceTypingMessage typingMessage = new SignalServiceTypingMessage(typing ? Action.STARTED : Action.STOPPED, System.currentTimeMillis(), groupId);
- // Loki - Don't send typing indicators in group chats or to ourselves
- if (recipient.isGroupRecipient()) { return; }
-
-
- LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId);
- if (friendRequestStatus != LokiThreadFriendRequestStatus.FRIENDS) { return; }
-
- boolean isOurDevice = PromiseUtil.get(MultiDeviceUtilities.isOneOfOurDevices(context, recipient.getAddress()), false);
- if (!isOurDevice) {
- messageSender.sendTyping(0, addresses, unidentifiedAccess, typingMessage);
- }
+ messageSender.sendTyping(addresses, unidentifiedAccess, typingMessage);
}
@Override
diff --git a/src/org/thoughtcrime/securesms/jobs/UpdateApkJob.java b/src/org/thoughtcrime/securesms/jobs/UpdateApkJob.java
index f67381d560..4a0359c967 100644
--- a/src/org/thoughtcrime/securesms/jobs/UpdateApkJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/UpdateApkJob.java
@@ -11,14 +11,12 @@ import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
import org.thoughtcrime.securesms.logging.Log;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import network.loki.messenger.BuildConfig;
import org.thoughtcrime.securesms.service.UpdateApkReadyListener;
import org.thoughtcrime.securesms.util.FileUtils;
import org.thoughtcrime.securesms.util.Hex;
@@ -29,6 +27,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
+import network.loki.messenger.BuildConfig;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
@@ -43,7 +42,7 @@ public class UpdateApkJob extends BaseJob {
this(new Job.Parameters.Builder()
.setQueue("UpdateApkJob")
.addConstraint(NetworkConstraint.KEY)
- .setMaxAttempts(2)
+ .setMaxAttempts(3)
.build());
}
diff --git a/src/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java b/src/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java
index 6d6d0f4ba7..1e9fff7f65 100644
--- a/src/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java
+++ b/src/org/thoughtcrime/securesms/linkpreview/LinkPreviewRepository.java
@@ -8,10 +8,8 @@ import android.text.Html;
import android.text.TextUtils;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
-import com.bumptech.glide.load.resource.gif.GifDrawable;
import com.bumptech.glide.request.FutureTarget;
-import com.bumptech.glide.util.ByteBufferUtil;
import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.attachments.UriAttachment;
@@ -73,7 +71,7 @@ public class LinkPreviewRepository implements InjectableType {
ApplicationContext.getInstance(context).injectDependencies(this);
}
- public RequestController getLinkPreview(@NonNull Context context, @NonNull String url, @NonNull Callback> callback) {
+ RequestController getLinkPreview(@NonNull Context context, @NonNull String url, @NonNull Callback> callback) {
CompositeRequestController compositeController = new CompositeRequestController();
if (!LinkPreviewUtil.isWhitelistedLinkUrl(url)) {
@@ -152,28 +150,6 @@ public class LinkPreviewRepository implements InjectableType {
return new CallRequestController(call);
}
- public @NonNull RequestController fetchGIF(@NonNull Context context, @NonNull String url, @NonNull Callback> callback) {
- FutureTarget future = GlideApp.with(context).asGif().load(new ChunkedImageUrl(url)).skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.NONE)
- .centerInside().submit(1024, 1024);
- RequestController controller = () -> future.cancel(false);
- SignalExecutors.UNBOUNDED.execute(() -> {
- try {
- GifDrawable gif = future.get();
- byte[] bytes = ByteBufferUtil.toBytes(gif.getBuffer());
- Uri uri = BlobProvider.getInstance().forData(bytes).createForSingleSessionInMemory();
- Optional thumbnail = Optional.of(new UriAttachment(uri, uri, MediaUtil.IMAGE_GIF, AttachmentDatabase.TRANSFER_PROGRESS_DONE,
- bytes.length, gif.getIntrinsicWidth(), gif.getIntrinsicHeight(), null, null, false, false, null, null));
- callback.onComplete(thumbnail);
- } catch (CancellationException | ExecutionException | InterruptedException e) {
- controller.cancel();
- callback.onComplete(Optional.absent());
- } finally {
- future.cancel(false);
- }
- });
- return () -> future.cancel(true);
- }
-
private @NonNull RequestController fetchThumbnail(@NonNull Context context, @NonNull String imageUrl, @NonNull Callback> callback) {
FutureTarget bitmapFuture = GlideApp.with(context).asBitmap()
.load(new ChunkedImageUrl(imageUrl))
@@ -317,7 +293,7 @@ public class LinkPreviewRepository implements InjectableType {
}
}
- public interface Callback {
+ interface Callback {
void onComplete(@NonNull T result);
}
}
diff --git a/src/org/thoughtcrime/securesms/loki/FriendRequestHandler.kt b/src/org/thoughtcrime/securesms/loki/FriendRequestHandler.kt
deleted file mode 100644
index e59a2875a9..0000000000
--- a/src/org/thoughtcrime/securesms/loki/FriendRequestHandler.kt
+++ /dev/null
@@ -1,108 +0,0 @@
-package org.thoughtcrime.securesms.loki
-
-import android.content.Context
-import nl.komponents.kovenant.ui.successUi
-import org.thoughtcrime.securesms.database.DatabaseFactory
-import org.thoughtcrime.securesms.util.TextSecurePreferences
-import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus
-import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
-
-object FriendRequestHandler {
- enum class ActionType { Sending, Sent, Failed }
-
- @JvmStatic
- fun updateFriendRequestState(context: Context, type: ActionType, messageId: Long, threadId: Long) {
- if (threadId < 0) return
- val recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId) ?: return
- if (!recipient.address.isPhone) { return }
-
- val currentFriendStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId)
- // Update thread status if we haven't sent a friend request before
- if (currentFriendStatus != LokiThreadFriendRequestStatus.REQUEST_RECEIVED &&
- currentFriendStatus != LokiThreadFriendRequestStatus.REQUEST_SENT &&
- currentFriendStatus != LokiThreadFriendRequestStatus.FRIENDS
- ) {
- val threadFriendStatus = when (type) {
- ActionType.Sending -> LokiThreadFriendRequestStatus.REQUEST_SENDING
- ActionType.Failed -> LokiThreadFriendRequestStatus.NONE
- ActionType.Sent -> LokiThreadFriendRequestStatus.REQUEST_SENT
- }
- val database = DatabaseFactory.getLokiThreadDatabase(context)
- database.setFriendRequestStatus(threadId, threadFriendStatus)
- // If we sent a friend request then we need to hide the session restore prompt
- if (type == ActionType.Sent) {
- database.removeAllSessionRestoreDevices(threadId)
- }
- }
-
- // Update message status
- if (messageId >= 0) {
- val messageDatabase = DatabaseFactory.getLokiMessageDatabase(context)
- val friendRequestStatus = messageDatabase.getFriendRequestStatus(messageId)
- if (type == ActionType.Sending) {
- // We only want to update message status if we aren't friends with another of their devices
- // This avoids spam in the ui where it would keep telling the user that they sent a friend request on every single message
- isFriendsWithAnyLinkedDevice(context, recipient).successUi { isFriends ->
- if (!isFriends && friendRequestStatus == LokiMessageFriendRequestStatus.NONE) {
- messageDatabase.setFriendRequestStatus(messageId, LokiMessageFriendRequestStatus.REQUEST_SENDING)
- }
- }
- } else if (friendRequestStatus != LokiMessageFriendRequestStatus.NONE) {
- // Update the friend request status of the message if we have it
- val messageFriendRequestStatus = when (type) {
- ActionType.Failed -> LokiMessageFriendRequestStatus.REQUEST_FAILED
- ActionType.Sent -> LokiMessageFriendRequestStatus.REQUEST_PENDING
- else -> throw IllegalStateException()
- }
- messageDatabase.setFriendRequestStatus(messageId, messageFriendRequestStatus)
- }
- }
- }
-
- @JvmStatic
- fun updateLastFriendRequestMessage(context: Context, threadId: Long, status: LokiMessageFriendRequestStatus) {
- if (threadId < 0) { return }
- val recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId) ?: return
- if (!recipient.address.isPhone || recipient.address.serialize() == TextSecurePreferences.getLocalNumber(context)) { return }
-
- val messages = DatabaseFactory.getSmsDatabase(context).getAllMessageIDs(threadId)
- val lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context)
- val lastMessage = messages.find {
- val friendRequestStatus = lokiMessageDatabase.getFriendRequestStatus(it)
- friendRequestStatus == LokiMessageFriendRequestStatus.REQUEST_PENDING
- } ?: return
-
- DatabaseFactory.getLokiMessageDatabase(context).setFriendRequestStatus(lastMessage, status)
- }
-
- @JvmStatic
- fun receivedIncomingFriendRequestMessage(context: Context, threadId: Long) {
- val smsMessageDatabase = DatabaseFactory.getSmsDatabase(context)
-
- // We only want to update the last message status if we're not friends with any of their linked devices
- // This ensures that we don't spam the UI with accept/decline messages
- val recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId) ?: return
- if (!recipient.address.isPhone || recipient.address.serialize() == TextSecurePreferences.getLocalNumber(context)) { return }
-
- isFriendsWithAnyLinkedDevice(context, recipient).successUi { isFriends ->
- if (isFriends) { return@successUi }
-
- // Since messages are forwarded to the primary device thread, we need to update it there
- val messageCount = smsMessageDatabase.getMessageCountForThread(threadId)
- val messageID = smsMessageDatabase.getIDForMessageAtIndex(threadId, messageCount - 1) // The message that was just received
- if (messageID < 0) { return@successUi }
-
- val messageDatabase = DatabaseFactory.getLokiMessageDatabase(context)
-
- // We need to go through and set all messages which are REQUEST_PENDING to NONE
- smsMessageDatabase.getAllMessageIDs(threadId)
- .filter { messageDatabase.getFriendRequestStatus(it) == LokiMessageFriendRequestStatus.REQUEST_PENDING }
- .forEach {
- messageDatabase.setFriendRequestStatus(it, LokiMessageFriendRequestStatus.NONE)
- }
-
- // Set the last message to pending
- messageDatabase.setFriendRequestStatus(messageID, LokiMessageFriendRequestStatus.REQUEST_PENDING)
- }
- }
-}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/GeneralUtilities.kt b/src/org/thoughtcrime/securesms/loki/GeneralUtilities.kt
deleted file mode 100644
index 856b038b9a..0000000000
--- a/src/org/thoughtcrime/securesms/loki/GeneralUtilities.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.thoughtcrime.securesms.loki
-
-import android.content.Context
-import android.content.res.Resources
-import android.os.Build
-import android.support.annotation.ColorRes
-import org.thoughtcrime.securesms.database.Address
-import org.thoughtcrime.securesms.database.DatabaseFactory
-import org.thoughtcrime.securesms.recipients.Recipient
-import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
-import kotlin.math.roundToInt
-
-fun Resources.getColorWithID(@ColorRes id: Int, theme: Resources.Theme?): Int {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- getColor(id, theme)
- } else {
- @Suppress("DEPRECATION") getColor(id)
- }
-}
-
-fun toPx(dp: Int, resources: Resources): Int {
- val scale = resources.displayMetrics.density
- return (dp * scale).roundToInt()
-}
-
-fun isPublicChat(context: Context, recipient: String): Boolean {
- return DatabaseFactory.getLokiThreadDatabase(context).getAllPublicChats().values.map { it.server }.contains(recipient)
-}
-
-fun getFriendPublicKeys(context: Context, devicePublicKeys: Set): Set {
- val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
- return devicePublicKeys.mapNotNull { device ->
- val address = Address.fromSerialized(device)
- val recipient = Recipient.from(context, address, false)
- val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient)
- if (threadID < 0) { return@mapNotNull null }
- val friendRequestStatus = lokiThreadDatabase.getFriendRequestStatus(threadID)
- if (friendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS) device else null
- }.toSet()
-}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/LokiSessionResetImplementation.kt b/src/org/thoughtcrime/securesms/loki/LokiSessionResetImplementation.kt
deleted file mode 100644
index f2f92edfd7..0000000000
--- a/src/org/thoughtcrime/securesms/loki/LokiSessionResetImplementation.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package org.thoughtcrime.securesms.loki
-
-import android.content.Context
-import org.thoughtcrime.securesms.database.DatabaseFactory
-import org.thoughtcrime.securesms.sms.MessageSender
-import org.whispersystems.libsignal.loki.LokiSessionResetProtocol
-import org.whispersystems.libsignal.loki.LokiSessionResetStatus
-import org.whispersystems.libsignal.protocol.PreKeySignalMessage
-
-class LokiSessionResetImplementation(private val context: Context) : LokiSessionResetProtocol {
-
- override fun getSessionResetStatus(hexEncodedPublicKey: String): LokiSessionResetStatus {
- return DatabaseFactory.getLokiThreadDatabase(context).getSessionResetStatus(hexEncodedPublicKey)
- }
-
- override fun setSessionResetStatus(hexEncodedPublicKey: String, sessionResetStatus: LokiSessionResetStatus) {
- return DatabaseFactory.getLokiThreadDatabase(context).setSessionResetStatus(hexEncodedPublicKey, sessionResetStatus)
- }
-
- override fun onNewSessionAdopted(hexEncodedPublicKey: String, oldSessionResetStatus: LokiSessionResetStatus) {
- if (oldSessionResetStatus == LokiSessionResetStatus.IN_PROGRESS) {
- // Send a message back to the contact to finalise session reset
- MessageSender.sendBackgroundMessage(context, hexEncodedPublicKey)
- }
-
- // TODO: Show session reset succeed message
- }
-
- override fun validatePreKeySignalMessage(sender: String, message: PreKeySignalMessage) {
- val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getPreKeyRecord(sender)
- check(preKeyRecord != null) { "Received a background message from a user without an associated pre key record." }
- check(preKeyRecord.id == (message.preKeyId ?: -1)) { "Received a background message from an unknown source." }
- }
-}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/MultiDeviceOpenGroupUpdateJob.kt b/src/org/thoughtcrime/securesms/loki/MultiDeviceOpenGroupUpdateJob.kt
deleted file mode 100644
index 7ae1a94f1e..0000000000
--- a/src/org/thoughtcrime/securesms/loki/MultiDeviceOpenGroupUpdateJob.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-package org.thoughtcrime.securesms.loki
-
-import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
-import org.thoughtcrime.securesms.database.DatabaseFactory
-import org.thoughtcrime.securesms.dependencies.InjectableType
-import org.thoughtcrime.securesms.groups.GroupManager
-import org.thoughtcrime.securesms.jobmanager.Data
-import org.thoughtcrime.securesms.jobmanager.Job
-import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
-import org.thoughtcrime.securesms.jobs.BaseJob
-import org.thoughtcrime.securesms.logging.Log
-import org.thoughtcrime.securesms.util.TextSecurePreferences
-import org.whispersystems.signalservice.api.SignalServiceMessageSender
-import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage
-import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat
-import java.util.concurrent.TimeUnit
-import javax.inject.Inject
-
-class MultiDeviceOpenGroupUpdateJob private constructor(parameters: Parameters) : BaseJob(parameters), InjectableType {
-
- companion object {
- const val KEY = "MultiDeviceOpenGroupUpdateJob"
- }
-
- @Inject
- lateinit var messageSender: SignalServiceMessageSender
-
- constructor() : this(Parameters.Builder()
- .addConstraint(NetworkConstraint.KEY)
- .setQueue("MultiDeviceOpenGroupUpdateJob")
- .setLifespan(TimeUnit.DAYS.toMillis(1))
- .setMaxAttempts(Parameters.UNLIMITED)
- .build())
-
- override fun getFactoryKey(): String { return KEY }
-
- override fun serialize(): Data { return Data.EMPTY }
-
- @Throws(Exception::class)
- public override fun onRun() {
- if (!TextSecurePreferences.isMultiDevice(context)) {
- Log.d("Loki", "Not multi device; aborting...")
- return
- }
-
- val openGroups = mutableListOf()
- DatabaseFactory.getGroupDatabase(context).groups.use { reader ->
- while (true) {
- val record = reader.next ?: return@use
- if (!record.isPublicChat) { continue; }
-
- val threadID = GroupManager.getThreadIdFromGroupId(record.encodedId, context)
- val openGroup = DatabaseFactory.getLokiThreadDatabase(context).getPublicChat(threadID)
- if (openGroup != null) {
- openGroups.add(openGroup)
- }
- }
- }
-
- if (openGroups.size > 0) {
- messageSender.sendMessage(0, SignalServiceSyncMessage.forOpenGroups(openGroups),
- UnidentifiedAccessUtil.getAccessForSync(context))
- } else {
- Log.d("Loki", "No open groups to sync.")
- }
- }
-
- public override fun onShouldRetry(exception: Exception): Boolean {
- return false
- }
-
- override fun onCanceled() { }
-
- class Factory : Job.Factory {
-
- override fun create(parameters: Parameters, data: Data): MultiDeviceOpenGroupUpdateJob {
- return MultiDeviceOpenGroupUpdateJob(parameters)
- }
- }
-}
diff --git a/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt b/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt
deleted file mode 100644
index 9411932aee..0000000000
--- a/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt
+++ /dev/null
@@ -1,192 +0,0 @@
-@file:JvmName("MultiDeviceUtilities")
-package org.thoughtcrime.securesms.loki
-
-import android.content.Context
-import nl.komponents.kovenant.Promise
-import nl.komponents.kovenant.functional.bind
-import nl.komponents.kovenant.functional.map
-import nl.komponents.kovenant.toFailVoid
-import nl.komponents.kovenant.ui.successUi
-import org.thoughtcrime.securesms.ApplicationContext
-import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
-import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
-import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
-import org.thoughtcrime.securesms.database.Address
-import org.thoughtcrime.securesms.database.DatabaseFactory
-import org.thoughtcrime.securesms.logging.Log
-import org.thoughtcrime.securesms.recipients.Recipient
-import org.thoughtcrime.securesms.util.TextSecurePreferences
-import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
-import org.whispersystems.signalservice.api.push.SignalServiceAddress
-import org.whispersystems.signalservice.loki.api.multidevice.DeviceLink
-import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities
-import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI
-import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
-import org.whispersystems.signalservice.loki.utilities.recover
-import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
-
-fun checkIsRevokedSlaveDevice(context: Context) {
- val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context) ?: return
- val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
- LokiFileServerAPI.shared.getDeviceLinks(masterHexEncodedPublicKey, true).bind { deviceLinks ->
- val deviceLink = deviceLinks.find { it.masterHexEncodedPublicKey == masterHexEncodedPublicKey && it.slaveHexEncodedPublicKey == hexEncodedPublicKey }
- if (deviceLink != null) throw Error("Device hasn't been revoked.")
- DatabaseFactory.getLokiAPIDatabase(context).clearDeviceLinks(hexEncodedPublicKey)
- LokiFileServerAPI.shared.setDeviceLinks(setOf())
- }.successUi {
- TextSecurePreferences.setNeedsIsRevokedSlaveDeviceCheck(context, false)
- ApplicationContext.getInstance(context).clearData()
- }.fail { error ->
- TextSecurePreferences.setNeedsIsRevokedSlaveDeviceCheck(context, true)
- Log.d("Loki", "Revocation check failed due to error: ${error.message ?: error}.")
- }
-}
-
-fun updateDeviceLinksOnServer(context: Context) {
- val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
- val deviceLinks = DatabaseFactory.getLokiAPIDatabase(context).getDeviceLinks(hexEncodedPublicKey)
- LokiFileServerAPI.shared.setDeviceLinks(deviceLinks)
-}
-
-fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: String): Promise