diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 928dfd6269..0466db7e71 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 b7c55daee6..efa0e2bcbb 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 bfa2c456dc..df2bebd0b5 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 0000000000..a9c3f96dc7
--- /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 2fac36fe4e..d1578d60de 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 41e2ac5d37..392ad467f4 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 0000000000..b43e8de341
--- /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 0000000000..7fcb8e1160
--- /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 0000000000..21c58cf71d
--- /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 0000000000..56d1ed983a
--- /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 0000000000..8edc9267df
--- /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 e895f99639..0fcac50855 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 0000000000..0b01b12fdc
--- /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 67d64f3697..55ccb98171 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 e84eb4354b..79748f4ce2 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 b26d9f86dc..d586f461c2 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 0000000000..48fc5f3a31
--- /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 958e2a8bd1..374020fd95 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 3ed8a9a187..16ed23c8fe 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 c32f241721..8745cd043b 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 c62b3d962e..b38993a466 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 12dafb675a..9a9c918179 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 5325308411..86c436f345 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 f819a8ffbd..d0db3ae370 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 b4943f9993..690ce585cb 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 1c851247ea..b573020b22 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 763496b9cd..a7ce551c87 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 cb3b53f007..5978be024e 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 2fdb040c7b..c4bc562d79 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 38ff02ee8a..a593c57943 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 7a7a67d535..87df483995 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 c9145e77e1..3711ddb8b0 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 3bb54e43d9..16d1b09b85 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 29d3a12172..f91b97f574 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 ef9fa207ac..7d497dea60 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 1447b5bafd..0ce7521000 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 5ce7dd3442..2b9e034c4f 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 0a959521f5..e49ff67090 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 0000000000..6358fdcb43
--- /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 0decbdd0c5..adb3a6e165 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 683eea84b6..7ae1a94f1e 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 8cbf546901..9411932aee 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 b6eaf24444..1a6040398c 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 7e99a11ac7..15275280ce 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 8744131638..e15be3140f 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 0e09697740..16ffae7b35 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 87dcdb528e..d3db8f6728 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 0000000000..2aa0a403dd
--- /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 c82e4e9e3d..bcf24e26ba 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 44fc08bddf..35bab0ac35 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 8a7d0acf85..d618008005 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 274b36af74..18802dee97 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 0000000000..749d9d5263
--- /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 4b8fdc9037..46f0251afd 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 fe70c7e934..23b401ff15 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 51f2e59c51..49b64dd383 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 c8ac9bb5fb..27c08a958b 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 87f7afdf6b..837c9bb1bd 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 831590d61c..0fcf572097 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 55b89dd85f..bc43369461 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 c976cabea1..27f7ddd87f 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 c6e7f1b784..388746575e 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 fd58d1d4b2..0ec4c0efdc 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 0000000000..0fb0c14319
--- /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 438db55116..f11faf924a 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 907a21217e..0029d5f6a4 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 42c204dfbb..d306ab259d 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);
}