diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 928dfd626..0466db7e7 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -23,9 +23,13 @@ Describe here the issue that you are experiencing.
- list the steps
- that reproduce the bug
-**Actual result:** Describe here what happens after you run the steps above (i.e. the buggy behaviour)
+**Actual result:**
-**Expected result:** Describe here what should happen after you run the steps above (i.e. what would be the correct behaviour)
+Describe here what happens after you run the steps above (i.e. the buggy behaviour)
+
+**Expected result:**
+
+Describe here what should happen after you run the steps above (i.e. what would be the correct behaviour)
### Screenshots
@@ -33,10 +37,11 @@ Describe here the issue that you are experiencing.
### Device info
+
**Device:** Manufacturer Model XVI
**Android version:** 0.0.0
**Session version:** 0.0.0
-
+### Link to debug log
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index b7c55daee..efa0e2bcb 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -23,7 +23,7 @@ If applicable, add screenshots or logs to help explain your problem.
- Device: [e.g. Samsung Galaxy S8]
- OS: [e.g. Android Pie]
- - Version of Loki Messenger or latest commit hash
+ - Version of Session or latest commit hash
**Additional context**
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index bfa2c456d..df2bebd0b 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -47,6 +47,7 @@
+
@@ -112,6 +113,9 @@
android:name="org.thoughtcrime.securesms.loki.redesign.activities.DisplayNameActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" />
+
+
+
+
+
+
diff --git a/BUILDING.md b/BUILDING.md
new file mode 100644
index 000000000..a9c3f96dc
--- /dev/null
+++ b/BUILDING.md
@@ -0,0 +1,66 @@
+Building Session
+===============
+
+Basics
+------
+
+Session uses [Gradle](http://gradle.org) to build the project and to maintain
+dependencies. However, you needn't install it yourself; the
+"gradle wrapper" `gradlew`, mentioned below, will do that for you.
+
+Building Session
+---------------
+
+The following steps should help you (re)build Session from the command line.
+
+1. Checkout the session-android project source with the command:
+
+ git clone https://github.com/loki-project/session-android.git
+
+2. Make sure you have the [Android SDK](https://developer.android.com/sdk/index.html) installed.
+3. Ensure that the following packages are installed from the Android SDK manager:
+ * Android SDK Build Tools (see buildToolsVersion in build.gradle)
+ * SDK Platform (All API levels)
+ * Android Support Repository
+ * Google Repository
+4. Create a local.properties file at the root of your source checkout and add an sdk.dir entry to it. For example:
+
+ sdk.dir=/Application/android-sdk-macosx
+
+5. Using Java 8
+
+6. Execute Gradle:
+
+ ./gradlew build
+
+Visual assets
+----------------------
+
+Source assets tend to be large binary blobs, which are best stored outside of git repositories. Some source files are SVGs that can be auto-colored and sized using a tool like [android-res-utils](https://github.com/sebkur/android-res-utils).
+
+Sample command for generating our audio placeholder image:
+
+```bash
+pngs_from_svg.py ic_audio.svg /path/to/Session/res/ 150 --color #000 --opacity 0.54 --suffix _light
+pngs_from_svg.py ic_audio.svg /path/to/Session/res/ 150 --color #fff --opacity 1.00 --suffix _light
+```
+
+Setting up a development environment
+------------------------------------
+
+[Android Studio](https://developer.android.com/sdk/installing/studio.html) is the recommended development environment.
+
+1. Install Android Studio.
+2. Open Android Studio. On a new installation, the Quickstart panel will appear. If you have open projects, close them using "File > Close Project" to see the Quickstart panel.
+3. From the Quickstart panel, choose "Configure" then "SDK Manager".
+4. In the SDK Tools tab of the SDK Manager, make sure that the "Android Support Repository" is installed, and that the latest "Android SDK build-tools" are installed. Click "OK" to return to the Quickstart panel.
+5. From the Quickstart panel, choose "Checkout from Version Control" then "git".
+6. Paste the URL for the session-android project when prompted (https://github.com/loki-project/session-android.git).
+7. Android studio should detect the presence of a project file and ask you whether to open it. Click "yes".
+9. Default config options should be good enough.
+9. Project initialisation and build should proceed.
+
+Contributing code
+-----------------
+
+Code contributions should be sent via github as pull requests, from feature branches [as explained here](https://help.github.com/articles/using-pull-requests).
diff --git a/README.md b/README.md
index 2fac36fe4..d1578d60d 100644
--- a/README.md
+++ b/README.md
@@ -6,13 +6,13 @@
## Summary
-Session integrates directly with [Loki Service Nodes](https://lokidocs.com/ServiceNodes/SNOverview/), which are a set of distributed, decentralized and Sybil resistant nodes. Service Nodes act as servers which store messages offline, and a set of nodes which allow for onion routing functionality obfuscating users IP addresses. For a full understanding of how Session works, read the [Session Whitepaper](https://getsession.org/whitepaper).
+Session integrates directly with [Loki Service Nodes](https://lokidocs.com/ServiceNodes/SNOverview/), which are a set of distributed, decentralized and Sybil resistant nodes. Service Nodes act as servers which store messages offline, and a set of nodes which allow for onion routing functionality obfuscating users' IP addresses. For a full understanding of how Session works, read the [Session Whitepaper](https://getsession.org/whitepaper).
![AndroidSession](https://i.imgur.com/0YC9TyI.png)
## Want to Contribute? Found a Bug or Have a feature request?
-Please search for any [existing issues](https://github.com/loki-project/session-android/issues) that describe your bugs in order to avoid duplicate submissions. Submissions can be made by making a pull request to our development branch. If you don't know where to start contributing , try reading the Github issues page for ideas.
+Please search for any [existing issues](https://github.com/loki-project/session-android/issues) that describe your bugs in order to avoid duplicate submissions. Submissions can be made by making a pull request to our development branch. If you don't know where to start contributing, try reading the Github issues page for ideas.
## Build instruction
diff --git a/build.gradle b/build.gradle
index 41e2ac5d3..392ad467f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,6 +6,8 @@ buildscript {
ext.kovenant_version = "3.3.0"
ext.identicon_version = "v11"
ext.rss_parser_version = "2.0.4"
+ ext.google_services_version = "4.3.3"
+ ext.firebase_messaging_version = "18.0.0"
repositories {
mavenLocal()
@@ -16,6 +18,7 @@ buildscript {
classpath "com.android.tools.build:gradle:$gradle_version"
classpath files('libs/gradle-witness.jar')
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "com.google.gms:google-services:$google_services_version"
}
}
@@ -24,6 +27,7 @@ apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'
apply plugin: 'witness'
apply plugin: 'kotlin-kapt'
+apply plugin: 'com.google.gms.google-services'
repositories {
mavenLocal()
@@ -87,6 +91,8 @@ dependencies {
implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation 'android.arch.lifecycle:common-java8:1.1.1'
+ implementation "com.google.firebase:firebase-messaging:$firebase_messaging_version"
+
implementation 'com.google.android.exoplayer:exoplayer-core:2.9.1'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.1'
@@ -184,8 +190,8 @@ dependencies {
implementation "com.github.ybq:Android-SpinKit:1.4.0"
}
-def canonicalVersionCode = 48
-def canonicalVersionName = "1.0.11"
+def canonicalVersionCode = 49
+def canonicalVersionName = "1.1.0"
def postFixSize = 10
def abiPostFix = ['armeabi-v7a' : 1,
diff --git a/res/drawable/pn_option_background.xml b/res/drawable/pn_option_background.xml
new file mode 100644
index 000000000..b43e8de34
--- /dev/null
+++ b/res/drawable/pn_option_background.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/pn_option_background_deselect_transition.xml b/res/drawable/pn_option_background_deselect_transition.xml
new file mode 100644
index 000000000..7fcb8e116
--- /dev/null
+++ b/res/drawable/pn_option_background_deselect_transition.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/pn_option_background_select_transition.xml b/res/drawable/pn_option_background_select_transition.xml
new file mode 100644
index 000000000..21c58cf71
--- /dev/null
+++ b/res/drawable/pn_option_background_select_transition.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/res/drawable/pn_option_background_selected.xml b/res/drawable/pn_option_background_selected.xml
new file mode 100644
index 000000000..56d1ed983
--- /dev/null
+++ b/res/drawable/pn_option_background_selected.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout-sw400dp/activity_pn_mode.xml b/res/layout-sw400dp/activity_pn_mode.xml
new file mode 100644
index 000000000..8edc9267d
--- /dev/null
+++ b/res/layout-sw400dp/activity_pn_mode.xml
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout-sw400dp/fragment_enter_chat_url.xml b/res/layout-sw400dp/fragment_enter_chat_url.xml
index e895f9963..0fcac5085 100644
--- a/res/layout-sw400dp/fragment_enter_chat_url.xml
+++ b/res/layout-sw400dp/fragment_enter_chat_url.xml
@@ -41,6 +41,6 @@
android:textColor="@color/text"
android:alpha="0.6"
android:textAlignment="center"
- android:text="Open groups can be joined by anyone and do not provide full metadata protection" />
+ android:text="Open groups can be joined by anyone and do not provide full privacy protection" />
\ No newline at end of file
diff --git a/res/layout-sw400dp/fragment_pn_mode_bottom_sheet.xml b/res/layout-sw400dp/fragment_pn_mode_bottom_sheet.xml
new file mode 100644
index 000000000..0b01b12fd
--- /dev/null
+++ b/res/layout-sw400dp/fragment_pn_mode_bottom_sheet.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/activity_create_closed_group.xml b/res/layout/activity_create_closed_group.xml
index 67d64f369..55ccb9817 100644
--- a/res/layout/activity_create_closed_group.xml
+++ b/res/layout/activity_create_closed_group.xml
@@ -32,7 +32,7 @@
android:textColor="@color/text"
android:alpha="0.6"
android:textAlignment="center"
- android:text="Closed groups are end-to-end encrypted group chats for up to 10 members. They provide the same privacy protections as one-on-one sessions." />
+ android:text="Closed groups support up to 10 members and provide the same privacy protections as one-on-one sessions." />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/fragment_enter_chat_url.xml b/res/layout/fragment_enter_chat_url.xml
index e84eb4354..79748f4ce 100644
--- a/res/layout/fragment_enter_chat_url.xml
+++ b/res/layout/fragment_enter_chat_url.xml
@@ -41,6 +41,6 @@
android:textColor="@color/text"
android:alpha="0.6"
android:textAlignment="center"
- android:text="Open groups can be joined by anyone and do not provide full metadata protection" />
+ android:text="Open groups can be joined by anyone and do not provide full privacy protection" />
\ No newline at end of file
diff --git a/res/layout/fragment_open_group_suggestion_bottom_sheet.xml b/res/layout/fragment_open_group_suggestion_bottom_sheet.xml
index b26d9f86d..d586f461c 100644
--- a/res/layout/fragment_open_group_suggestion_bottom_sheet.xml
+++ b/res/layout/fragment_open_group_suggestion_bottom_sheet.xml
@@ -58,6 +58,6 @@
android:textColor="@color/text"
android:alpha="0.6"
android:textAlignment="center"
- android:text="Open groups can be joined by anyone and do not provide full metadata protection" />
+ android:text="Open groups can be joined by anyone and do not provide full privacy protection" />
diff --git a/res/layout/fragment_pn_mode_bottom_sheet.xml b/res/layout/fragment_pn_mode_bottom_sheet.xml
new file mode 100644
index 000000000..48fc5f3a3
--- /dev/null
+++ b/res/layout/fragment_pn_mode_bottom_sheet.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 958e2a8bd..374020fd9 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -26,6 +26,8 @@
#3F4146
#99FFFFFF
#1F1F1F
+ #1B1B1B
+ #212121
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3ed8a9a18..16ed23c8f 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -32,6 +32,7 @@
56dp
8dp
4dp
+ 8dp
8dp
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c32f24172..8745cd043 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1670,5 +1670,26 @@
Are you sure you want to leave this group?
Are you sure you want to delete this conversation?
Conversation deleted
+ Push Notifications
+ There are two ways Session can handle push notifications. Make sure to read the descriptions carefully before you choose.
+ Firebase Cloud Messaging
+ Session will use the Firebase Cloud Messaging service to receive push notifications. You’ll be notified of new messages reliably and immediately. Using FCM means that this device will communicate directly with Google’s servers to retrieve push notifications, which will expose your IP address to Google. Your messages will still be onion-routed and end-to-end encrypted, so the contents of your messages will remain completely private.
+ Background Polling
+ Session will occasionally check for new messages in the background. This guarantees full privacy protection, but message notifications may be significantly delayed.
+ Recommended
+ Please Pick an Option
+ Push Notifications
+ Session now features two ways to handle push notifications. Make sure to read the descriptions carefully before you choose.
+ Firebase Cloud Messaging
+ Session will use the Firebase Cloud Messaging service to receive push notifications. You’ll be notified of new messages reliably and immediately. Using FCM means that this device will communicate directly with Google’s servers to retrieve push notifications, which will expose your IP address to Google. Your messages will still be onion-routed and end-to-end encrypted, so the contents of your messages will remain completely private.
+ Background Polling
+ Session will occasionally check for new messages in the background. This guarantees full privacy protection, but message notifications may be significantly delayed.
+ Recommended
+ Please Pick an Option
+ Confirm
+ Skip
+ Notification Strategy
+ Use FCM
+ Using Firebase Cloud Messaging allows for more reliable push notifications, but exposes your IP to Google.
diff --git a/res/xml/network_security_configuration.xml b/res/xml/network_security_configuration.xml
index c62b3d962..b38993a46 100644
--- a/res/xml/network_security_configuration.xml
+++ b/res/xml/network_security_configuration.xml
@@ -1,11 +1,10 @@
- imaginary.stream
- chat.getsession.org
+ 149.56.148.124
storage.seed1.loki.network
storage.seed2.loki.network
- public.loki.foundation:22023
+ public.loki.foundation
file-dev.lokinet.org
127.0.0.1
diff --git a/res/xml/preferences_notifications.xml b/res/xml/preferences_notifications.xml
index 12dafb675..9a9c91817 100644
--- a/res/xml/preferences_notifications.xml
+++ b/res/xml/preferences_notifications.xml
@@ -21,6 +21,19 @@
+
+
+
+
+
+
+
+
{
+ if (!task.isSuccessful()) {
+ Log.w(TAG, "getInstanceId failed", task.getException());
+ return;
+ }
+ String token = task.getResult().getToken();
+ String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
+ if (userHexEncodedPublicKey == null) return;
+ if (TextSecurePreferences.isUsingFCM(this)) {
+ LokiPushNotificationManager.register(token, userHexEncodedPublicKey, context, force);
+ } else {
+ LokiPushNotificationManager.unregister(token, context);
+ }
+ });
+ }
+
@Override
public void ping(@NotNull String s) {
// TODO: Implement
@@ -464,7 +491,9 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
if (userHexEncodedPublicKey == null) return;
LokiAPIDatabase lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(this);
Context context = this;
- lokiPoller = new LokiPoller(userHexEncodedPublicKey, lokiAPIDatabase, broadcaster, protos -> {
+ LokiSwarmAPI.Companion.configureIfNeeded(lokiAPIDatabase);
+ LokiAPI.Companion.configureIfNeeded(userHexEncodedPublicKey, lokiAPIDatabase, broadcaster);
+ lokiPoller = new LokiPoller(userHexEncodedPublicKey, lokiAPIDatabase, protos -> {
for (SignalServiceProtos.Envelope proto : protos) {
new PushContentReceiveJob(context).processEnvelope(new SignalServiceEnvelope(proto));
}
diff --git a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java
index 532530841..86c436f34 100644
--- a/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java
+++ b/src/org/thoughtcrime/securesms/ApplicationPreferencesActivity.java
@@ -52,7 +52,7 @@ import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec;
-import org.whispersystems.signalservice.loki.utilities.SerializationKt;
+import org.whispersystems.signalservice.loki.utilities.HexEncodingKt;
import java.io.File;
@@ -341,7 +341,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
try {
String hexEncodedSeed = IdentityKeyUtil.retrieve(getContext(), IdentityKeyUtil.lokiSeedKey);
if (hexEncodedSeed == null) {
- hexEncodedSeed = SerializationKt.getHexEncodedPrivateKey(IdentityKeyUtil.getIdentityKeyPair(getContext())); // Legacy account
+ hexEncodedSeed = HexEncodingKt.getHexEncodedPrivateKey(IdentityKeyUtil.getIdentityKeyPair(getContext())); // Legacy account
}
String seed = new MnemonicCodec(languageFileDirectory).encode(hexEncodedSeed, MnemonicCodec.Language.Configuration.Companion.getEnglish());
new AlertDialog.Builder(getContext())
diff --git a/src/org/thoughtcrime/securesms/CreateProfileActivity.java b/src/org/thoughtcrime/securesms/CreateProfileActivity.java
index f819a8ffb..d0db3ae37 100644
--- a/src/org/thoughtcrime/securesms/CreateProfileActivity.java
+++ b/src/org/thoughtcrime/securesms/CreateProfileActivity.java
@@ -58,8 +58,8 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.crypto.ProfileCipher;
import org.whispersystems.signalservice.api.util.StreamDetails;
import org.whispersystems.signalservice.loki.api.LokiDotNetAPI;
-import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
-import org.whispersystems.signalservice.loki.api.LokiPublicChatAPI;
+import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
+import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChatAPI;
import java.io.ByteArrayInputStream;
import java.io.File;
diff --git a/src/org/thoughtcrime/securesms/components/QuoteView.java b/src/org/thoughtcrime/securesms/components/QuoteView.java
index b4943f999..690ce585c 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.LokiPublicChat;
+import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat;
import java.util.List;
diff --git a/src/org/thoughtcrime/securesms/components/TypingStatusSender.java b/src/org/thoughtcrime/securesms/components/TypingStatusSender.java
index 1c851247e..b573020b2 100644
--- a/src/org/thoughtcrime/securesms/components/TypingStatusSender.java
+++ b/src/org/thoughtcrime/securesms/components/TypingStatusSender.java
@@ -11,8 +11,8 @@ import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.jobs.TypingSendJob;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Util;
-import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
-import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
+import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities;
+import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
import java.util.HashMap;
import java.util.Map;
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java
index 763496b9c..a7ce551c8 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java
@@ -228,10 +228,10 @@ 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.DeviceLink;
+import org.whispersystems.signalservice.loki.api.multidevice.DeviceLink;
import org.whispersystems.signalservice.loki.api.LokiAPI;
-import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
-import org.whispersystems.signalservice.loki.api.LokiPublicChat;
+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;
@@ -317,7 +317,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private Button makeDefaultSmsButton;
private Button registerButton;
private InputAwareLayout container;
- private View composePanel;
protected Stub reminderView;
private Stub unverifiedBannerView;
private Stub groupShareProfileView;
@@ -552,7 +551,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
updateTitleTextView(recipient);
updateSubtitleTextView();
setActionBarColor(recipient.getColor());
- setBlockedUserState(recipient, isSecureText, isDefaultSms);
+ updateInputUI(recipient, isSecureText, isDefaultSms);
setGroupShareProfileReminder(recipient);
calculateCharactersRemaining();
@@ -645,7 +644,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
updateTitleTextView(recipient);
updateSubtitleTextView();
NotificationChannels.updateContactChannelName(this, recipient);
- setBlockedUserState(recipient, isSecureText, isDefaultSms);
+ updateInputUI(recipient, isSecureText, isDefaultSms);
supportInvalidateOptionsMenu();
break;
case TAKE_PHOTO:
@@ -858,6 +857,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
searchViewModel.onSearchClosed();
searchNav.setVisibility(View.GONE);
inputPanel.setVisibility(View.VISIBLE);
+ updateInputUI(recipient, isSecureText, isDefaultSms);
fragment.onSearchQueryUpdated(null);
invalidateOptionsMenu();
return true;
@@ -1343,7 +1343,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
calculateCharactersRemaining();
supportInvalidateOptionsMenu();
- setBlockedUserState(recipient, isSecureText, isDefaultSms);
+ updateInputUI(recipient, isSecureText, isDefaultSms);
}
///// Initializers
@@ -1627,7 +1627,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
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);
- composePanel = ViewUtil.findById(this, R.id.bottom_panel);
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);
@@ -1835,7 +1834,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
updateTitleTextView(recipient);
updateSubtitleTextView();
// titleView.setVerified(identityRecords.isVerified());
- setBlockedUserState(recipient, isSecureText, isDefaultSms);
+ updateInputUI(recipient, isSecureText, isDefaultSms);
setActionBarColor(recipient.getColor());
setGroupShareProfileReminder(recipient);
updateReminders(recipient.hasSeenInviteReminder());
@@ -2043,29 +2042,30 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
setStatusBarColor(getResources().getColor(R.color.action_bar_background));
}
- private void setBlockedUserState(Recipient recipient, boolean isSecureText, boolean isDefaultSms) {
- if (recipient.isGroupRecipient() && recipient.getAddress().isRSSFeed()) {
+ // FIXME: This name is confusing because we also have updateInputPanel and setInputPanelEnabled
+ private void updateInputUI(Recipient recipient, boolean isSecureText, boolean isDefaultSms) {
+ if (recipient.isGroupRecipient() && !isActiveGroup()) {
unblockButton.setVisibility(View.GONE);
- composePanel.setVisibility(View.GONE);
+ inputPanel.setVisibility(View.GONE);
makeDefaultSmsButton.setVisibility(View.GONE);
registerButton.setVisibility(View.GONE);
} else if (recipient.isBlocked()) {
unblockButton.setVisibility(View.VISIBLE);
- composePanel.setVisibility(View.GONE);
+ inputPanel.setVisibility(View.GONE);
makeDefaultSmsButton.setVisibility(View.GONE);
registerButton.setVisibility(View.GONE);
} else if (!isSecureText && isPushGroupConversation()) {
unblockButton.setVisibility(View.GONE);
- composePanel.setVisibility(View.GONE);
+ inputPanel.setVisibility(View.GONE);
makeDefaultSmsButton.setVisibility(View.GONE);
registerButton.setVisibility(View.VISIBLE);
} else if (!isSecureText && !isDefaultSms) {
unblockButton.setVisibility(View.GONE);
- composePanel.setVisibility(View.GONE);
+ inputPanel.setVisibility(View.GONE);
makeDefaultSmsButton.setVisibility(View.VISIBLE);
registerButton.setVisibility(View.GONE);
} else {
- composePanel.setVisibility(View.VISIBLE);
+ inputPanel.setVisibility(View.VISIBLE);
unblockButton.setVisibility(View.GONE);
makeDefaultSmsButton.setVisibility(View.GONE);
registerButton.setVisibility(View.GONE);
@@ -2125,7 +2125,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private boolean isActiveGroup() {
- if (!isGroupConversation()) return false;
+ if (!isGroupConversation() || recipient.getAddress().isRSSFeed()) return false;
Optional record = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getAddress().toGroupString());
return record.isPresent() && record.get().isActive();
@@ -2314,7 +2314,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
String hint = enabled ? "Message" : "Pending session request";
inputPanel.setHint(hint);
inputPanel.setEnabled(enabled);
- if (enabled) {
+ if (enabled && inputPanel.getVisibility() == View.VISIBLE) {
inputPanel.composeText.requestFocus();
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(inputPanel.composeText, 0);
@@ -2939,6 +2939,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public void handleReplyMessage(MessageRecord messageRecord) {
+ if (recipient.isGroupRecipient() && !isActiveGroup()) { return; }
+
Recipient author;
if (messageRecord.isOutgoing()) {
@@ -3152,7 +3154,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
subtitleTextView.setVisibility(View.GONE);
}
} else if (PublicKeyValidation.isValid(recipient.getAddress().toString())) {
- subtitleTextView.setText(recipient.getAddress().toString());
+ String ourMasterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(this);
+ String hexEncodedPublicKey = (recipient.isLocalNumber() && ourMasterHexEncodedPublicKey != null) ? ourMasterHexEncodedPublicKey : recipient.getAddress().toPhoneString();
+ subtitleTextView.setText(hexEncodedPublicKey);
} else {
subtitleTextView.setVisibility(View.GONE);
}
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java
index cb3b53f00..5978be024 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationFragment.java
@@ -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.LokiPublicChat;
-import org.whispersystems.signalservice.loki.api.LokiPublicChatAPI;
+import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat;
+import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChatAPI;
import java.io.IOException;
import java.io.InputStream;
diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java
index 2fdb040c7..c4bc562d7 100644
--- a/src/org/thoughtcrime/securesms/conversation/ConversationItem.java
+++ b/src/org/thoughtcrime/securesms/conversation/ConversationItem.java
@@ -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.LokiPublicChat;
-import org.whispersystems.signalservice.loki.api.LokiPublicChatAPI;
+import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat;
+import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChatAPI;
import java.util.Collections;
import java.util.HashSet;
diff --git a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java
index 38ff02ee8..a593c5794 100644
--- a/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java
+++ b/src/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java
@@ -45,7 +45,7 @@ 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.LokiPublicChat;
+import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat;
import java.io.File;
diff --git a/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java b/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java
index 7a7a67d53..87df48399 100644
--- a/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java
+++ b/src/org/thoughtcrime/securesms/database/loaders/DeviceListLoader.java
@@ -11,7 +11,7 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities;
import org.thoughtcrime.securesms.util.AsyncLoader;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
-import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
+import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec;
import java.io.File;
diff --git a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java
index c9145e77e..3711ddb8b 100644
--- a/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java
+++ b/src/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java
@@ -36,7 +36,7 @@ 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.LokiDeviceLinkUtilities;
+import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
import java.util.Collections;
@@ -137,49 +137,57 @@ public class GroupMessageProcessor {
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
String id = GroupUtil.getEncodedId(group);
- // Only update group if admin sent the message
+ String ourHexEncodedPublicKey = getMasterHexEncodedPublicKey(context, TextSecurePreferences.getLocalNumber(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.");
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.");
+ database.setActive(id, false);
+ return null;
+ }
}
- Set recordMembers = new HashSet<>(groupRecord.getMembers());
- Set messageMembers = new HashSet<>();
+ Set currentMembers = new HashSet<>(groupRecord.getMembers());
+ Set newMembers = new HashSet<>();
for (String messageMember : group.getMembers().get()) {
- messageMembers.add(Address.fromExternal(context, messageMember));
+ newMembers.add(Address.fromExternal(context, messageMember));
}
- Set addedMembers = new HashSet<>(messageMembers);
- addedMembers.removeAll(recordMembers);
+ // Added members are the members who are present in newMembers but not in currentMembers
+ Set addedMembers = new HashSet<>(newMembers);
+ addedMembers.removeAll(currentMembers);
- Set missingMembers = new HashSet<>(recordMembers);
- missingMembers.removeAll(messageMembers);
+ // Kicked members are members who are present in currentMembers but not in newMembers
+ Set removedMembers = new HashSet<>(currentMembers);
+ removedMembers.removeAll(newMembers);
GroupContext.Builder builder = createGroupContext(group);
builder.setType(GroupContext.Type.UPDATE);
- if (addedMembers.size() > 0) {
- Set unionMembers = new HashSet<>(recordMembers);
- unionMembers.addAll(messageMembers);
- database.updateMembers(id, new LinkedList<>(unionMembers));
-
- builder.clearMembers();
-
- for (Address addedMember : addedMembers) {
- builder.addMembers(addedMember.serialize());
- }
- } else {
- builder.clearMembers();
+ // Update our group members if they're different
+ if (!currentMembers.equals(newMembers)) {
+ database.updateMembers(id, new LinkedList<>(newMembers));
}
- if (missingMembers.size() > 0) {
- for (Address removedMember : missingMembers) {
- builder.addMembers(removedMember.serialize());
- }
+ // 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
+ for (Address addedMember : addedMembers) {
+ builder.addNewMembers(addedMember.serialize());
+ }
+
+ for (Address removedMember : removedMembers) {
+ builder.addRemovedMembers(removedMember.serialize());
}
if (group.getName().isPresent() || group.getAvatar().isPresent()) {
@@ -191,10 +199,15 @@ public class GroupMessageProcessor {
builder.clearName();
}
- if (!groupRecord.isActive()) database.setActive(id, true);
+ // If we were removed then we need to disable the chat
+ if (removedMembers.contains(Address.fromSerialized(ourHexEncodedPublicKey))) {
+ database.setActive(id, false);
+ } else {
+ if (!groupRecord.isActive()) database.setActive(id, true);
- if (group.getMembers().isPresent()) {
- establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
+ if (group.getMembers().isPresent()) {
+ establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
+ }
}
return storeMessage(context, content, group, builder.build(), outgoing);
diff --git a/src/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java b/src/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java
index 3bb54e43d..16d1b09b8 100644
--- a/src/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/AttachmentUploadJob.java
@@ -27,7 +27,7 @@ import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
-import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
+import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
import java.io.IOException;
import java.io.InputStream;
diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
index 29d3a1217..f91b97f57 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java
@@ -137,12 +137,12 @@ 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.DeviceLink;
-import org.whispersystems.signalservice.loki.api.DeviceLinkingSession;
+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.LokiDeviceLinkUtilities;
-import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
-import org.whispersystems.signalservice.loki.api.LokiPublicChat;
+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;
@@ -1895,7 +1895,28 @@ 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;
- return (isContentMessage && !isGroupActive) || (sender.isBlocked() && !isLeaveMessage);
+ 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);
} else {
return sender.isBlocked();
}
diff --git a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java
index ef9fa207a..7d497dea6 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java
@@ -50,8 +50,8 @@ 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.LokiDeviceLinkUtilities;
-import org.whispersystems.signalservice.loki.api.LokiPublicChat;
+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;
diff --git a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java
index 1447b5baf..0ce752100 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java
@@ -45,7 +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.LokiDeviceLinkUtilities;
+import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
diff --git a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java
index 5ce7dd344..2b9e034c4 100644
--- a/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/PushTextSendJob.java
@@ -33,7 +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.LokiDeviceLinkUtilities;
+import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.messaging.LokiSyncMessage;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
diff --git a/src/org/thoughtcrime/securesms/loki/LokiPublicChatManager.kt b/src/org/thoughtcrime/securesms/loki/LokiPublicChatManager.kt
index 0a959521f..e49ff6709 100644
--- a/src/org/thoughtcrime/securesms/loki/LokiPublicChatManager.kt
+++ b/src/org/thoughtcrime/securesms/loki/LokiPublicChatManager.kt
@@ -13,7 +13,7 @@ import org.thoughtcrime.securesms.groups.GroupManager
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPublicChatPoller
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util
-import org.whispersystems.signalservice.loki.api.LokiPublicChat
+import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat
class LokiPublicChatManager(private val context: Context) {
private var chats = mutableMapOf()
diff --git a/src/org/thoughtcrime/securesms/loki/LokiPushNotificationManager.kt b/src/org/thoughtcrime/securesms/loki/LokiPushNotificationManager.kt
new file mode 100644
index 000000000..6358fdcb4
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/LokiPushNotificationManager.kt
@@ -0,0 +1,82 @@
+package org.thoughtcrime.securesms.loki
+
+import android.content.Context
+import okhttp3.*
+import org.thoughtcrime.securesms.util.TextSecurePreferences
+import org.whispersystems.libsignal.logging.Log
+import org.whispersystems.signalservice.internal.util.JsonUtil
+import org.whispersystems.signalservice.loki.api.LokiPushNotificationAcknowledgement
+import java.io.IOException
+
+object LokiPushNotificationManager {
+ private val connection = OkHttpClient()
+
+ private val server by lazy {
+ LokiPushNotificationAcknowledgement.shared.server
+ }
+
+ private const val tokenExpirationInterval = 2 * 24 * 60 * 60 * 1000
+
+ @JvmStatic
+ fun unregister(token: String, context: Context?) {
+ val parameters = mapOf( "token" to token )
+ val url = "${server}/register"
+ val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
+ val request = Request.Builder().url(url).post(body).build()
+ connection.newCall(request).enqueue(object : Callback {
+
+ override fun onResponse(call: Call, response: Response) {
+ when (response.code()) {
+ 200 -> {
+ val bodyAsString = response.body()!!.string()
+ val json = JsonUtil.fromJson(bodyAsString, Map::class.java)
+ val code = json?.get("code") as? Int
+ if (code != null && code != 0) {
+ TextSecurePreferences.setIsUsingFCM(context, false)
+ } else {
+ Log.d("Loki", "Couldn't disable FCM due to error: ${json?.get("message") as? String ?: "null"}.")
+ }
+ }
+ }
+ }
+
+ override fun onFailure(call: Call, exception: IOException) {
+ Log.d("Loki", "Couldn't disable FCM.")
+ }
+ })
+ }
+
+ @JvmStatic
+ fun register(token: String, hexEncodedPublicKey: String, context: Context?, force: Boolean) {
+ val oldToken = TextSecurePreferences.getFCMToken(context)
+ val lastUploadDate = TextSecurePreferences.getLastFCMUploadTime(context)
+ if (!force && token == oldToken && System.currentTimeMillis() - lastUploadDate < tokenExpirationInterval) { return }
+ val parameters = mapOf( "token" to token, "pubKey" to hexEncodedPublicKey )
+ val url = "${server}/register"
+ val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters))
+ val request = Request.Builder().url(url).post(body).build()
+ connection.newCall(request).enqueue(object : Callback {
+
+ override fun onResponse(call: Call, response: Response) {
+ when (response.code()) {
+ 200 -> {
+ val bodyAsString = response.body()!!.string()
+ val json = JsonUtil.fromJson(bodyAsString, Map::class.java)
+ val code = json?.get("code") as? Int
+ if (code != null && code != 0) {
+ TextSecurePreferences.setIsUsingFCM(context, true)
+ TextSecurePreferences.setFCMToken(context, token)
+ TextSecurePreferences.setLastFCMUploadTime(context, System.currentTimeMillis())
+ } else {
+ Log.d("Loki", "Couldn't register for FCM due to error: ${json?.get("message") as? String ?: "null"}.")
+ }
+ }
+ }
+ }
+
+ override fun onFailure(call: Call, exception: IOException) {
+ Log.d("Loki", "Couldn't register for FCM.")
+ }
+ })
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/loki/LokiThreadDatabase.kt b/src/org/thoughtcrime/securesms/loki/LokiThreadDatabase.kt
index 0decbdd0c..adb3a6e16 100644
--- a/src/org/thoughtcrime/securesms/loki/LokiThreadDatabase.kt
+++ b/src/org/thoughtcrime/securesms/loki/LokiThreadDatabase.kt
@@ -12,7 +12,7 @@ import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.libsignal.loki.LokiSessionResetStatus
import org.whispersystems.signalservice.internal.util.JsonUtil
-import org.whispersystems.signalservice.loki.api.LokiPublicChat
+import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat
import org.whispersystems.signalservice.loki.messaging.LokiThreadDatabaseProtocol
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation
diff --git a/src/org/thoughtcrime/securesms/loki/MultiDeviceOpenGroupUpdateJob.kt b/src/org/thoughtcrime/securesms/loki/MultiDeviceOpenGroupUpdateJob.kt
index 683eea84b..7ae1a94f1 100644
--- a/src/org/thoughtcrime/securesms/loki/MultiDeviceOpenGroupUpdateJob.kt
+++ b/src/org/thoughtcrime/securesms/loki/MultiDeviceOpenGroupUpdateJob.kt
@@ -12,7 +12,7 @@ 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.LokiPublicChat
+import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat
import java.util.concurrent.TimeUnit
import javax.inject.Inject
diff --git a/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt b/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt
index 8cbf54690..9411932ae 100644
--- a/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt
+++ b/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt
@@ -18,9 +18,9 @@ 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.DeviceLink
-import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities
-import org.whispersystems.signalservice.loki.api.LokiFileServerAPI
+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
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/DisplayNameActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/DisplayNameActivity.kt
index b6eaf2444..1a6040398 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/activities/DisplayNameActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/DisplayNameActivity.kt
@@ -2,15 +2,16 @@ package org.thoughtcrime.securesms.loki.redesign.activities
import android.content.Intent
import android.os.Bundle
+import android.view.KeyEvent
+import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
+import android.widget.TextView.OnEditorActionListener
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_display_name.*
import network.loki.messenger.R
-import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.BaseActionBarActivity
-import org.thoughtcrime.securesms.database.DatabaseFactory
+import org.thoughtcrime.securesms.loki.redesign.utilities.push
import org.thoughtcrime.securesms.loki.redesign.utilities.setUpActionBarSessionLogo
-import org.thoughtcrime.securesms.loki.redesign.utilities.show
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.crypto.ProfileCipher
@@ -21,6 +22,19 @@ class DisplayNameActivity : BaseActionBarActivity() {
setUpActionBarSessionLogo()
setContentView(R.layout.activity_display_name)
displayNameEditText.imeOptions = displayNameEditText.imeOptions or 16777216 // Always use incognito keyboard
+ displayNameEditText.setOnEditorActionListener(
+ OnEditorActionListener { _, actionId, event ->
+ // Handle validation from keyboard to trigger registration
+ if (actionId == EditorInfo.IME_ACTION_SEARCH ||
+ actionId == EditorInfo.IME_ACTION_DONE ||
+ (event.action === KeyEvent.ACTION_DOWN
+ && event.keyCode === KeyEvent.KEYCODE_ENTER)) {
+ this.register();
+ return@OnEditorActionListener true
+ }
+ // Return true if you have consumed the action, else false.
+ false
+ })
registerButton.setOnClickListener { register() }
}
@@ -38,20 +52,7 @@ class DisplayNameActivity : BaseActionBarActivity() {
val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(displayNameEditText.windowToken, 0)
TextSecurePreferences.setProfileName(this, displayName)
- TextSecurePreferences.setHasSeenWelcomeScreen(this, true)
- TextSecurePreferences.setPromptedPushRegistration(this, true)
- val application = ApplicationContext.getInstance(this)
- application.setUpStorageAPIIfNeeded()
- application.setUpP2PAPI()
- val publicChatAPI = ApplicationContext.getInstance(this).lokiPublicChatAPI
- if (publicChatAPI != null) {
- // TODO: This won't be necessary anymore when we don't auto-join the Loki Public Chat anymore
- application.createDefaultPublicChatsIfNeeded()
- val servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers()
- servers.forEach { publicChatAPI.setDisplayName(displayName, it) }
- }
- val intent = Intent(this, HomeActivity::class.java)
- intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
- show(intent)
+ val intent = Intent(this, PNModeActivity::class.java)
+ push(intent)
}
}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeActivity.kt
index 7e99a11ac..15275280c 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/HomeActivity.kt
@@ -32,6 +32,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.model.ThreadRecord
import org.thoughtcrime.securesms.loki.getColorWithID
+import org.thoughtcrime.securesms.loki.redesign.dialogs.PNModeBottomSheet
import org.thoughtcrime.securesms.loki.redesign.utilities.push
import org.thoughtcrime.securesms.loki.redesign.utilities.show
import org.thoughtcrime.securesms.loki.redesign.views.ConversationView
@@ -156,32 +157,20 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
if (hasViewedSeed || !isMasterDevice) {
seedReminderView.visibility = View.GONE
}
-// if (!TextSecurePreferences.getHasSeenOpenGroupSuggestionSheet(this)) {
-// val bottomSheet = OpenGroupSuggestionBottomSheet()
-// bottomSheet.onJoinTapped = {
-// TextSecurePreferences.setHasSeenOpenGroupSuggestionSheet(this)
-// bottomSheet.dismiss()
-// // TODO: Duplication of the code in JoinPublicChatActivity
-// val application = ApplicationContext.getInstance(this)
-// val channel: Long = 1
-// val displayName = TextSecurePreferences.getProfileName(this)
-// val lokiPublicChatAPI = application.lokiPublicChatAPI!!
-// val url = "https://chat.getsession.org"
-// application.lokiPublicChatManager.addChat(url, channel).successUi {
-// lokiPublicChatAPI.getMessages(channel, url)
-// lokiPublicChatAPI.setDisplayName(displayName, url)
-// lokiPublicChatAPI.join(channel, url)
-// val profileKey: ByteArray = ProfileKeyUtil.getProfileKey(this)
-// val profileUrl: String? = TextSecurePreferences.getProfileAvatarUrl(this)
-// lokiPublicChatAPI.setProfilePicture(url, profileKey, profileUrl)
-// }
-// }
-// bottomSheet.onDismissTapped = {
-// TextSecurePreferences.setHasSeenOpenGroupSuggestionSheet(this)
-// bottomSheet.dismiss()
-// }
-// bottomSheet.show(supportFragmentManager, bottomSheet.tag)
-// }
+ if (!TextSecurePreferences.hasSeenPNModeSheet(this)) {
+ val bottomSheet = PNModeBottomSheet()
+ bottomSheet.onConfirmTapped = { isUsingFCM ->
+ TextSecurePreferences.setHasSeenPNModeSheet(this, true)
+ TextSecurePreferences.setIsUsingFCM(this, isUsingFCM)
+ ApplicationContext.getInstance(this).registerForFCMIfNeeded(true)
+ bottomSheet.dismiss()
+ }
+ bottomSheet.onSkipTapped = {
+ TextSecurePreferences.setHasSeenPNModeSheet(this, true)
+ bottomSheet.dismiss()
+ }
+ bottomSheet.show(supportFragmentManager, bottomSheet.tag)
+ }
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/LandingActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/LandingActivity.kt
index 874413163..e15be3140 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/activities/LandingActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/LandingActivity.kt
@@ -26,7 +26,7 @@ import org.whispersystems.curve25519.Curve25519
import org.whispersystems.libsignal.ecc.Curve
import org.whispersystems.libsignal.ecc.ECKeyPair
import org.whispersystems.libsignal.util.KeyHelper
-import org.whispersystems.signalservice.loki.api.DeviceLink
+import org.whispersystems.signalservice.loki.api.multidevice.DeviceLink
import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey
import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesActivity.kt
index 0e0969774..16ffae7b3 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesActivity.kt
@@ -23,9 +23,9 @@ import org.thoughtcrime.securesms.loki.redesign.dialogs.*
import org.thoughtcrime.securesms.loki.signAndSendDeviceLinkMessage
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.util.TextSecurePreferences
-import org.whispersystems.signalservice.loki.api.DeviceLink
+import org.whispersystems.signalservice.loki.api.multidevice.DeviceLink
import org.whispersystems.signalservice.loki.api.LokiAPI
-import org.whispersystems.signalservice.loki.api.LokiFileServerAPI
+import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI
import java.util.*
import kotlin.concurrent.schedule
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesLoader.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesLoader.kt
index 87dcdb528..d3db8f672 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesLoader.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/LinkedDevicesLoader.kt
@@ -6,7 +6,7 @@ import org.thoughtcrime.securesms.devicelist.Device
import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities
import org.thoughtcrime.securesms.util.AsyncLoader
import org.thoughtcrime.securesms.util.TextSecurePreferences
-import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities
+import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
import java.io.File
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/PNModeActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/PNModeActivity.kt
new file mode 100644
index 000000000..2aa0a403d
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/PNModeActivity.kt
@@ -0,0 +1,112 @@
+package org.thoughtcrime.securesms.loki.redesign.activities
+
+import android.app.AlertDialog
+import android.content.Intent
+import android.graphics.drawable.TransitionDrawable
+import android.os.Bundle
+import android.os.Handler
+import android.support.annotation.DrawableRes
+import android.view.View
+import android.widget.LinearLayout
+import android.widget.Toast
+import kotlinx.android.synthetic.main.activity_display_name.registerButton
+import kotlinx.android.synthetic.main.activity_home.*
+import kotlinx.android.synthetic.main.activity_pn_mode.*
+import network.loki.messenger.R
+import org.thoughtcrime.securesms.ApplicationContext
+import org.thoughtcrime.securesms.BaseActionBarActivity
+import org.thoughtcrime.securesms.database.DatabaseFactory
+import org.thoughtcrime.securesms.loki.redesign.utilities.setUpActionBarSessionLogo
+import org.thoughtcrime.securesms.loki.redesign.utilities.show
+import org.thoughtcrime.securesms.util.GroupUtil
+import org.thoughtcrime.securesms.util.TextSecurePreferences
+
+class PNModeActivity : BaseActionBarActivity() {
+ private var selectedOptionView: LinearLayout? = null
+
+ // region Lifecycle
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setUpActionBarSessionLogo()
+ setContentView(R.layout.activity_pn_mode)
+ fcmOptionView.setOnClickListener { toggleFCM() }
+ backgroundPollingOptionView.setOnClickListener { toggleBackgroundPolling() }
+ registerButton.setOnClickListener { register() }
+ }
+ // endregion
+
+ // region Animation
+ private fun performTransition(@DrawableRes transitionID: Int, subject: View) {
+ val drawable = resources.getDrawable(transitionID, theme) as TransitionDrawable
+ subject.background = drawable
+ drawable.startTransition(250)
+ }
+ // endregion
+
+ // region Interaction
+ private fun toggleFCM() {
+ when (selectedOptionView) {
+ null -> {
+ performTransition(R.drawable.pn_option_background_select_transition, fcmOptionView)
+ selectedOptionView = fcmOptionView
+ }
+ fcmOptionView -> {
+ performTransition(R.drawable.pn_option_background_deselect_transition, fcmOptionView)
+ selectedOptionView = null
+ }
+ backgroundPollingOptionView -> {
+ performTransition(R.drawable.pn_option_background_select_transition, fcmOptionView)
+ performTransition(R.drawable.pn_option_background_deselect_transition, backgroundPollingOptionView)
+ selectedOptionView = fcmOptionView
+ }
+ }
+ }
+
+ private fun toggleBackgroundPolling() {
+ when (selectedOptionView) {
+ null -> {
+ performTransition(R.drawable.pn_option_background_select_transition, backgroundPollingOptionView)
+ selectedOptionView = backgroundPollingOptionView
+ }
+ backgroundPollingOptionView -> {
+ performTransition(R.drawable.pn_option_background_deselect_transition, backgroundPollingOptionView)
+ selectedOptionView = null
+ }
+ fcmOptionView -> {
+ performTransition(R.drawable.pn_option_background_select_transition, backgroundPollingOptionView)
+ performTransition(R.drawable.pn_option_background_deselect_transition, fcmOptionView)
+ selectedOptionView = backgroundPollingOptionView
+ }
+ }
+ }
+
+ private fun register() {
+ if (selectedOptionView == null) {
+ val dialog = AlertDialog.Builder(this)
+ dialog.setTitle(R.string.activity_pn_mode_no_option_picked_dialog_title)
+ dialog.setPositiveButton(R.string.ok) { _, _ -> }
+ dialog.create().show()
+ return
+ }
+ val displayName = TextSecurePreferences.getProfileName(this)
+ TextSecurePreferences.setHasSeenWelcomeScreen(this, true)
+ TextSecurePreferences.setPromptedPushRegistration(this, true)
+ TextSecurePreferences.setIsUsingFCM(this, (selectedOptionView == fcmOptionView))
+ TextSecurePreferences.setHasSeenPNModeSheet(this, true) // Shouldn't be shown to users who've done the new onboarding
+ val application = ApplicationContext.getInstance(this)
+ application.setUpStorageAPIIfNeeded()
+ application.setUpP2PAPI()
+ val publicChatAPI = ApplicationContext.getInstance(this).lokiPublicChatAPI
+ if (publicChatAPI != null) {
+ // TODO: This won't be necessary anymore when we don't auto-join the Loki Public Chat anymore
+ application.createDefaultPublicChatsIfNeeded()
+ val servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers()
+ servers.forEach { publicChatAPI.setDisplayName(displayName, it) }
+ }
+ application.registerForFCMIfNeeded(true)
+ val intent = Intent(this, HomeActivity::class.java)
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ show(intent)
+ }
+ // endregion
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/SettingsActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/SettingsActivity.kt
index c82e4e9e3..bcf24e26b 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/activities/SettingsActivity.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/SettingsActivity.kt
@@ -42,7 +42,7 @@ import org.thoughtcrime.securesms.util.BitmapUtil
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.whispersystems.signalservice.api.crypto.ProfileCipher
import org.whispersystems.signalservice.api.util.StreamDetails
-import org.whispersystems.signalservice.loki.api.LokiFileServerAPI
+import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI
import java.io.ByteArrayInputStream
import java.io.File
import java.security.SecureRandom
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/dialogs/LinkDeviceMasterModeDialog.kt b/src/org/thoughtcrime/securesms/loki/redesign/dialogs/LinkDeviceMasterModeDialog.kt
index 44fc08bdd..35bab0ac3 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/dialogs/LinkDeviceMasterModeDialog.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/dialogs/LinkDeviceMasterModeDialog.kt
@@ -17,9 +17,9 @@ import org.thoughtcrime.securesms.loki.redesign.utilities.QRCodeUtilities
import org.thoughtcrime.securesms.loki.toPx
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util
-import org.whispersystems.signalservice.loki.api.DeviceLink
-import org.whispersystems.signalservice.loki.api.DeviceLinkingSession
-import org.whispersystems.signalservice.loki.api.DeviceLinkingSessionListener
+import org.whispersystems.signalservice.loki.api.multidevice.DeviceLink
+import org.whispersystems.signalservice.loki.api.multidevice.DeviceLinkingSession
+import org.whispersystems.signalservice.loki.api.multidevice.DeviceLinkingSessionListener
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
class LinkDeviceMasterModeDialog : DialogFragment(), DeviceLinkingSessionListener {
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/dialogs/LinkDeviceSlaveModeDialog.kt b/src/org/thoughtcrime/securesms/loki/redesign/dialogs/LinkDeviceSlaveModeDialog.kt
index 8a7d0acf8..d61800800 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/dialogs/LinkDeviceSlaveModeDialog.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/dialogs/LinkDeviceSlaveModeDialog.kt
@@ -15,9 +15,9 @@ import network.loki.messenger.R
import org.thoughtcrime.securesms.loki.redesign.utilities.MnemonicUtilities
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.Util
-import org.whispersystems.signalservice.loki.api.DeviceLink
-import org.whispersystems.signalservice.loki.api.DeviceLinkingSession
-import org.whispersystems.signalservice.loki.api.DeviceLinkingSessionListener
+import org.whispersystems.signalservice.loki.api.multidevice.DeviceLink
+import org.whispersystems.signalservice.loki.api.multidevice.DeviceLinkingSession
+import org.whispersystems.signalservice.loki.api.multidevice.DeviceLinkingSessionListener
import org.whispersystems.signalservice.loki.crypto.MnemonicCodec
class LinkDeviceSlaveModeDialog : DialogFragment(), DeviceLinkingSessionListener {
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/dialogs/OpenGroupSuggestionBottomSheet.kt b/src/org/thoughtcrime/securesms/loki/redesign/dialogs/OpenGroupSuggestionBottomSheet.kt
index 274b36af7..18802dee9 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/dialogs/OpenGroupSuggestionBottomSheet.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/dialogs/OpenGroupSuggestionBottomSheet.kt
@@ -8,7 +8,7 @@ import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_open_group_suggestion_bottom_sheet.*
import network.loki.messenger.R
-public class OpenGroupSuggestionBottomSheet : BottomSheetDialogFragment() {
+class OpenGroupSuggestionBottomSheet : BottomSheetDialogFragment() {
var onJoinTapped: (() -> Unit)? = null
var onDismissTapped: (() -> Unit)? = null
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/dialogs/PNModeBottomSheet.kt b/src/org/thoughtcrime/securesms/loki/redesign/dialogs/PNModeBottomSheet.kt
new file mode 100644
index 000000000..749d9d526
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/loki/redesign/dialogs/PNModeBottomSheet.kt
@@ -0,0 +1,100 @@
+package org.thoughtcrime.securesms.loki.redesign.dialogs
+
+import android.app.AlertDialog
+import android.content.DialogInterface
+import android.graphics.drawable.TransitionDrawable
+import android.os.Bundle
+import android.support.annotation.DrawableRes
+import android.support.design.widget.BottomSheetDialogFragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import kotlinx.android.synthetic.main.fragment_pn_mode_bottom_sheet.*
+import network.loki.messenger.R
+import org.thoughtcrime.securesms.util.TextSecurePreferences
+
+class PNModeBottomSheet : BottomSheetDialogFragment() {
+ private var selectedOptionView: LinearLayout? = null
+ var onConfirmTapped: ((Boolean) -> Unit)? = null
+ var onSkipTapped: (() -> Unit)? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setStyle(STYLE_NORMAL, R.style.SessionBottomSheetDialogTheme)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ return inflater.inflate(R.layout.fragment_pn_mode_bottom_sheet, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ fcmOptionView.setOnClickListener { toggleFCM() }
+ backgroundPollingOptionView.setOnClickListener { toggleBackgroundPolling() }
+ confirmButton.setOnClickListener { confirm() }
+ skipButton.setOnClickListener { onSkipTapped?.invoke() }
+ }
+
+ override fun onDismiss(dialog: DialogInterface?) {
+ TextSecurePreferences.setHasSeenPNModeSheet(context, true)
+ super.onDismiss(dialog)
+ }
+
+ // region Animation
+ private fun performTransition(@DrawableRes transitionID: Int, subject: View) {
+ val drawable = resources.getDrawable(transitionID, context!!.theme) as TransitionDrawable
+ subject.background = drawable
+ drawable.startTransition(250)
+ }
+ // endregion
+
+ // region Interaction
+ private fun toggleFCM() {
+ when (selectedOptionView) {
+ null -> {
+ performTransition(R.drawable.pn_option_background_select_transition, fcmOptionView)
+ selectedOptionView = fcmOptionView
+ }
+ fcmOptionView -> {
+ performTransition(R.drawable.pn_option_background_deselect_transition, fcmOptionView)
+ selectedOptionView = null
+ }
+ backgroundPollingOptionView -> {
+ performTransition(R.drawable.pn_option_background_select_transition, fcmOptionView)
+ performTransition(R.drawable.pn_option_background_deselect_transition, backgroundPollingOptionView)
+ selectedOptionView = fcmOptionView
+ }
+ }
+ }
+
+ private fun toggleBackgroundPolling() {
+ when (selectedOptionView) {
+ null -> {
+ performTransition(R.drawable.pn_option_background_select_transition, backgroundPollingOptionView)
+ selectedOptionView = backgroundPollingOptionView
+ }
+ backgroundPollingOptionView -> {
+ performTransition(R.drawable.pn_option_background_deselect_transition, backgroundPollingOptionView)
+ selectedOptionView = null
+ }
+ fcmOptionView -> {
+ performTransition(R.drawable.pn_option_background_select_transition, backgroundPollingOptionView)
+ performTransition(R.drawable.pn_option_background_deselect_transition, fcmOptionView)
+ selectedOptionView = backgroundPollingOptionView
+ }
+ }
+ }
+
+ private fun confirm() {
+ if (selectedOptionView == null) {
+ val dialog = AlertDialog.Builder(context)
+ dialog.setTitle(R.string.sheet_pn_mode_no_option_picked_dialog_title)
+ dialog.setPositiveButton(R.string.ok) { _, _ -> }
+ dialog.create().show()
+ return
+ }
+ onConfirmTapped?.invoke(selectedOptionView == fcmOptionView)
+ }
+ // endregion
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/messaging/BackgroundPollWorker.kt b/src/org/thoughtcrime/securesms/loki/redesign/messaging/BackgroundPollWorker.kt
index 4b8fdc903..46f0251af 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/messaging/BackgroundPollWorker.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/messaging/BackgroundPollWorker.kt
@@ -28,13 +28,15 @@ class BackgroundPollWorker : PersistentAlarmManagerListener() {
}
override fun onAlarm(context: Context, scheduledTime: Long): Long {
+ if (TextSecurePreferences.isUsingFCM(context)) { return 0L }
if (scheduledTime != 0L) {
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context)
try {
val applicationContext = context.applicationContext as ApplicationContext
val broadcaster = applicationContext.broadcaster
- LokiAPI(userHexEncodedPublicKey, lokiAPIDatabase, broadcaster).getMessages().map { messages ->
+ LokiAPI.configureIfNeeded(userHexEncodedPublicKey, lokiAPIDatabase, broadcaster)
+ LokiAPI.shared.getMessages().map { messages ->
messages.forEach {
PushContentReceiveJob(context).processEnvelope(SignalServiceEnvelope(it))
}
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/messaging/LokiAPIDatabase.kt b/src/org/thoughtcrime/securesms/loki/redesign/messaging/LokiAPIDatabase.kt
index fe70c7e93..23b401ff1 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/messaging/LokiAPIDatabase.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/messaging/LokiAPIDatabase.kt
@@ -7,9 +7,9 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
import org.thoughtcrime.securesms.loki.redesign.utilities.*
import org.thoughtcrime.securesms.util.Base64
import org.thoughtcrime.securesms.util.TextSecurePreferences
-import org.whispersystems.signalservice.loki.api.DeviceLink
import org.whispersystems.signalservice.loki.api.LokiAPIDatabaseProtocol
import org.whispersystems.signalservice.loki.api.LokiAPITarget
+import org.whispersystems.signalservice.loki.api.multidevice.DeviceLink
// TODO: Clean this up a bit
@@ -84,7 +84,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
var string = "${target.address}-${target.port}"
val keySet = target.publicKeySet
if (keySet != null) {
- string += "-${keySet.idKey}-${keySet.encryptionKey}"
+ string += "-${keySet.ed25519Key}-${keySet.x25519Key}"
}
string
}
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/messaging/LokiPublicChatPoller.kt b/src/org/thoughtcrime/securesms/loki/redesign/messaging/LokiPublicChatPoller.kt
index 51f2e59c5..49b64dd38 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/messaging/LokiPublicChatPoller.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/messaging/LokiPublicChatPoller.kt
@@ -21,7 +21,11 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
import org.whispersystems.signalservice.api.messages.SignalServiceGroup
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage
import org.whispersystems.signalservice.api.push.SignalServiceAddress
-import org.whispersystems.signalservice.loki.api.*
+import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI
+import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities
+import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat
+import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChatAPI
+import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChatMessage
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus
import org.whispersystems.signalservice.loki.utilities.successBackground
import java.security.MessageDigest
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/messaging/LokiRSSFeedPoller.kt b/src/org/thoughtcrime/securesms/loki/redesign/messaging/LokiRSSFeedPoller.kt
index c8ac9bb5f..27c08a958 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/messaging/LokiRSSFeedPoller.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/messaging/LokiRSSFeedPoller.kt
@@ -13,8 +13,8 @@ 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.push.SignalServiceAddress
-import org.whispersystems.signalservice.loki.api.LokiRSSFeed
-import org.whispersystems.signalservice.loki.api.LokiRSSFeedProxy
+import org.whispersystems.signalservice.loki.api.rssfeeds.LokiRSSFeed
+import org.whispersystems.signalservice.loki.api.rssfeeds.LokiRSSFeedProxy
import org.whispersystems.signalservice.loki.utilities.successBackground
import java.text.SimpleDateFormat
import java.util.regex.Pattern
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/utilities/OpenGroupUtilities.kt b/src/org/thoughtcrime/securesms/loki/redesign/utilities/OpenGroupUtilities.kt
index 87f7afdf6..837c9bb1b 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/utilities/OpenGroupUtilities.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/utilities/OpenGroupUtilities.kt
@@ -8,7 +8,7 @@ import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.groups.GroupManager
import org.thoughtcrime.securesms.util.TextSecurePreferences
-import org.whispersystems.signalservice.loki.api.LokiPublicChat
+import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat
object OpenGroupUtilities {
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/views/MentionCandidateView.kt b/src/org/thoughtcrime/securesms/loki/redesign/views/MentionCandidateView.kt
index 831590d61..0fcf57209 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/views/MentionCandidateView.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/views/MentionCandidateView.kt
@@ -9,7 +9,7 @@ import android.widget.LinearLayout
import kotlinx.android.synthetic.main.view_mention_candidate.view.*
import network.loki.messenger.R
import org.thoughtcrime.securesms.mms.GlideRequests
-import org.whispersystems.signalservice.loki.api.LokiPublicChatAPI
+import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChatAPI
import org.whispersystems.signalservice.loki.messaging.Mention
class MentionCandidateView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attrs, defStyleAttr) {
diff --git a/src/org/thoughtcrime/securesms/loki/redesign/views/ProfilePictureView.kt b/src/org/thoughtcrime/securesms/loki/redesign/views/ProfilePictureView.kt
index 55b89dd85..bc4336946 100644
--- a/src/org/thoughtcrime/securesms/loki/redesign/views/ProfilePictureView.kt
+++ b/src/org/thoughtcrime/securesms/loki/redesign/views/ProfilePictureView.kt
@@ -15,6 +15,7 @@ import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.loki.JazzIdenticonDrawable
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.recipients.Recipient
+import org.thoughtcrime.securesms.util.TextSecurePreferences
// TODO: Look into a better way of handling different sizes. Maybe an enum (with associated values) encapsulating the different modes?
@@ -60,12 +61,15 @@ class ProfilePictureView : RelativeLayout {
fun setProfilePictureIfNeeded(imageView: ImageView, hexEncodedPublicKey: String, @DimenRes sizeID: Int) {
glide.clear(imageView)
if (hexEncodedPublicKey.isNotEmpty()) {
- val signalProfilePicture = Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false).contactPhoto
+ val recipient = Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false);
+ val signalProfilePicture = recipient.contactPhoto
if (signalProfilePicture != null && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "0" && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "") {
glide.load(signalProfilePicture).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView)
} else {
val size = resources.getDimensionPixelSize(sizeID)
- val jazzIcon = JazzIdenticonDrawable(size, size, hexEncodedPublicKey)
+ val masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context)
+ val hepk = if (recipient.isLocalNumber && masterHexEncodedPublicKey != null) masterHexEncodedPublicKey else hexEncodedPublicKey
+ val jazzIcon = JazzIdenticonDrawable(size, size, hepk)
glide.load(jazzIcon).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView)
}
} else {
diff --git a/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java b/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java
index c976cabea..27f7ddd87 100644
--- a/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java
+++ b/src/org/thoughtcrime/securesms/mms/PushMediaConstraints.java
@@ -3,7 +3,7 @@ package org.thoughtcrime.securesms.mms;
import android.content.Context;
import org.thoughtcrime.securesms.util.Util;
-import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
+import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
public class PushMediaConstraints extends MediaConstraints {
diff --git a/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java b/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java
index c6e7f1b78..388746575 100644
--- a/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java
+++ b/src/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java
@@ -23,7 +23,7 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
import org.thoughtcrime.securesms.util.Util;
-import org.whispersystems.signalservice.loki.api.LokiFileServerAPI;
+import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
import java.util.LinkedList;
import java.util.List;
diff --git a/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java b/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java
index fd58d1d4b..0ec4c0efd 100644
--- a/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java
+++ b/src/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragment.java
@@ -14,6 +14,7 @@ import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.text.TextUtils;
+import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
@@ -32,6 +33,16 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
public void onCreate(Bundle paramBundle) {
super.onCreate(paramBundle);
+ // Set up FCM toggle
+ String fcmKey = "pref_key_use_fcm";
+ ((SwitchPreferenceCompat)findPreference(fcmKey)).setChecked(TextSecurePreferences.isUsingFCM(getContext()));
+ this.findPreference(fcmKey)
+ .setOnPreferenceChangeListener((preference, newValue) -> {
+ TextSecurePreferences.setIsUsingFCM(getContext(), (boolean) newValue);
+ ApplicationContext.getInstance(getContext()).registerForFCMIfNeeded(true);
+ return true;
+ });
+
Preference ledBlinkPref = this.findPreference(TextSecurePreferences.LED_BLINK_PREF);
if (NotificationChannels.supported()) {
diff --git a/src/org/thoughtcrime/securesms/service/PushNotificationService.kt b/src/org/thoughtcrime/securesms/service/PushNotificationService.kt
new file mode 100644
index 000000000..0fb0c1431
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/service/PushNotificationService.kt
@@ -0,0 +1,36 @@
+package org.thoughtcrime.securesms.service
+
+import com.google.firebase.messaging.FirebaseMessagingService
+import com.google.firebase.messaging.RemoteMessage
+import org.thoughtcrime.securesms.jobs.PushContentReceiveJob
+import org.thoughtcrime.securesms.loki.LokiPushNotificationManager
+import org.thoughtcrime.securesms.util.TextSecurePreferences
+import org.whispersystems.libsignal.logging.Log
+import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope
+import org.whispersystems.signalservice.internal.util.Base64
+import org.whispersystems.signalservice.loki.messaging.LokiMessageWrapper
+
+class PushNotificationService : FirebaseMessagingService() {
+
+ override fun onNewToken(token: String) {
+ super.onNewToken(token)
+ Log.d("Loki", "New FCM token: $token.")
+ val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this) ?: return
+ LokiPushNotificationManager.register(token, userHexEncodedPublicKey, this, false)
+ }
+
+ override fun onMessageReceived(message: RemoteMessage) {
+ val base64EncodedData = message.data["ENCRYPTED_DATA"]
+ val data = base64EncodedData?.let { Base64.decode(it) }
+ if (data != null) {
+ try {
+ val envelope = LokiMessageWrapper.unwrap(data)
+ PushContentReceiveJob(this).processEnvelope(SignalServiceEnvelope(envelope))
+ } catch (e: Exception) {
+ Log.d("Loki", "Failed to unwrap data for message.")
+ }
+ } else {
+ Log.d("Loki", "Failed to decode data for message.")
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/org/thoughtcrime/securesms/sms/MessageSender.java b/src/org/thoughtcrime/securesms/sms/MessageSender.java
index 438db5511..f11faf924 100644
--- a/src/org/thoughtcrime/securesms/sms/MessageSender.java
+++ b/src/org/thoughtcrime/securesms/sms/MessageSender.java
@@ -62,7 +62,7 @@ import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.push.ContactTokenDetails;
-import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities;
+import org.whispersystems.signalservice.loki.api.multidevice.LokiDeviceLinkUtilities;
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
diff --git a/src/org/thoughtcrime/securesms/util/GroupUtil.java b/src/org/thoughtcrime/securesms/util/GroupUtil.java
index 907a21217..0029d5f6a 100644
--- a/src/org/thoughtcrime/securesms/util/GroupUtil.java
+++ b/src/org/thoughtcrime/securesms/util/GroupUtil.java
@@ -10,7 +10,6 @@ import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.database.Address;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase;
-import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -159,7 +158,7 @@ public class GroupUtil {
@NonNull private final Context context;
@Nullable private final GroupContext groupContext;
- private final List members;
+ private final List newMembers;
private final List removedMembers;
private boolean ourDeviceWasRemoved;
@@ -167,35 +166,35 @@ public class GroupUtil {
this.context = context.getApplicationContext();
this.groupContext = groupContext;
- this.members = new LinkedList<>();
+ this.newMembers = new LinkedList<>();
this.removedMembers = new LinkedList<>();
this.ourDeviceWasRemoved = false;
- if (groupContext != null && !groupContext.getMembersList().isEmpty()) {
- List memberList = groupContext.getMembersList();
- List currentMembers = getCurrentGroupMembers();
+ if (groupContext != null) {
+ List newMembers = groupContext.getNewMembersList();
+ for (String member : newMembers) {
+ this.newMembers.add(this.toRecipient(member));
+ }
- // Add them to the member or removed members lists
- for (String member : memberList) {
- Address address = Address.fromSerialized(member);
- Recipient recipient = Recipient.from(context, address, true);
- if (currentMembers == null || currentMembers.contains(address)) {
- this.members.add(recipient);
- } else {
- this.removedMembers.add(recipient);
- }
+ List removedMembers = groupContext.getRemovedMembersList();
+ for (String member : removedMembers) {
+ this.removedMembers.add(this.toRecipient(member));
}
// Check if our device was removed
if (!removedMembers.isEmpty()) {
String masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
String hexEncodedPublicKey = masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : TextSecurePreferences.getLocalNumber(context);
- Recipient self = Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false);
- ourDeviceWasRemoved = removedMembers.contains(self);
+ ourDeviceWasRemoved = removedMembers.contains(hexEncodedPublicKey);
}
}
}
+ private Recipient toRecipient(String hexEncodedPublicKey) {
+ Address address = Address.fromSerialized(hexEncodedPublicKey);
+ return Recipient.from(context, address, false);
+ }
+
public String toString(Recipient sender) {
// Show the local removed message
if (ourDeviceWasRemoved) {
@@ -211,10 +210,10 @@ public class GroupUtil {
String title = groupContext.getName();
- if (!members.isEmpty()) {
+ if (!newMembers.isEmpty()) {
description.append("\n");
description.append(context.getResources().getQuantityString(R.plurals.GroupUtil_joined_the_group,
- members.size(), toString(members)));
+ newMembers.size(), toString(newMembers)));
}
if (!removedMembers.isEmpty()) {
@@ -224,8 +223,8 @@ public class GroupUtil {
}
if (title != null && !title.trim().isEmpty()) {
- if (!members.isEmpty()) description.append(" ");
- else description.append("\n");
+ String separator = (!newMembers.isEmpty() || !removedMembers.isEmpty()) ? " " : "\n";
+ description.append(separator);
description.append(context.getString(R.string.GroupUtil_group_name_is_now, title));
}
@@ -233,8 +232,8 @@ public class GroupUtil {
}
public void addListener(RecipientModifiedListener listener) {
- if (!this.members.isEmpty()) {
- for (Recipient member : this.members) {
+ if (!this.newMembers.isEmpty()) {
+ for (Recipient member : this.newMembers) {
member.addListener(listener);
}
}
@@ -252,23 +251,5 @@ public class GroupUtil {
return result;
}
-
- private List getCurrentGroupMembers() {
- if (groupContext == null) { return null; }
- GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
- byte[] decodedGroupId = groupContext.getId().toByteArray();
- String signalGroupId = getEncodedId(decodedGroupId, false);
- String publicChatId = getEncodedPublicChatId(decodedGroupId);
- String rssFeedId = getEncodedRSSFeedId(decodedGroupId);
- GroupRecord groupRecord = null;
- if (!groupDatabase.isUnknownGroup(signalGroupId)) {
- groupRecord = groupDatabase.getGroup(signalGroupId).orNull();
- } else if (!groupDatabase.isUnknownGroup(publicChatId)) {
- groupRecord = groupDatabase.getGroup(publicChatId).orNull();
- } else if (!groupDatabase.isUnknownGroup(rssFeedId)) {
- groupRecord = groupDatabase.getGroup(rssFeedId).orNull();
- }
- return (groupRecord != null) ? groupRecord.getMembers() : null;
- }
}
}
diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java
index 42c204dfb..d306ab259 100644
--- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java
+++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java
@@ -185,6 +185,45 @@ public class TextSecurePreferences {
private static final String MEDIA_KEYBOARD_MODE = "pref_media_keyboard_mode";
+ // region FCM
+ private static final String IS_USING_FCM = "pref_is_using_fcm";
+ private static final String FCM_TOKEN = "pref_fcm_token";
+ private static final String LAST_FCM_TOKEN_UPLOAD_TIME = "pref_last_fcm_token_upload_time";
+ private static final String HAS_SEEN_PN_MODE_SHEET = "pref_has_seen_pn_mode_sheet";
+
+ public static boolean isUsingFCM(Context context) {
+ return getBooleanPreference(context, IS_USING_FCM, false);
+ }
+
+ public static void setIsUsingFCM(Context context, boolean value) {
+ setBooleanPreference(context, IS_USING_FCM, value);
+ }
+
+ public static String getFCMToken(Context context) {
+ return getStringPreference(context, FCM_TOKEN, "");
+ }
+
+ public static void setFCMToken(Context context, String value) {
+ setStringPreference(context, FCM_TOKEN, value);
+ }
+
+ public static long getLastFCMUploadTime(Context context) {
+ return getLongPreference(context, LAST_FCM_TOKEN_UPLOAD_TIME, 0);
+ }
+
+ public static void setLastFCMUploadTime(Context context, long value) {
+ setLongPreference(context, LAST_FCM_TOKEN_UPLOAD_TIME, value);
+ }
+
+ public static boolean hasSeenPNModeSheet(Context context) {
+ return getBooleanPreference(context, HAS_SEEN_PN_MODE_SHEET, false);
+ }
+
+ public static void setHasSeenPNModeSheet(Context context, boolean value) {
+ setBooleanPreference(context, HAS_SEEN_PN_MODE_SHEET, value);
+ }
+ // endregion
+
public static boolean isScreenLockEnabled(@NonNull Context context) {
return getBooleanPreference(context, SCREEN_LOCK, false);
}