mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-30 13:35:18 +00:00
Merge branch 'dev' into share
This commit is contained in:
commit
f164508673
10
.github/ISSUE_TEMPLATE.md
vendored
10
.github/ISSUE_TEMPLATE.md
vendored
@ -3,12 +3,9 @@ You can also preview your report before submitting it. You may remove sections t
|
|||||||
|
|
||||||
Before we begin, please note that this tracker is only for issues. It is not for questions, comments, or feature requests.
|
Before we begin, please note that this tracker is only for issues. It is not for questions, comments, or feature requests.
|
||||||
|
|
||||||
If you would like to discuss a new feature or submit suggestions, please visit the community forum:
|
|
||||||
https://community.signalusers.org
|
|
||||||
|
|
||||||
If you are looking for support, please visit our support center:
|
If you are looking for support, please file an issue or:
|
||||||
https://support.signal.org/
|
or email team@loki.network
|
||||||
or email support@signal.org
|
|
||||||
|
|
||||||
Let's begin with a checklist: Replace the empty checkboxes [ ] below with checked ones [x] accordingly. -->
|
Let's begin with a checklist: Replace the empty checkboxes [ ] below with checked ones [x] accordingly. -->
|
||||||
|
|
||||||
@ -48,6 +45,3 @@ Describe here what should happen after you run the steps above (i.e. what would
|
|||||||
**Session version:** 0.0.0
|
**Session version:** 0.0.0
|
||||||
|
|
||||||
### Link to debug log
|
### Link to debug log
|
||||||
|
|
||||||
<!-- immediately after the bug has happened capture a debug log via Signal's advanced settings and paste the link below -->
|
|
||||||
|
|
||||||
|
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,12 +1,8 @@
|
|||||||
<!-- You can remove this first section if you have contributed before -->
|
<!-- You can remove this first section if you have contributed before -->
|
||||||
### First time contributor checklist
|
### First time contributor checklist
|
||||||
<!-- replace the empty checkboxes [ ] below with checked ones [x] accordingly -->
|
<!-- replace the empty checkboxes [ ] below with checked ones [x] accordingly -->
|
||||||
- [ ] I have read [how to contribute](https://github.com/signalapp/Signal-Android/blob/master/CONTRIBUTING.md) to this project
|
|
||||||
- [ ] I have signed the [Contributor License Agreement](https://whispersystems.org/cla/)
|
|
||||||
|
|
||||||
### Contributor checklist
|
### Contributor checklist
|
||||||
<!-- replace the empty checkboxes [ ] below with checked ones [x] accordingly -->
|
<!-- replace the empty checkboxes [ ] below with checked ones [x] accordingly -->
|
||||||
- [ ] I am following the [Code Style Guidelines](https://github.com/signalapp/Signal-Android/wiki/Code-Style-Guidelines)
|
|
||||||
- [ ] I have tested my contribution on these devices:
|
- [ ] I have tested my contribution on these devices:
|
||||||
* Device A, Android X.Y.Z
|
* Device A, Android X.Y.Z
|
||||||
* Device B, Android Z.Y
|
* Device B, Android Z.Y
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -26,3 +26,4 @@ jni/libspeex/.deps/
|
|||||||
pkcs11.password
|
pkcs11.password
|
||||||
fabric.properties
|
fabric.properties
|
||||||
play
|
play
|
||||||
|
google-services.json
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
|
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
@ -112,6 +113,9 @@
|
|||||||
android:name="org.thoughtcrime.securesms.loki.redesign.activities.DisplayNameActivity"
|
android:name="org.thoughtcrime.securesms.loki.redesign.activities.DisplayNameActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
android:windowSoftInputMode="adjustResize" />
|
android:windowSoftInputMode="adjustResize" />
|
||||||
|
<activity
|
||||||
|
android:name="org.thoughtcrime.securesms.loki.redesign.activities.PNModeActivity"
|
||||||
|
android:screenOrientation="portrait" />
|
||||||
<activity
|
<activity
|
||||||
android:name="org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity"
|
android:name="org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
@ -509,6 +513,14 @@
|
|||||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/TextSecure.LightNoActionBar" />
|
android:theme="@style/TextSecure.LightNoActionBar" />
|
||||||
|
<service
|
||||||
|
android:name="org.thoughtcrime.securesms.service.PushNotificationService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
<service
|
<service
|
||||||
android:name="org.thoughtcrime.securesms.service.WebRtcCallService"
|
android:name="org.thoughtcrime.securesms.service.WebRtcCallService"
|
||||||
android:enabled="true" />
|
android:enabled="true" />
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
# Contributing to Signal Android
|
|
||||||
|
|
||||||
Thank you for supporting Signal and looking for ways to help. Please note that some conventions here might be a bit different than what you are used to, even if you have contributed to other open source projects before. Reading this document will help you save time and work effectively with the developers and other contributors.
|
|
||||||
|
|
||||||
|
|
||||||
## Development Ideology
|
|
||||||
|
|
||||||
Truths which we believe to be self-evident:
|
|
||||||
|
|
||||||
1. **The answer is not more options.** If you feel compelled to add a preference that's exposed to the user, it's very possible you've made a wrong turn somewhere.
|
|
||||||
1. **The user doesn't know what a key is.** We need to minimize the points at which a user is exposed to this sort of terminology as extremely as possible.
|
|
||||||
1. **There are no power users.** The idea that some users "understand" concepts better than others has proven to be, for the most part, false. If anything, "power users" are more dangerous than the rest, and we should avoid exposing dangerous functionality to them.
|
|
||||||
1. **If it's "like PGP," it's wrong.** PGP is our guide for what not to do.
|
|
||||||
1. **It's an asynchronous world.** Be wary of anything that is anti-asynchronous: ACKs, protocol confirmations, or any protocol-level "advisory" message.
|
|
||||||
1. **There is no such thing as time.** Protocol ideas that require synchronized clocks are doomed to failure.
|
|
||||||
|
|
||||||
|
|
||||||
## Translations
|
|
||||||
|
|
||||||
Thanks to a dedicated community of volunteer translators, Signal is now available in more than one hundred languages. We use Transifex to manage our translation efforts, not GitHub. Any suggestions, corrections, or new translations should be submitted to the [Signal localization project for Android](https://www.transifex.com/signalapp/signal-android/).
|
|
||||||
|
|
||||||
|
|
||||||
## Issues
|
|
||||||
|
|
||||||
### Useful bug reports
|
|
||||||
1. Please search both open and closed issues to make sure your bug report is not a duplicate.
|
|
||||||
1. Read the [guide to submitting useful bug reports](https://github.com/signalapp/Signal-Android/wiki/Submitting-useful-bug-reports) before posting a bug.
|
|
||||||
|
|
||||||
### The issue tracker is for bugs, not feature requests
|
|
||||||
The GitHub issue tracker is not used for feature requests, but new ideas can be submitted and discussed on the [community forum](https://community.signalusers.org/c/feature-requests). The purpose of this issue tracker is to track bugs in the Android client. Bug reports should only be submitted for existing functionality that does not work as intended. Comments that are relevant and concise will help the developers solve issues more quickly.
|
|
||||||
|
|
||||||
### Send support questions to support
|
|
||||||
You can reach support by sending an email to support@signal.org or by visiting the [Signal Support Center](https://support.signal.org/) where you can also search for existing troubleshooting articles and find answers to frequently asked questions. Please do not post support questions on the GitHub issue tracker.
|
|
||||||
|
|
||||||
### GitHub is not a generic discussion forum
|
|
||||||
Conversations about open bug reports belong here. However, all other discussions should take place on the [community forum](https://community.signalusers.org). You can use the community forum to discuss anything that is related to Signal or to hang out with your fellow users in the "Off Topic" category.
|
|
||||||
|
|
||||||
### Don't bump issues
|
|
||||||
Every time someone comments on an issue, GitHub sends an email to [hundreds of people](https://github.com/signalapp/Signal-Android/watchers). Bumping issues with a "+1" (or asking for updates) generates a lot of unnecessary email notifications and does not help anyone solve the issue any faster. Please be respectful of everyone's time and only comment when you have new information to add.
|
|
||||||
|
|
||||||
### Open issues
|
|
||||||
|
|
||||||
#### If it's open, it's tracked
|
|
||||||
The developers read every issue, but high-priority bugs or features can take precedence over others. Signal is an open source project, and everyone is encouraged to play an active role in diagnosing and fixing open issues.
|
|
||||||
|
|
||||||
### Closed issues
|
|
||||||
|
|
||||||
#### "My issue was closed without giving a reason!"
|
|
||||||
Although we do our best, writing detailed explanations for every issue can be time consuming, and the topic also might have been covered previously in other related issues.
|
|
||||||
|
|
||||||
|
|
||||||
## Pull requests
|
|
||||||
|
|
||||||
### Smaller is better
|
|
||||||
Big changes are significantly less likely to be accepted. Large features often require protocol modifications and necessitate a staged rollout process that is coordinated across millions of users on multiple platforms (Android, iOS, and Desktop).
|
|
||||||
|
|
||||||
Try not to take on too much at once. As a first-time contributor, we recommend starting with small and simple PRs in order to become familiar with the codebase. Most of the work should go into discovering which three lines need to change rather than writing the code.
|
|
||||||
|
|
||||||
### Sign the Contributor License Agreement (CLA)
|
|
||||||
You will need to [sign our CLA](https://signal.org/cla/) before your pull request can be merged.
|
|
||||||
|
|
||||||
### Follow the Code Style Guidelines
|
|
||||||
Ensure that your code adheres to the [Code Style Guidelines](https://github.com/signalapp/Signal-Android/wiki/Code-Style-Guidelines) before submitting a pull request.
|
|
||||||
|
|
||||||
### Submit finished and well-tested pull requests
|
|
||||||
Please do not submit pull requests that are still a work in progress. Pull requests should be thoroughly tested and ready to merge before they are submitted.
|
|
||||||
|
|
||||||
### Merging can sometimes take a while
|
|
||||||
If your pull request follows all of the advice above but still has not been merged, this usually means that the developers haven't had time to review it yet. We understand that this might feel frustrating, and we apologize. The Signal team is still small, but [we are hiring](https://signal.org/workworkwork/).
|
|
||||||
|
|
||||||
|
|
||||||
## How can I contribute?
|
|
||||||
There are several other ways to get involved:
|
|
||||||
* Help new users learn about Signal.
|
|
||||||
* Redirect support questions to support@signal.org and the [Signal Support Center](https://support.signal.org/).
|
|
||||||
* Redirect non-bug discussions to the [community forum](https://community.signalusers.org).
|
|
||||||
* Improve documentation in the [wiki](https://github.com/signalapp/Signal-Android/wiki).
|
|
||||||
* Join the community of volunteer translators on Transifex:
|
|
||||||
* [Android](https://www.transifex.com/signalapp/signal-android/)
|
|
||||||
* [iOS](https://www.transifex.com/signalapp/signal-ios/)
|
|
||||||
* [Desktop](https://www.transifex.com/signalapp/signal-desktop/)
|
|
||||||
* Find and mark duplicate issues.
|
|
||||||
* Try to reproduce issues and help with troubleshooting.
|
|
||||||
* Discover solutions to open issues and post any relevant findings.
|
|
||||||
* Test other people's pull requests.
|
|
||||||
* Contribute to Signal via the [Freedom of the Press Foundation's donation page](https://freedom.press/crowdfunding/signal/).
|
|
||||||
* Share Signal with your friends and family.
|
|
||||||
|
|
||||||
Signal is made for you. Thank you for your feedback and support.
|
|
10
build.gradle
10
build.gradle
@ -6,6 +6,8 @@ buildscript {
|
|||||||
ext.kovenant_version = "3.3.0"
|
ext.kovenant_version = "3.3.0"
|
||||||
ext.identicon_version = "v11"
|
ext.identicon_version = "v11"
|
||||||
ext.rss_parser_version = "2.0.4"
|
ext.rss_parser_version = "2.0.4"
|
||||||
|
ext.google_services_version = "4.3.3"
|
||||||
|
ext.firebase_messaging_version = "18.0.0"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
@ -16,6 +18,7 @@ buildscript {
|
|||||||
classpath "com.android.tools.build:gradle:$gradle_version"
|
classpath "com.android.tools.build:gradle:$gradle_version"
|
||||||
classpath files('libs/gradle-witness.jar')
|
classpath files('libs/gradle-witness.jar')
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
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: 'kotlin-android'
|
||||||
apply plugin: 'witness'
|
apply plugin: 'witness'
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
|
apply plugin: 'com.google.gms.google-services'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
@ -87,6 +91,8 @@ dependencies {
|
|||||||
implementation 'android.arch.lifecycle:extensions:1.1.1'
|
implementation 'android.arch.lifecycle:extensions:1.1.1'
|
||||||
implementation 'android.arch.lifecycle:common-java8: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-core:2.9.1'
|
||||||
implementation 'com.google.android.exoplayer:exoplayer-ui: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"
|
implementation "com.github.ybq:Android-SpinKit:1.4.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
def canonicalVersionCode = 48
|
def canonicalVersionCode = 49
|
||||||
def canonicalVersionName = "1.0.11"
|
def canonicalVersionName = "1.1.0"
|
||||||
|
|
||||||
def postFixSize = 10
|
def postFixSize = 10
|
||||||
def abiPostFix = ['armeabi-v7a' : 1,
|
def abiPostFix = ['armeabi-v7a' : 1,
|
||||||
|
15
res/drawable/pn_option_background.xml
Normal file
15
res/drawable/pn_option_background.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle" >
|
||||||
|
|
||||||
|
<solid
|
||||||
|
android:color="@color/pn_option_background" />
|
||||||
|
|
||||||
|
<stroke
|
||||||
|
android:color="@color/pn_option_border"
|
||||||
|
android:width="@dimen/border_thickness" />
|
||||||
|
|
||||||
|
<corners android:radius="@dimen/pn_option_corner_radius" />
|
||||||
|
|
||||||
|
</shape>
|
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<transition xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@drawable/pn_option_background_selected" />
|
||||||
|
<item android:drawable="@drawable/pn_option_background" />
|
||||||
|
</transition>
|
5
res/drawable/pn_option_background_select_transition.xml
Normal file
5
res/drawable/pn_option_background_select_transition.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<transition xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@drawable/pn_option_background" />
|
||||||
|
<item android:drawable="@drawable/pn_option_background_selected" />
|
||||||
|
</transition>
|
15
res/drawable/pn_option_background_selected.xml
Normal file
15
res/drawable/pn_option_background_selected.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle" >
|
||||||
|
|
||||||
|
<solid
|
||||||
|
android:color="@color/pn_option_background" />
|
||||||
|
|
||||||
|
<stroke
|
||||||
|
android:color="@color/accent"
|
||||||
|
android:width="@dimen/border_thickness" />
|
||||||
|
|
||||||
|
<corners android:radius="@dimen/pn_option_corner_radius" />
|
||||||
|
|
||||||
|
</shape>
|
116
res/layout-sw400dp/activity_pn_mode.xml
Normal file
116
res/layout-sw400dp/activity_pn_mode.xml
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/default_session_background"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:textSize="@dimen/very_large_font_size"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/activity_pn_mode_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:textSize="@dimen/medium_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/activity_pn_mode_explanation" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/fcmOptionView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginTop="@dimen/medium_spacing"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/pn_option_background">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="@dimen/medium_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/activity_pn_mode_fcm_option_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:textSize="@dimen/very_small_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/activity_pn_mode_fcm_option_explanation" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:textSize="@dimen/very_small_font_size"
|
||||||
|
android:textColor="@color/accent"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/activity_pn_mode_recommended_option_tag" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/backgroundPollingOptionView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginTop="@dimen/small_spacing"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/pn_option_background">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="@dimen/medium_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/activity_pn_mode_background_polling_option_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:textSize="@dimen/very_small_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/activity_pn_mode_background_polling_option_explanation" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="@style/MediumProminentFilledButton"
|
||||||
|
android:id="@+id/registerButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/medium_button_height"
|
||||||
|
android:layout_marginLeft="@dimen/massive_spacing"
|
||||||
|
android:layout_marginRight="@dimen/massive_spacing"
|
||||||
|
android:layout_marginBottom="@dimen/medium_spacing"
|
||||||
|
android:text="Continue" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
118
res/layout-sw400dp/fragment_pn_mode_bottom_sheet.xml
Normal file
118
res/layout-sw400dp/fragment_pn_mode_bottom_sheet.xml
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
app:behavior_hideable="true"
|
||||||
|
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
|
||||||
|
android:background="@drawable/default_bottom_sheet_background_inset">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginTop="@dimen/medium_spacing"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:textSize="@dimen/very_large_font_size"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/sheet_pn_mode_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:textSize="@dimen/medium_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/sheet_pn_mode_explanation" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/fcmOptionView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginTop="@dimen/medium_spacing"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/pn_option_background">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="@dimen/medium_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/sheet_pn_mode_fcm_option_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:textSize="@dimen/very_small_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/sheet_pn_mode_fcm_option_explanation" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:textSize="@dimen/very_small_font_size"
|
||||||
|
android:textColor="@color/accent"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/sheet_pn_mode_recommended_option_tag" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/backgroundPollingOptionView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginTop="@dimen/small_spacing"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/pn_option_background">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="@dimen/medium_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/sheet_pn_mode_background_polling_option_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:textSize="@dimen/very_small_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/sheet_pn_mode_background_polling_option_explanation" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="@style/MediumProminentOutlineButton"
|
||||||
|
android:id="@+id/confirmButton"
|
||||||
|
android:layout_width="240dp"
|
||||||
|
android:layout_height="@dimen/medium_button_height"
|
||||||
|
android:layout_marginTop="@dimen/medium_spacing"
|
||||||
|
android:text="@string/sheet_pn_mode_confirm_button_title" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="@style/MediumUnimportantOutlineButton"
|
||||||
|
android:id="@+id/skipButton"
|
||||||
|
android:layout_width="240dp"
|
||||||
|
android:layout_height="@dimen/medium_button_height"
|
||||||
|
android:layout_marginTop="@dimen/small_spacing"
|
||||||
|
android:layout_marginBottom="@dimen/medium_spacing"
|
||||||
|
android:text="@string/sheet_pn_mode_skip_button_title" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -73,6 +73,32 @@
|
|||||||
android:layout_centerHorizontal="true"
|
android:layout_centerHorizontal="true"
|
||||||
android:layout_alignParentBottom="true" />
|
android:layout_alignParentBottom="true" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/emptyStateContainer"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="32dp"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_centerInParent="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="@dimen/medium_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="You don't have any contacts yet" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="@style/MediumProminentOutlineButton"
|
||||||
|
android:id="@+id/createNewPrivateChatButton"
|
||||||
|
android:layout_width="196dp"
|
||||||
|
android:layout_height="@dimen/medium_button_height"
|
||||||
|
android:layout_marginTop="@dimen/medium_spacing"
|
||||||
|
android:text="Start a Session" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -31,7 +31,7 @@
|
|||||||
android:layout_width="196dp"
|
android:layout_width="196dp"
|
||||||
android:layout_height="@dimen/medium_button_height"
|
android:layout_height="@dimen/medium_button_height"
|
||||||
android:layout_marginTop="@dimen/medium_spacing"
|
android:layout_marginTop="@dimen/medium_spacing"
|
||||||
android:text="Link a Device" />
|
android:text="Link a Device (Beta)" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
116
res/layout/activity_pn_mode.xml
Normal file
116
res/layout/activity_pn_mode.xml
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/default_session_background"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:textSize="@dimen/large_font_size"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/activity_pn_mode_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:textSize="@dimen/small_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/activity_pn_mode_explanation" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/fcmOptionView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginTop="@dimen/small_spacing"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/pn_option_background">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="@dimen/medium_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/activity_pn_mode_fcm_option_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:textSize="@dimen/very_small_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/activity_pn_mode_fcm_option_explanation" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:textSize="@dimen/very_small_font_size"
|
||||||
|
android:textColor="@color/accent"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/activity_pn_mode_recommended_option_tag" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/backgroundPollingOptionView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginTop="@dimen/small_spacing"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/pn_option_background">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="@dimen/medium_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/activity_pn_mode_background_polling_option_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:textSize="@dimen/very_small_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/activity_pn_mode_background_polling_option_explanation" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="@style/MediumProminentFilledButton"
|
||||||
|
android:id="@+id/registerButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/medium_button_height"
|
||||||
|
android:layout_marginLeft="@dimen/massive_spacing"
|
||||||
|
android:layout_marginRight="@dimen/massive_spacing"
|
||||||
|
android:layout_marginBottom="@dimen/medium_spacing"
|
||||||
|
android:text="Continue" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
118
res/layout/fragment_pn_mode_bottom_sheet.xml
Normal file
118
res/layout/fragment_pn_mode_bottom_sheet.xml
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
app:behavior_hideable="true"
|
||||||
|
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
|
||||||
|
android:background="@drawable/default_bottom_sheet_background_inset">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginTop="@dimen/medium_spacing"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:textSize="@dimen/large_font_size"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/sheet_pn_mode_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:textSize="@dimen/small_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/sheet_pn_mode_explanation" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/fcmOptionView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginTop="@dimen/small_spacing"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/pn_option_background">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="@dimen/medium_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/sheet_pn_mode_fcm_option_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:textSize="@dimen/very_small_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/sheet_pn_mode_fcm_option_explanation" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:textSize="@dimen/very_small_font_size"
|
||||||
|
android:textColor="@color/accent"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/sheet_pn_mode_recommended_option_tag" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/backgroundPollingOptionView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="@dimen/very_large_spacing"
|
||||||
|
android:layout_marginTop="@dimen/small_spacing"
|
||||||
|
android:layout_marginRight="@dimen/very_large_spacing"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/pn_option_background">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="@dimen/medium_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/sheet_pn_mode_background_polling_option_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:textSize="@dimen/very_small_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:text="@string/sheet_pn_mode_background_polling_option_explanation" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="@style/MediumProminentOutlineButton"
|
||||||
|
android:id="@+id/confirmButton"
|
||||||
|
android:layout_width="240dp"
|
||||||
|
android:layout_height="@dimen/medium_button_height"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:text="@string/sheet_pn_mode_confirm_button_title" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="@style/MediumUnimportantOutlineButton"
|
||||||
|
android:id="@+id/skipButton"
|
||||||
|
android:layout_width="240dp"
|
||||||
|
android:layout_height="@dimen/medium_button_height"
|
||||||
|
android:layout_marginTop="@dimen/small_spacing"
|
||||||
|
android:layout_marginBottom="@dimen/medium_spacing"
|
||||||
|
android:text="@string/sheet_pn_mode_skip_button_title" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -26,6 +26,8 @@
|
|||||||
<color name="sent_message_background">#3F4146</color>
|
<color name="sent_message_background">#3F4146</color>
|
||||||
<color name="quote_not_found_background">#99FFFFFF</color>
|
<color name="quote_not_found_background">#99FFFFFF</color>
|
||||||
<color name="new_conversation_button_collapsed_background">#1F1F1F</color>
|
<color name="new_conversation_button_collapsed_background">#1F1F1F</color>
|
||||||
|
<color name="pn_option_background">#1B1B1B</color>
|
||||||
|
<color name="pn_option_border">#212121</color>
|
||||||
<!-- Session -->
|
<!-- Session -->
|
||||||
|
|
||||||
<!-- Loki -->
|
<!-- Loki -->
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
<dimen name="setting_button_height">56dp</dimen>
|
<dimen name="setting_button_height">56dp</dimen>
|
||||||
<dimen name="dialog_corner_radius">8dp</dimen>
|
<dimen name="dialog_corner_radius">8dp</dimen>
|
||||||
<dimen name="dialog_button_corner_radius">4dp</dimen>
|
<dimen name="dialog_button_corner_radius">4dp</dimen>
|
||||||
|
<dimen name="pn_option_corner_radius">8dp</dimen>
|
||||||
|
|
||||||
<!-- Distances -->
|
<!-- Distances -->
|
||||||
<dimen name="small_spacing">8dp</dimen>
|
<dimen name="small_spacing">8dp</dimen>
|
||||||
|
@ -1671,4 +1671,26 @@
|
|||||||
<string name="fragment_contact_selection_contacts_title">Contacts</string>
|
<string name="fragment_contact_selection_contacts_title">Contacts</string>
|
||||||
<string name="fragment_contact_selection_closed_groups_title">Closed Groups</string>
|
<string name="fragment_contact_selection_closed_groups_title">Closed Groups</string>
|
||||||
<string name="fragment_contact_selection_open_groups_title">Open Groups</string>
|
<string name="fragment_contact_selection_open_groups_title">Open Groups</string>
|
||||||
|
<string name="activity_pn_mode_title">Push Notifications</string>
|
||||||
|
<string name="activity_pn_mode_explanation">There are two ways Session can handle push notifications. Make sure to read the descriptions carefully before you choose.</string>
|
||||||
|
<string name="activity_pn_mode_fcm_option_title">Firebase Cloud Messaging</string>
|
||||||
|
<string name="activity_pn_mode_fcm_option_explanation">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.</string>
|
||||||
|
<string name="activity_pn_mode_background_polling_option_title">Background Polling</string>
|
||||||
|
<string name="activity_pn_mode_background_polling_option_explanation">Session will occasionally check for new messages in the background. This guarantees full privacy protection, but message notifications may be significantly delayed.</string>
|
||||||
|
<string name="activity_pn_mode_recommended_option_tag">Recommended</string>
|
||||||
|
<string name="activity_pn_mode_no_option_picked_dialog_title">Please Pick an Option</string>
|
||||||
|
<string name="sheet_pn_mode_title">Push Notifications</string>
|
||||||
|
<string name="sheet_pn_mode_explanation">Session now features two ways to handle push notifications. Make sure to read the descriptions carefully before you choose.</string>
|
||||||
|
<string name="sheet_pn_mode_fcm_option_title">Firebase Cloud Messaging</string>
|
||||||
|
<string name="sheet_pn_mode_fcm_option_explanation">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.</string>
|
||||||
|
<string name="sheet_pn_mode_background_polling_option_title">Background Polling</string>
|
||||||
|
<string name="sheet_pn_mode_background_polling_option_explanation">Session will occasionally check for new messages in the background. This guarantees full privacy protection, but message notifications may be significantly delayed.</string>
|
||||||
|
<string name="sheet_pn_mode_recommended_option_tag">Recommended</string>
|
||||||
|
<string name="sheet_pn_mode_no_option_picked_dialog_title">Please Pick an Option</string>
|
||||||
|
<string name="sheet_pn_mode_confirm_button_title">Confirm</string>
|
||||||
|
<string name="sheet_pn_mode_skip_button_title">Skip</string>
|
||||||
|
<string name="preferences_notifications_strategy_category_title">Notification Strategy</string>
|
||||||
|
<string name="preferences_notifications_use_fcm_option_title">Use FCM</string>
|
||||||
|
<string name="preferences_notifications_use_fcm_option_explanation">Using Firebase Cloud Messaging allows for more reliable push notifications, but exposes your IP to Google.</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -21,6 +21,19 @@
|
|||||||
|
|
||||||
<PreferenceCategory android:layout="@layout/preference_divider" />
|
<PreferenceCategory android:layout="@layout/preference_divider" />
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="@string/preferences_notifications_strategy_category_title">
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.components.SwitchPreferenceCompat
|
||||||
|
android:dependency="pref_key_enable_notifications"
|
||||||
|
android:key="pref_key_use_fcm"
|
||||||
|
android:title="@string/preferences_notifications_use_fcm_option_title"
|
||||||
|
android:summary="@string/preferences_notifications_use_fcm_option_explanation"
|
||||||
|
android:defaultValue="false" />
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory android:layout="@layout/preference_divider" />
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/activity_notification_settings_style_section_title">
|
<PreferenceCategory android:title="@string/activity_notification_settings_style_section_title">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.preferences.widgets.SignalPreference
|
<org.thoughtcrime.securesms.preferences.widgets.SignalPreference
|
||||||
|
@ -30,6 +30,8 @@ import android.support.annotation.NonNull;
|
|||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.multidex.MultiDexApplication;
|
import android.support.multidex.MultiDexApplication;
|
||||||
|
|
||||||
|
import com.google.firebase.iid.FirebaseInstanceId;
|
||||||
|
|
||||||
import org.conscrypt.Conscrypt;
|
import org.conscrypt.Conscrypt;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.signal.aesgcmprovider.AesGcmProvider;
|
import org.signal.aesgcmprovider.AesGcmProvider;
|
||||||
@ -61,13 +63,14 @@ import org.thoughtcrime.securesms.logging.Log;
|
|||||||
import org.thoughtcrime.securesms.logging.PersistentLogger;
|
import org.thoughtcrime.securesms.logging.PersistentLogger;
|
||||||
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
|
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
|
||||||
import org.thoughtcrime.securesms.loki.LokiPublicChatManager;
|
import org.thoughtcrime.securesms.loki.LokiPublicChatManager;
|
||||||
|
import org.thoughtcrime.securesms.loki.LokiPushNotificationManager;
|
||||||
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
|
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
|
||||||
import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity;
|
import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity;
|
||||||
import org.thoughtcrime.securesms.loki.redesign.messaging.BackgroundOpenGroupPollWorker;
|
import org.thoughtcrime.securesms.loki.redesign.messaging.BackgroundOpenGroupPollWorker;
|
||||||
import org.thoughtcrime.securesms.loki.redesign.messaging.BackgroundPollWorker;
|
import org.thoughtcrime.securesms.loki.redesign.messaging.BackgroundPollWorker;
|
||||||
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIDatabase;
|
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiRSSFeedPoller;
|
|
||||||
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiUserDatabase;
|
import org.thoughtcrime.securesms.loki.redesign.messaging.LokiUserDatabase;
|
||||||
|
import org.thoughtcrime.securesms.loki.redesign.shelved.LokiRSSFeedPoller;
|
||||||
import org.thoughtcrime.securesms.loki.redesign.utilities.Broadcaster;
|
import org.thoughtcrime.securesms.loki.redesign.utilities.Broadcaster;
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||||
@ -97,6 +100,7 @@ import org.whispersystems.signalservice.loki.api.LokiAPIDatabaseProtocol;
|
|||||||
import org.whispersystems.signalservice.loki.api.LokiP2PAPI;
|
import org.whispersystems.signalservice.loki.api.LokiP2PAPI;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiP2PAPIDelegate;
|
import org.whispersystems.signalservice.loki.api.LokiP2PAPIDelegate;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiPoller;
|
import org.whispersystems.signalservice.loki.api.LokiPoller;
|
||||||
|
import org.whispersystems.signalservice.loki.api.LokiPushNotificationAcknowledgement;
|
||||||
import org.whispersystems.signalservice.loki.api.LokiSwarmAPI;
|
import org.whispersystems.signalservice.loki.api.LokiSwarmAPI;
|
||||||
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
|
import org.whispersystems.signalservice.loki.api.fileserver.LokiFileServerAPI;
|
||||||
import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat;
|
import org.whispersystems.signalservice.loki.api.publicchats.LokiPublicChat;
|
||||||
@ -107,7 +111,6 @@ import java.io.File;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -182,6 +185,8 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
||||||
// Loki - Set up P2P API if needed
|
// Loki - Set up P2P API if needed
|
||||||
setUpP2PAPI();
|
setUpP2PAPI();
|
||||||
|
// Loki - Set up push notification acknowledgement
|
||||||
|
LokiPushNotificationAcknowledgement.Companion.configureIfNeeded(BuildConfig.DEBUG);
|
||||||
// Loki - Update device mappings
|
// Loki - Update device mappings
|
||||||
if (setUpStorageAPIIfNeeded()) {
|
if (setUpStorageAPIIfNeeded()) {
|
||||||
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
|
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(this);
|
||||||
@ -199,6 +204,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
// Loki - Set up public chat manager
|
// Loki - Set up public chat manager
|
||||||
lokiPublicChatManager = new LokiPublicChatManager(this);
|
lokiPublicChatManager = new LokiPublicChatManager(this);
|
||||||
updatePublicChatProfilePictureIfNeeded();
|
updatePublicChatProfilePictureIfNeeded();
|
||||||
|
registerForFCMIfNeeded(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -455,6 +461,24 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
}, this);
|
}, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void registerForFCMIfNeeded(Boolean force) {
|
||||||
|
Context context = this;
|
||||||
|
FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(task -> {
|
||||||
|
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
|
@Override
|
||||||
public void ping(@NotNull String s) {
|
public void ping(@NotNull String s) {
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
@ -509,8 +533,10 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void createRSSFeedsIfNeeded() {
|
public void createRSSFeedsIfNeeded() {
|
||||||
|
return;
|
||||||
|
/*
|
||||||
ArrayList<LokiRSSFeed> feeds = new ArrayList<>();
|
ArrayList<LokiRSSFeed> feeds = new ArrayList<>();
|
||||||
// feeds.add(lokiNewsFeed());
|
feeds.add(lokiNewsFeed());
|
||||||
feeds.add(lokiMessengerUpdatesFeed());
|
feeds.add(lokiMessengerUpdatesFeed());
|
||||||
for (LokiRSSFeed feed : feeds) {
|
for (LokiRSSFeed feed : feeds) {
|
||||||
boolean isFeedSetUp = TextSecurePreferences.isChatSetUp(this, feed.getId());
|
boolean isFeedSetUp = TextSecurePreferences.isChatSetUp(this, feed.getId());
|
||||||
@ -519,9 +545,12 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
TextSecurePreferences.markChatSetUp(this, feed.getId());
|
TextSecurePreferences.markChatSetUp(this, feed.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createRSSFeedPollersIfNeeded() {
|
private void createRSSFeedPollersIfNeeded() {
|
||||||
|
return;
|
||||||
|
/*
|
||||||
// Only create the RSS feed pollers if their threads aren't deleted
|
// Only create the RSS feed pollers if their threads aren't deleted
|
||||||
LokiRSSFeed lokiNewsFeed = lokiNewsFeed();
|
LokiRSSFeed lokiNewsFeed = lokiNewsFeed();
|
||||||
long lokiNewsFeedThreadID = GroupManager.getRSSFeedThreadId(lokiNewsFeed.getId(), this);
|
long lokiNewsFeedThreadID = GroupManager.getRSSFeedThreadId(lokiNewsFeed.getId(), this);
|
||||||
@ -537,6 +566,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
if (lokiMessengerUpdatesFeedPoller == null) {
|
if (lokiMessengerUpdatesFeedPoller == null) {
|
||||||
lokiMessengerUpdatesFeedPoller = new LokiRSSFeedPoller(this, lokiMessengerUpdatesFeed());
|
lokiMessengerUpdatesFeedPoller = new LokiRSSFeedPoller(this, lokiMessengerUpdatesFeed());
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpThreadDeletionListeners(long threadID, Runnable onDelete) {
|
private void setUpThreadDeletionListeners(long threadID, Runnable onDelete) {
|
||||||
@ -561,9 +591,12 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void startRSSFeedPollersIfNeeded() {
|
public void startRSSFeedPollersIfNeeded() {
|
||||||
|
return;
|
||||||
|
/*
|
||||||
createRSSFeedPollersIfNeeded();
|
createRSSFeedPollersIfNeeded();
|
||||||
if (lokiNewsFeedPoller != null) lokiNewsFeedPoller.startIfNeeded();
|
if (lokiNewsFeedPoller != null) lokiNewsFeedPoller.startIfNeeded();
|
||||||
if (lokiMessengerUpdatesFeedPoller != null) lokiMessengerUpdatesFeedPoller.startIfNeeded();
|
if (lokiMessengerUpdatesFeedPoller != null) lokiMessengerUpdatesFeedPoller.startIfNeeded();
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resubmitProfilePictureIfNeeded() {
|
private void resubmitProfilePictureIfNeeded() {
|
||||||
|
@ -317,7 +317,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
private Button makeDefaultSmsButton;
|
private Button makeDefaultSmsButton;
|
||||||
private Button registerButton;
|
private Button registerButton;
|
||||||
private InputAwareLayout container;
|
private InputAwareLayout container;
|
||||||
private View composePanel;
|
|
||||||
protected Stub<ReminderView> reminderView;
|
protected Stub<ReminderView> reminderView;
|
||||||
private Stub<UnverifiedBannerView> unverifiedBannerView;
|
private Stub<UnverifiedBannerView> unverifiedBannerView;
|
||||||
private Stub<GroupShareProfileView> groupShareProfileView;
|
private Stub<GroupShareProfileView> groupShareProfileView;
|
||||||
@ -552,7 +551,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
updateTitleTextView(recipient);
|
updateTitleTextView(recipient);
|
||||||
updateSubtitleTextView();
|
updateSubtitleTextView();
|
||||||
setActionBarColor(recipient.getColor());
|
setActionBarColor(recipient.getColor());
|
||||||
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
updateInputUI(recipient, isSecureText, isDefaultSms);
|
||||||
setGroupShareProfileReminder(recipient);
|
setGroupShareProfileReminder(recipient);
|
||||||
calculateCharactersRemaining();
|
calculateCharactersRemaining();
|
||||||
|
|
||||||
@ -645,7 +644,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
updateTitleTextView(recipient);
|
updateTitleTextView(recipient);
|
||||||
updateSubtitleTextView();
|
updateSubtitleTextView();
|
||||||
NotificationChannels.updateContactChannelName(this, recipient);
|
NotificationChannels.updateContactChannelName(this, recipient);
|
||||||
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
updateInputUI(recipient, isSecureText, isDefaultSms);
|
||||||
supportInvalidateOptionsMenu();
|
supportInvalidateOptionsMenu();
|
||||||
break;
|
break;
|
||||||
case TAKE_PHOTO:
|
case TAKE_PHOTO:
|
||||||
@ -858,6 +857,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
searchViewModel.onSearchClosed();
|
searchViewModel.onSearchClosed();
|
||||||
searchNav.setVisibility(View.GONE);
|
searchNav.setVisibility(View.GONE);
|
||||||
inputPanel.setVisibility(View.VISIBLE);
|
inputPanel.setVisibility(View.VISIBLE);
|
||||||
|
updateInputUI(recipient, isSecureText, isDefaultSms);
|
||||||
fragment.onSearchQueryUpdated(null);
|
fragment.onSearchQueryUpdated(null);
|
||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
return true;
|
return true;
|
||||||
@ -1343,7 +1343,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
|
|
||||||
calculateCharactersRemaining();
|
calculateCharactersRemaining();
|
||||||
supportInvalidateOptionsMenu();
|
supportInvalidateOptionsMenu();
|
||||||
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
updateInputUI(recipient, isSecureText, isDefaultSms);
|
||||||
}
|
}
|
||||||
|
|
||||||
///// Initializers
|
///// Initializers
|
||||||
@ -1627,7 +1627,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
unblockButton = ViewUtil.findById(this, R.id.unblock_button);
|
unblockButton = ViewUtil.findById(this, R.id.unblock_button);
|
||||||
makeDefaultSmsButton = ViewUtil.findById(this, R.id.make_default_sms_button);
|
makeDefaultSmsButton = ViewUtil.findById(this, R.id.make_default_sms_button);
|
||||||
registerButton = ViewUtil.findById(this, R.id.register_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);
|
container = ViewUtil.findById(this, R.id.layout_container);
|
||||||
reminderView = ViewUtil.findStubById(this, R.id.reminder_stub);
|
reminderView = ViewUtil.findStubById(this, R.id.reminder_stub);
|
||||||
unverifiedBannerView = ViewUtil.findStubById(this, R.id.unverified_banner_stub);
|
unverifiedBannerView = ViewUtil.findStubById(this, R.id.unverified_banner_stub);
|
||||||
@ -1835,7 +1834,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
updateTitleTextView(recipient);
|
updateTitleTextView(recipient);
|
||||||
updateSubtitleTextView();
|
updateSubtitleTextView();
|
||||||
// titleView.setVerified(identityRecords.isVerified());
|
// titleView.setVerified(identityRecords.isVerified());
|
||||||
setBlockedUserState(recipient, isSecureText, isDefaultSms);
|
updateInputUI(recipient, isSecureText, isDefaultSms);
|
||||||
setActionBarColor(recipient.getColor());
|
setActionBarColor(recipient.getColor());
|
||||||
setGroupShareProfileReminder(recipient);
|
setGroupShareProfileReminder(recipient);
|
||||||
updateReminders(recipient.hasSeenInviteReminder());
|
updateReminders(recipient.hasSeenInviteReminder());
|
||||||
@ -2043,29 +2042,30 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
setStatusBarColor(getResources().getColor(R.color.action_bar_background));
|
setStatusBarColor(getResources().getColor(R.color.action_bar_background));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setBlockedUserState(Recipient recipient, boolean isSecureText, boolean isDefaultSms) {
|
// FIXME: This name is confusing because we also have updateInputPanel and setInputPanelEnabled
|
||||||
if (recipient.isGroupRecipient() && recipient.getAddress().isRSSFeed()) {
|
private void updateInputUI(Recipient recipient, boolean isSecureText, boolean isDefaultSms) {
|
||||||
|
if (recipient.isGroupRecipient() && !isActiveGroup()) {
|
||||||
unblockButton.setVisibility(View.GONE);
|
unblockButton.setVisibility(View.GONE);
|
||||||
composePanel.setVisibility(View.GONE);
|
inputPanel.setVisibility(View.GONE);
|
||||||
makeDefaultSmsButton.setVisibility(View.GONE);
|
makeDefaultSmsButton.setVisibility(View.GONE);
|
||||||
registerButton.setVisibility(View.GONE);
|
registerButton.setVisibility(View.GONE);
|
||||||
} else if (recipient.isBlocked()) {
|
} else if (recipient.isBlocked()) {
|
||||||
unblockButton.setVisibility(View.VISIBLE);
|
unblockButton.setVisibility(View.VISIBLE);
|
||||||
composePanel.setVisibility(View.GONE);
|
inputPanel.setVisibility(View.GONE);
|
||||||
makeDefaultSmsButton.setVisibility(View.GONE);
|
makeDefaultSmsButton.setVisibility(View.GONE);
|
||||||
registerButton.setVisibility(View.GONE);
|
registerButton.setVisibility(View.GONE);
|
||||||
} else if (!isSecureText && isPushGroupConversation()) {
|
} else if (!isSecureText && isPushGroupConversation()) {
|
||||||
unblockButton.setVisibility(View.GONE);
|
unblockButton.setVisibility(View.GONE);
|
||||||
composePanel.setVisibility(View.GONE);
|
inputPanel.setVisibility(View.GONE);
|
||||||
makeDefaultSmsButton.setVisibility(View.GONE);
|
makeDefaultSmsButton.setVisibility(View.GONE);
|
||||||
registerButton.setVisibility(View.VISIBLE);
|
registerButton.setVisibility(View.VISIBLE);
|
||||||
} else if (!isSecureText && !isDefaultSms) {
|
} else if (!isSecureText && !isDefaultSms) {
|
||||||
unblockButton.setVisibility(View.GONE);
|
unblockButton.setVisibility(View.GONE);
|
||||||
composePanel.setVisibility(View.GONE);
|
inputPanel.setVisibility(View.GONE);
|
||||||
makeDefaultSmsButton.setVisibility(View.VISIBLE);
|
makeDefaultSmsButton.setVisibility(View.VISIBLE);
|
||||||
registerButton.setVisibility(View.GONE);
|
registerButton.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
composePanel.setVisibility(View.VISIBLE);
|
inputPanel.setVisibility(View.VISIBLE);
|
||||||
unblockButton.setVisibility(View.GONE);
|
unblockButton.setVisibility(View.GONE);
|
||||||
makeDefaultSmsButton.setVisibility(View.GONE);
|
makeDefaultSmsButton.setVisibility(View.GONE);
|
||||||
registerButton.setVisibility(View.GONE);
|
registerButton.setVisibility(View.GONE);
|
||||||
@ -2125,7 +2125,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isActiveGroup() {
|
private boolean isActiveGroup() {
|
||||||
if (!isGroupConversation()) return false;
|
if (!isGroupConversation() || recipient.getAddress().isRSSFeed()) return false;
|
||||||
|
|
||||||
Optional<GroupRecord> record = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getAddress().toGroupString());
|
Optional<GroupRecord> record = DatabaseFactory.getGroupDatabase(this).getGroup(getRecipient().getAddress().toGroupString());
|
||||||
return record.isPresent() && record.get().isActive();
|
return record.isPresent() && record.get().isActive();
|
||||||
@ -2314,7 +2314,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
String hint = enabled ? "Message" : "Pending session request";
|
String hint = enabled ? "Message" : "Pending session request";
|
||||||
inputPanel.setHint(hint);
|
inputPanel.setHint(hint);
|
||||||
inputPanel.setEnabled(enabled);
|
inputPanel.setEnabled(enabled);
|
||||||
if (enabled) {
|
if (enabled && inputPanel.getVisibility() == View.VISIBLE) {
|
||||||
inputPanel.composeText.requestFocus();
|
inputPanel.composeText.requestFocus();
|
||||||
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||||
inputMethodManager.showSoftInput(inputPanel.composeText, 0);
|
inputMethodManager.showSoftInput(inputPanel.composeText, 0);
|
||||||
@ -2939,6 +2939,8 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleReplyMessage(MessageRecord messageRecord) {
|
public void handleReplyMessage(MessageRecord messageRecord) {
|
||||||
|
if (recipient.isGroupRecipient() && !isActiveGroup()) { return; }
|
||||||
|
|
||||||
Recipient author;
|
Recipient author;
|
||||||
|
|
||||||
if (messageRecord.isOutgoing()) {
|
if (messageRecord.isOutgoing()) {
|
||||||
|
@ -137,49 +137,57 @@ public class GroupMessageProcessor {
|
|||||||
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||||
String id = GroupUtil.getEncodedId(group);
|
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) {
|
if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) {
|
||||||
|
// Only update group if the group admin sent the message
|
||||||
String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, content.getSender());
|
String hexEncodedPublicKey = getMasterHexEncodedPublicKey(context, content.getSender());
|
||||||
if (!groupRecord.getAdmins().contains(Address.fromSerialized(hexEncodedPublicKey))) {
|
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.");
|
Log.d("Loki - Group Message", "Received a group update message from a non-admin user for " + id +". Ignoring.");
|
||||||
return null;
|
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<Address> recordMembers = new HashSet<>(groupRecord.getMembers());
|
Set<Address> currentMembers = new HashSet<>(groupRecord.getMembers());
|
||||||
Set<Address> messageMembers = new HashSet<>();
|
Set<Address> newMembers = new HashSet<>();
|
||||||
|
|
||||||
for (String messageMember : group.getMembers().get()) {
|
for (String messageMember : group.getMembers().get()) {
|
||||||
messageMembers.add(Address.fromExternal(context, messageMember));
|
newMembers.add(Address.fromExternal(context, messageMember));
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<Address> addedMembers = new HashSet<>(messageMembers);
|
// Added members are the members who are present in newMembers but not in currentMembers
|
||||||
addedMembers.removeAll(recordMembers);
|
Set<Address> addedMembers = new HashSet<>(newMembers);
|
||||||
|
addedMembers.removeAll(currentMembers);
|
||||||
|
|
||||||
Set<Address> missingMembers = new HashSet<>(recordMembers);
|
// Kicked members are members who are present in currentMembers but not in newMembers
|
||||||
missingMembers.removeAll(messageMembers);
|
Set<Address> removedMembers = new HashSet<>(currentMembers);
|
||||||
|
removedMembers.removeAll(newMembers);
|
||||||
|
|
||||||
GroupContext.Builder builder = createGroupContext(group);
|
GroupContext.Builder builder = createGroupContext(group);
|
||||||
builder.setType(GroupContext.Type.UPDATE);
|
builder.setType(GroupContext.Type.UPDATE);
|
||||||
|
|
||||||
if (addedMembers.size() > 0) {
|
// Update our group members if they're different
|
||||||
Set<Address> unionMembers = new HashSet<>(recordMembers);
|
if (!currentMembers.equals(newMembers)) {
|
||||||
unionMembers.addAll(messageMembers);
|
database.updateMembers(id, new LinkedList<>(newMembers));
|
||||||
database.updateMembers(id, new LinkedList<>(unionMembers));
|
}
|
||||||
|
|
||||||
builder.clearMembers();
|
|
||||||
|
|
||||||
|
// 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) {
|
for (Address addedMember : addedMembers) {
|
||||||
builder.addMembers(addedMember.serialize());
|
builder.addNewMembers(addedMember.serialize());
|
||||||
}
|
|
||||||
} else {
|
|
||||||
builder.clearMembers();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (missingMembers.size() > 0) {
|
for (Address removedMember : removedMembers) {
|
||||||
for (Address removedMember : missingMembers) {
|
builder.addRemovedMembers(removedMember.serialize());
|
||||||
builder.addMembers(removedMember.serialize());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group.getName().isPresent() || group.getAvatar().isPresent()) {
|
if (group.getName().isPresent() || group.getAvatar().isPresent()) {
|
||||||
@ -191,11 +199,16 @@ public class GroupMessageProcessor {
|
|||||||
builder.clearName();
|
builder.clearName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 (!groupRecord.isActive()) database.setActive(id, true);
|
||||||
|
|
||||||
if (group.getMembers().isPresent()) {
|
if (group.getMembers().isPresent()) {
|
||||||
establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
|
establishSessionsWithMembersIfNeeded(context, group.getMembers().get());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return storeMessage(context, content, group, builder.build(), outgoing);
|
return storeMessage(context, content, group, builder.build(), outgoing);
|
||||||
}
|
}
|
||||||
|
@ -1895,7 +1895,28 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
boolean isGroupActive = groupId.isPresent() && groupDatabase.isActive(groupId.get());
|
boolean isGroupActive = groupId.isPresent() && groupDatabase.isActive(groupId.get());
|
||||||
boolean isLeaveMessage = message.getGroupInfo().isPresent() && message.getGroupInfo().get().getType() == SignalServiceGroup.Type.QUIT;
|
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 {
|
} else {
|
||||||
return sender.isBlocked();
|
return sender.isBlocked();
|
||||||
}
|
}
|
||||||
|
@ -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 = 12 * 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.")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -47,7 +47,7 @@ class CreateClosedGroupActivity : PassphraseRequiredActionBarActivity(), MemberC
|
|||||||
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
|
override fun onCreate(savedInstanceState: Bundle?, isReady: Boolean) {
|
||||||
super.onCreate(savedInstanceState, isReady)
|
super.onCreate(savedInstanceState, isReady)
|
||||||
setContentView(R.layout.activity_create_closed_group)
|
setContentView(R.layout.activity_create_closed_group)
|
||||||
supportActionBar!!.title = "New Closed Group"
|
supportActionBar!!.title = "New Closed Group (Beta)"
|
||||||
recyclerView.adapter = createClosedGroupAdapter
|
recyclerView.adapter = createClosedGroupAdapter
|
||||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||||
createNewPrivateChatButton.setOnClickListener { createNewPrivateChat() }
|
createNewPrivateChatButton.setOnClickListener { createNewPrivateChat() }
|
||||||
|
@ -9,11 +9,9 @@ import android.widget.TextView.OnEditorActionListener
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import kotlinx.android.synthetic.main.activity_display_name.*
|
import kotlinx.android.synthetic.main.activity_display_name.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.ApplicationContext
|
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
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.setUpActionBarSessionLogo
|
||||||
import org.thoughtcrime.securesms.loki.redesign.utilities.show
|
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
import org.whispersystems.signalservice.api.crypto.ProfileCipher
|
import org.whispersystems.signalservice.api.crypto.ProfileCipher
|
||||||
|
|
||||||
@ -25,16 +23,14 @@ class DisplayNameActivity : BaseActionBarActivity() {
|
|||||||
setContentView(R.layout.activity_display_name)
|
setContentView(R.layout.activity_display_name)
|
||||||
displayNameEditText.imeOptions = displayNameEditText.imeOptions or 16777216 // Always use incognito keyboard
|
displayNameEditText.imeOptions = displayNameEditText.imeOptions or 16777216 // Always use incognito keyboard
|
||||||
displayNameEditText.setOnEditorActionListener(
|
displayNameEditText.setOnEditorActionListener(
|
||||||
OnEditorActionListener { _, actionId, event ->
|
OnEditorActionListener { _, actionID, event ->
|
||||||
// Handle validation from keyboard to trigger registration
|
if (actionID == EditorInfo.IME_ACTION_SEARCH ||
|
||||||
if (actionId == EditorInfo.IME_ACTION_SEARCH ||
|
actionID == EditorInfo.IME_ACTION_DONE ||
|
||||||
actionId == EditorInfo.IME_ACTION_DONE ||
|
(event.action == KeyEvent.ACTION_DOWN &&
|
||||||
(event.action === KeyEvent.ACTION_DOWN
|
event.keyCode == KeyEvent.KEYCODE_ENTER)) {
|
||||||
&& event.keyCode === KeyEvent.KEYCODE_ENTER)) {
|
this.register()
|
||||||
this.register();
|
|
||||||
return@OnEditorActionListener true
|
return@OnEditorActionListener true
|
||||||
}
|
}
|
||||||
// Return true if you have consumed the action, else false.
|
|
||||||
false
|
false
|
||||||
})
|
})
|
||||||
registerButton.setOnClickListener { register() }
|
registerButton.setOnClickListener { register() }
|
||||||
@ -54,20 +50,7 @@ class DisplayNameActivity : BaseActionBarActivity() {
|
|||||||
val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
|
val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
inputMethodManager.hideSoftInputFromWindow(displayNameEditText.windowToken, 0)
|
inputMethodManager.hideSoftInputFromWindow(displayNameEditText.windowToken, 0)
|
||||||
TextSecurePreferences.setProfileName(this, displayName)
|
TextSecurePreferences.setProfileName(this, displayName)
|
||||||
TextSecurePreferences.setHasSeenWelcomeScreen(this, true)
|
val intent = Intent(this, PNModeActivity::class.java)
|
||||||
TextSecurePreferences.setPromptedPushRegistration(this, true)
|
push(intent)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -32,6 +32,7 @@ import org.thoughtcrime.securesms.database.DatabaseFactory
|
|||||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||||
import org.thoughtcrime.securesms.loki.getColorWithID
|
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.push
|
||||||
import org.thoughtcrime.securesms.loki.redesign.utilities.show
|
import org.thoughtcrime.securesms.loki.redesign.utilities.show
|
||||||
import org.thoughtcrime.securesms.loki.redesign.views.ConversationView
|
import org.thoughtcrime.securesms.loki.redesign.views.ConversationView
|
||||||
@ -111,6 +112,8 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
|||||||
recyclerView.adapter = homeAdapter
|
recyclerView.adapter = homeAdapter
|
||||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||||
ItemTouchHelper(SwipeCallback(this)).attachToRecyclerView(recyclerView)
|
ItemTouchHelper(SwipeCallback(this)).attachToRecyclerView(recyclerView)
|
||||||
|
// Set up empty state view
|
||||||
|
createNewPrivateChatButton.setOnClickListener { createNewPrivateChat() }
|
||||||
// This is a workaround for the fact that CursorRecyclerViewAdapter doesn't actually auto-update (even though it says it will)
|
// This is a workaround for the fact that CursorRecyclerViewAdapter doesn't actually auto-update (even though it says it will)
|
||||||
LoaderManager.getInstance(this).restartLoader(0, null, object : LoaderManager.LoaderCallbacks<Cursor> {
|
LoaderManager.getInstance(this).restartLoader(0, null, object : LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
|
|
||||||
@ -120,6 +123,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
|||||||
|
|
||||||
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor?) {
|
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor?) {
|
||||||
homeAdapter.changeCursor(cursor)
|
homeAdapter.changeCursor(cursor)
|
||||||
|
updateEmptyState()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLoaderReset(cursor: Loader<Cursor>) {
|
override fun onLoaderReset(cursor: Loader<Cursor>) {
|
||||||
@ -156,32 +160,20 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
|||||||
if (hasViewedSeed || !isMasterDevice) {
|
if (hasViewedSeed || !isMasterDevice) {
|
||||||
seedReminderView.visibility = View.GONE
|
seedReminderView.visibility = View.GONE
|
||||||
}
|
}
|
||||||
// if (!TextSecurePreferences.getHasSeenOpenGroupSuggestionSheet(this)) {
|
if (!TextSecurePreferences.hasSeenPNModeSheet(this)) {
|
||||||
// val bottomSheet = OpenGroupSuggestionBottomSheet()
|
val bottomSheet = PNModeBottomSheet()
|
||||||
// bottomSheet.onJoinTapped = {
|
bottomSheet.onConfirmTapped = { isUsingFCM ->
|
||||||
// TextSecurePreferences.setHasSeenOpenGroupSuggestionSheet(this)
|
TextSecurePreferences.setHasSeenPNModeSheet(this, true)
|
||||||
// bottomSheet.dismiss()
|
TextSecurePreferences.setIsUsingFCM(this, isUsingFCM)
|
||||||
// // TODO: Duplication of the code in JoinPublicChatActivity
|
ApplicationContext.getInstance(this).registerForFCMIfNeeded(true)
|
||||||
// val application = ApplicationContext.getInstance(this)
|
bottomSheet.dismiss()
|
||||||
// val channel: Long = 1
|
}
|
||||||
// val displayName = TextSecurePreferences.getProfileName(this)
|
bottomSheet.onSkipTapped = {
|
||||||
// val lokiPublicChatAPI = application.lokiPublicChatAPI!!
|
TextSecurePreferences.setHasSeenPNModeSheet(this, true)
|
||||||
// val url = "https://chat.getsession.org"
|
bottomSheet.dismiss()
|
||||||
// application.lokiPublicChatManager.addChat(url, channel).successUi {
|
}
|
||||||
// lokiPublicChatAPI.getMessages(channel, url)
|
bottomSheet.show(supportFragmentManager, bottomSheet.tag)
|
||||||
// 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)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
@ -192,6 +184,14 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
|
|||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
// region Updating
|
||||||
|
private fun updateEmptyState() {
|
||||||
|
val threadCount = (recyclerView.adapter as HomeAdapter).itemCount
|
||||||
|
emptyStateContainer.visibility = if (threadCount == 0) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Interaction
|
||||||
override fun handleSeedReminderViewContinueButtonTapped() {
|
override fun handleSeedReminderViewContinueButtonTapped() {
|
||||||
val intent = Intent(this, SeedActivity::class.java)
|
val intent = Intent(this, SeedActivity::class.java)
|
||||||
show(intent)
|
show(intent)
|
||||||
|
@ -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
|
||||||
|
}
|
@ -8,7 +8,7 @@ import android.view.ViewGroup
|
|||||||
import kotlinx.android.synthetic.main.fragment_open_group_suggestion_bottom_sheet.*
|
import kotlinx.android.synthetic.main.fragment_open_group_suggestion_bottom_sheet.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
|
|
||||||
public class OpenGroupSuggestionBottomSheet : BottomSheetDialogFragment() {
|
class OpenGroupSuggestionBottomSheet : BottomSheetDialogFragment() {
|
||||||
var onJoinTapped: (() -> Unit)? = null
|
var onJoinTapped: (() -> Unit)? = null
|
||||||
var onDismissTapped: (() -> Unit)? = null
|
var onDismissTapped: (() -> Unit)? = null
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
@ -28,6 +28,7 @@ class BackgroundPollWorker : PersistentAlarmManagerListener() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onAlarm(context: Context, scheduledTime: Long): Long {
|
override fun onAlarm(context: Context, scheduledTime: Long): Long {
|
||||||
|
if (TextSecurePreferences.isUsingFCM(context)) { return 0L }
|
||||||
if (scheduledTime != 0L) {
|
if (scheduledTime != 0L) {
|
||||||
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
|
val userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||||
val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context)
|
val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package org.thoughtcrime.securesms.loki.redesign.messaging
|
package org.thoughtcrime.securesms.loki.redesign.shelved
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Handler
|
import android.os.Handler
|
@ -39,6 +39,7 @@ class NewConversationButtonSetView : RelativeLayout {
|
|||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Settings
|
// region Settings
|
||||||
|
private val minDragDistance by lazy { toPx(40, resources).toFloat() }
|
||||||
private val maxDragDistance by lazy { toPx(56, resources).toFloat() }
|
private val maxDragDistance by lazy { toPx(56, resources).toFloat() }
|
||||||
private val dragMargin by lazy { toPx(16, resources).toFloat() }
|
private val dragMargin by lazy { toPx(16, resources).toFloat() }
|
||||||
private val bottomMargin by lazy { resources.getDimension(R.dimen.new_conversation_button_bottom_offset) }
|
private val bottomMargin by lazy { resources.getDimension(R.dimen.new_conversation_button_bottom_offset) }
|
||||||
@ -252,7 +253,8 @@ class NewConversationButtonSetView : RelativeLayout {
|
|||||||
expandedButton?.collapse()
|
expandedButton?.collapse()
|
||||||
this.expandedButton = null
|
this.expandedButton = null
|
||||||
collapse()
|
collapse()
|
||||||
if (event.action == MotionEvent.ACTION_UP) {
|
val distanceFromRestPosition = touch.distanceTo(buttonRestPosition)
|
||||||
|
if (event.action == MotionEvent.ACTION_UP && distanceFromRestPosition > (minDragDistance + mainButton.collapsedSize / 2)) {
|
||||||
if (sessionButton.contains(touch) || touch.isAbove(sessionButton, dragMargin)) { delegate?.createNewPrivateChat() }
|
if (sessionButton.contains(touch) || touch.isAbove(sessionButton, dragMargin)) { delegate?.createNewPrivateChat() }
|
||||||
else if (closedGroupButton.contains(touch) || touch.isRightOf(closedGroupButton, dragMargin)) { delegate?.createNewClosedGroup() }
|
else if (closedGroupButton.contains(touch) || touch.isRightOf(closedGroupButton, dragMargin)) { delegate?.createNewClosedGroup() }
|
||||||
else if (openGroupButton.contains(touch) || touch.isLeftOf(openGroupButton, dragMargin)) { delegate?.joinOpenGroup() }
|
else if (openGroupButton.contains(touch) || touch.isLeftOf(openGroupButton, dragMargin)) { delegate?.joinOpenGroup() }
|
||||||
|
@ -14,6 +14,7 @@ import android.support.v7.preference.ListPreference;
|
|||||||
import android.support.v7.preference.Preference;
|
import android.support.v7.preference.Preference;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
|
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||||
@ -32,6 +33,16 @@ public class NotificationsPreferenceFragment extends ListSummaryPreferenceFragme
|
|||||||
public void onCreate(Bundle paramBundle) {
|
public void onCreate(Bundle paramBundle) {
|
||||||
super.onCreate(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);
|
Preference ledBlinkPref = this.findPreference(TextSecurePreferences.LED_BLINK_PREF);
|
||||||
|
|
||||||
if (NotificationChannels.supported()) {
|
if (NotificationChannels.supported()) {
|
||||||
|
@ -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.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,6 @@ import com.google.protobuf.ByteString;
|
|||||||
import org.thoughtcrime.securesms.database.Address;
|
import org.thoughtcrime.securesms.database.Address;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
@ -159,7 +158,7 @@ public class GroupUtil {
|
|||||||
|
|
||||||
@NonNull private final Context context;
|
@NonNull private final Context context;
|
||||||
@Nullable private final GroupContext groupContext;
|
@Nullable private final GroupContext groupContext;
|
||||||
private final List<Recipient> members;
|
private final List<Recipient> newMembers;
|
||||||
private final List<Recipient> removedMembers;
|
private final List<Recipient> removedMembers;
|
||||||
private boolean ourDeviceWasRemoved;
|
private boolean ourDeviceWasRemoved;
|
||||||
|
|
||||||
@ -167,35 +166,35 @@ public class GroupUtil {
|
|||||||
this.context = context.getApplicationContext();
|
this.context = context.getApplicationContext();
|
||||||
this.groupContext = groupContext;
|
this.groupContext = groupContext;
|
||||||
|
|
||||||
this.members = new LinkedList<>();
|
this.newMembers = new LinkedList<>();
|
||||||
this.removedMembers = new LinkedList<>();
|
this.removedMembers = new LinkedList<>();
|
||||||
this.ourDeviceWasRemoved = false;
|
this.ourDeviceWasRemoved = false;
|
||||||
|
|
||||||
if (groupContext != null && !groupContext.getMembersList().isEmpty()) {
|
if (groupContext != null) {
|
||||||
List<String> memberList = groupContext.getMembersList();
|
List<String> newMembers = groupContext.getNewMembersList();
|
||||||
List<Address> currentMembers = getCurrentGroupMembers();
|
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<String> removedMembers = groupContext.getRemovedMembersList();
|
||||||
|
for (String member : removedMembers) {
|
||||||
|
this.removedMembers.add(this.toRecipient(member));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if our device was removed
|
// Check if our device was removed
|
||||||
if (!removedMembers.isEmpty()) {
|
if (!removedMembers.isEmpty()) {
|
||||||
String masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
String masterHexEncodedPublicKey = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
|
||||||
String hexEncodedPublicKey = masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : TextSecurePreferences.getLocalNumber(context);
|
String hexEncodedPublicKey = masterHexEncodedPublicKey != null ? masterHexEncodedPublicKey : TextSecurePreferences.getLocalNumber(context);
|
||||||
Recipient self = Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false);
|
ourDeviceWasRemoved = removedMembers.contains(hexEncodedPublicKey);
|
||||||
ourDeviceWasRemoved = removedMembers.contains(self);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Recipient toRecipient(String hexEncodedPublicKey) {
|
||||||
|
Address address = Address.fromSerialized(hexEncodedPublicKey);
|
||||||
|
return Recipient.from(context, address, false);
|
||||||
|
}
|
||||||
|
|
||||||
public String toString(Recipient sender) {
|
public String toString(Recipient sender) {
|
||||||
// Show the local removed message
|
// Show the local removed message
|
||||||
if (ourDeviceWasRemoved) {
|
if (ourDeviceWasRemoved) {
|
||||||
@ -211,10 +210,10 @@ public class GroupUtil {
|
|||||||
|
|
||||||
String title = groupContext.getName();
|
String title = groupContext.getName();
|
||||||
|
|
||||||
if (!members.isEmpty()) {
|
if (!newMembers.isEmpty()) {
|
||||||
description.append("\n");
|
description.append("\n");
|
||||||
description.append(context.getResources().getQuantityString(R.plurals.GroupUtil_joined_the_group,
|
description.append(context.getResources().getQuantityString(R.plurals.GroupUtil_joined_the_group,
|
||||||
members.size(), toString(members)));
|
newMembers.size(), toString(newMembers)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!removedMembers.isEmpty()) {
|
if (!removedMembers.isEmpty()) {
|
||||||
@ -224,8 +223,8 @@ public class GroupUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (title != null && !title.trim().isEmpty()) {
|
if (title != null && !title.trim().isEmpty()) {
|
||||||
if (!members.isEmpty()) description.append(" ");
|
String separator = (!newMembers.isEmpty() || !removedMembers.isEmpty()) ? " " : "\n";
|
||||||
else description.append("\n");
|
description.append(separator);
|
||||||
description.append(context.getString(R.string.GroupUtil_group_name_is_now, title));
|
description.append(context.getString(R.string.GroupUtil_group_name_is_now, title));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,8 +232,8 @@ public class GroupUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addListener(RecipientModifiedListener listener) {
|
public void addListener(RecipientModifiedListener listener) {
|
||||||
if (!this.members.isEmpty()) {
|
if (!this.newMembers.isEmpty()) {
|
||||||
for (Recipient member : this.members) {
|
for (Recipient member : this.newMembers) {
|
||||||
member.addListener(listener);
|
member.addListener(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -252,23 +251,5 @@ public class GroupUtil {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Address> 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,6 +185,45 @@ public class TextSecurePreferences {
|
|||||||
|
|
||||||
private static final String MEDIA_KEYBOARD_MODE = "pref_media_keyboard_mode";
|
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) {
|
public static boolean isScreenLockEnabled(@NonNull Context context) {
|
||||||
return getBooleanPreference(context, SCREEN_LOCK, false);
|
return getBooleanPreference(context, SCREEN_LOCK, false);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user