Compare commits
474 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c8a99c79c | ||
|
|
c6b9855198 | ||
|
|
c142928fad | ||
|
|
fc0cfd5188 | ||
|
|
d9c78e5c3e | ||
|
|
b449fceca0 | ||
|
|
f0d15c0bce | ||
|
|
8f031f61ea | ||
|
|
502e8559f0 | ||
|
|
8cf6f7e936 | ||
|
|
0ef01cc620 | ||
|
|
930828ef86 | ||
|
|
9ebe920195 | ||
|
|
0df36047e7 | ||
|
|
94604921f9 | ||
|
|
284fe294ac | ||
|
|
ffa01d491a | ||
|
|
1d9513e743 | ||
|
|
277c9e22f1 | ||
|
|
4e7b4da941 | ||
|
|
7e72c9c33b | ||
|
|
e0e2c3a3f5 | ||
|
|
3b5e444e76 | ||
|
|
02006e3ff5 | ||
|
|
8083596f19 | ||
|
|
6551689a0c | ||
|
|
3fbf21a34e | ||
|
|
b598431237 | ||
|
|
3b5d9a2cae | ||
|
|
3bd8aa8a86 | ||
|
|
fe5fca8eaf | ||
|
|
848101a783 | ||
|
|
d7c350f987 | ||
|
|
4c526f0b3c | ||
|
|
876ffb5b13 | ||
|
|
0b14cf3d6a | ||
|
|
c2044b36b1 | ||
|
|
f970b4acfa | ||
|
|
e69c0af613 | ||
|
|
42e2dd8b30 | ||
|
|
4fd6e7b033 | ||
|
|
3580816eac | ||
|
|
f1c2e5f194 | ||
|
|
b6d59f1d46 | ||
|
|
a3521681e7 | ||
|
|
ca10e1136c | ||
|
|
282fb2af0e | ||
|
|
1f5a597d50 | ||
|
|
a32666817c | ||
|
|
b9bd3f2b4c | ||
|
|
4173efbe5a | ||
|
|
8121c8bd41 | ||
|
|
6ca8218d55 | ||
|
|
653afbdd46 | ||
|
|
4453d1752f | ||
|
|
5782c8a58b | ||
|
|
3e041befc8 | ||
|
|
6d3847c26f | ||
|
|
4bdfc37bc1 | ||
|
|
28afe0d829 | ||
|
|
04dddd3378 | ||
|
|
23a14583fe | ||
|
|
19c83de510 | ||
|
|
e94d4b64cf | ||
|
|
7380c99f3a | ||
|
|
2961a372c3 | ||
|
|
5e2a4fb058 | ||
|
|
a16242b9f8 | ||
|
|
acfba9ac96 | ||
|
|
006343460e | ||
|
|
1cc8634cc7 | ||
|
|
9f8a110428 | ||
|
|
07b4279d0b | ||
|
|
6a33b231e3 | ||
|
|
b38d02061d | ||
|
|
f832a36a5e | ||
|
|
911ca7c29d | ||
|
|
544b75a2a7 | ||
|
|
56e8d4fb06 | ||
|
|
36a2278aef | ||
|
|
0c785b85b8 | ||
|
|
a0ecba147e | ||
|
|
977591ac82 | ||
|
|
fe1838d3fe | ||
|
|
ede06cf97d | ||
|
|
93deee6824 | ||
|
|
db19077834 | ||
|
|
6f91f62db2 | ||
|
|
1fc63b7597 | ||
|
|
a079e479ec | ||
|
|
e90705b459 | ||
|
|
244db437cb | ||
|
|
6558eae032 | ||
|
|
88b54a262b | ||
|
|
4fa8b8a4bd | ||
|
|
cc0ced9a81 | ||
|
|
52447f5e97 | ||
|
|
bceb69b284 | ||
|
|
a8d826020d | ||
|
|
2ada7f87f2 | ||
|
|
7f8ca58762 | ||
|
|
058c25808b | ||
|
|
c1e6b6b086 | ||
|
|
b0a6bb79f6 | ||
|
|
de52bf50a2 | ||
|
|
207dd23c86 | ||
|
|
7e0de29dd7 | ||
|
|
eaa8f1ee8f | ||
|
|
fb494c1151 | ||
|
|
49ecd9ef5d | ||
|
|
8772214fd4 | ||
|
|
5b74d0cbeb | ||
|
|
1e7c93007d | ||
|
|
608815a69b | ||
|
|
fb49efa34d | ||
|
|
b20e8616ec | ||
|
|
acf78b6b63 | ||
|
|
d68fe928b8 | ||
|
|
db4d561d9e | ||
|
|
6933ca50a7 | ||
|
|
88c0e6f8ab | ||
|
|
6cd4728e3c | ||
|
|
97cc82837c | ||
|
|
5f0b0e8495 | ||
|
|
c837d590ab | ||
|
|
6108a32631 | ||
|
|
6d78e1760d | ||
|
|
c7b7242eff | ||
|
|
77c687efcf | ||
|
|
274af2f010 | ||
|
|
6cd5100530 | ||
|
|
115a408b0b | ||
|
|
d983016137 | ||
|
|
a8309b6b2b | ||
|
|
e241589c92 | ||
|
|
dcb6240a6a | ||
|
|
599bb5ab0f | ||
|
|
739fe584c6 | ||
|
|
920d1c6207 | ||
|
|
c1d3d26a15 | ||
|
|
e3b66dc7e8 | ||
|
|
dfa08d1356 | ||
|
|
6da734c2f9 | ||
|
|
a9f3141a77 | ||
|
|
ee267d4702 | ||
|
|
c39174ed8a | ||
|
|
9c872b6da6 | ||
|
|
2924a09936 | ||
|
|
b9da012cc4 | ||
|
|
8626d41787 | ||
|
|
c58705722a | ||
|
|
c702ff676a | ||
|
|
995569dd50 | ||
|
|
a0dfd42527 | ||
|
|
a7dd78cce6 | ||
|
|
e2e9cd40b3 | ||
|
|
e4fc6f41b8 | ||
|
|
778b2a0d27 | ||
|
|
64cd15ca0b | ||
|
|
1e46c3c0ba | ||
|
|
91772b4e11 | ||
|
|
af5c7cb7ca | ||
|
|
e760474fa9 | ||
|
|
28bb88e347 | ||
|
|
17a1fe97ca | ||
|
|
7348237862 | ||
|
|
4e66db5dc1 | ||
|
|
ffa6c9acd1 | ||
|
|
b3e66a9259 | ||
|
|
5c0cf8c1f0 | ||
|
|
89c716fca4 | ||
|
|
aae9830dfa | ||
|
|
48c72697c1 | ||
|
|
aac7e9ea53 | ||
|
|
a1b10b3222 | ||
|
|
2ab8db33e3 | ||
|
|
2b1386232f | ||
|
|
7bb1caa22e | ||
|
|
72c14b8651 | ||
|
|
f85c3bb1e6 | ||
|
|
d8714fe3b4 | ||
|
|
c54c1907b6 | ||
|
|
55257155dd | ||
|
|
55a8bd86de | ||
|
|
c60909272b | ||
|
|
3dcc2d8171 | ||
|
|
0ccbb22e4c | ||
|
|
0ffa10eaea | ||
|
|
9824073023 | ||
|
|
d2adc11a2d | ||
|
|
4d0b870c88 | ||
|
|
840997cb7d | ||
|
|
e4406621ed | ||
|
|
7f96ee0b62 | ||
|
|
17f850b31a | ||
|
|
5d87ad0301 | ||
|
|
a33eeed6d3 | ||
|
|
554ce29337 | ||
|
|
6054d0b36f | ||
|
|
8ba15cf3b4 | ||
|
|
b15e5b4867 | ||
|
|
544511905a | ||
|
|
02e3f511bd | ||
|
|
0539b071e7 | ||
|
|
36e400650c | ||
|
|
d8930daf4b | ||
|
|
17b5816d5b | ||
|
|
e574c81ea2 | ||
|
|
6799a2f841 | ||
|
|
61fb20f412 | ||
|
|
47f648e7bc | ||
|
|
064c0ddb82 | ||
|
|
b42c42007d | ||
|
|
807cdfce2e | ||
|
|
43dc3aeebd | ||
|
|
0369c5ee16 | ||
|
|
3df2390fe4 | ||
|
|
9ec4a7af0f | ||
|
|
70636fb4a7 | ||
|
|
3c4efdd8f9 | ||
|
|
80deb301e5 | ||
|
|
5eabfdc34c | ||
|
|
5e96832666 | ||
|
|
1ccce24cf8 | ||
|
|
d59e4f2da7 | ||
|
|
a254c1a7b2 | ||
|
|
82cc610938 | ||
|
|
becf7c40e8 | ||
|
|
ed21c3ca03 | ||
|
|
631005e565 | ||
|
|
b57fab8c75 | ||
|
|
4260a8436b | ||
|
|
23d478191f | ||
|
|
d075a33d4e | ||
|
|
7507dadbe7 | ||
|
|
bfd0363390 | ||
|
|
cfb22825f4 | ||
|
|
d37fafbfe7 | ||
|
|
2dc2eb5835 | ||
|
|
019d036f69 | ||
|
|
f1ca5fc8e2 | ||
|
|
9089fc4001 | ||
|
|
b281b817ba | ||
|
|
cee6736656 | ||
|
|
350ca059b9 | ||
|
|
a7cc5bdc5e | ||
|
|
2893dfff60 | ||
|
|
1c14f06734 | ||
|
|
ff053437e3 | ||
|
|
03dc220cea | ||
|
|
097f97b5e4 | ||
|
|
95d3db3260 | ||
|
|
0485ae603b | ||
|
|
2f93da6f1a | ||
|
|
78f3e28974 | ||
|
|
fecd8300c3 | ||
|
|
262f90dbe7 | ||
|
|
ed271c6f3e | ||
|
|
3fa5843c93 | ||
|
|
7f544cb3e5 | ||
|
|
9f4d40a364 | ||
|
|
09f0c5b63f | ||
|
|
53e0dc5dee | ||
|
|
8b06051e7c | ||
|
|
660ec88202 | ||
|
|
b9057a1c11 | ||
|
|
1f85b1f3d2 | ||
|
|
71f78f03f4 | ||
|
|
4b3d129097 | ||
|
|
daeb823399 | ||
|
|
c7f76c5d1c | ||
|
|
9ba1391a1e | ||
|
|
5d137465e8 | ||
|
|
78cbb3c073 | ||
|
|
097d95428a | ||
|
|
2cfb6ede7f | ||
|
|
695663a5b1 | ||
|
|
a2204c8370 | ||
|
|
8c4757ea02 | ||
|
|
85821274fa | ||
|
|
a8b1ec8e52 | ||
|
|
adbdaebd69 | ||
|
|
c2da4fcd7d | ||
|
|
46ebff3659 | ||
|
|
452ccd0350 | ||
|
|
6e6c809690 | ||
|
|
7b5c1904cf | ||
|
|
c0aa9d7587 | ||
|
|
5d03e3d516 | ||
|
|
2bc3a4417f | ||
|
|
59d03cbeb2 | ||
|
|
f9f9ae68f5 | ||
|
|
88f2b67984 | ||
|
|
44dba4bd98 | ||
|
|
ef585eba42 | ||
|
|
42967dab13 | ||
|
|
4f75d1a5db | ||
|
|
debacbdf58 | ||
|
|
12c4283c30 | ||
|
|
096c9e0ff7 | ||
|
|
2f23a13a6f | ||
|
|
bdadbaaac4 | ||
|
|
16f41555ba | ||
|
|
8c037456e7 | ||
|
|
feccee557e | ||
|
|
67f5605bc4 | ||
|
|
ccb18cd46c | ||
|
|
5b1d91016c | ||
|
|
27ab04ab41 | ||
|
|
9432a45b39 | ||
|
|
9e3475ed94 | ||
|
|
86d088bce2 | ||
|
|
a135e7efa2 | ||
|
|
c247935f1a | ||
|
|
31657f5c15 | ||
|
|
bcecc30d33 | ||
|
|
7193252d77 | ||
|
|
5b682a3a3d | ||
|
|
6b8659a393 | ||
|
|
14557d3dc1 | ||
|
|
03cbee0277 | ||
|
|
adc0907906 | ||
|
|
f14a71a076 | ||
|
|
12c2b53f7c | ||
|
|
ff60b5b731 | ||
|
|
43954a176a | ||
|
|
d698d3bd6f | ||
|
|
50a81c0e60 | ||
|
|
b0b8377a8e | ||
|
|
7d02bb8487 | ||
|
|
8449d75684 | ||
|
|
b89163bb14 | ||
|
|
b7ce220600 | ||
|
|
948079a42e | ||
|
|
de1c6cdd0c | ||
|
|
37147fb0a5 | ||
|
|
33334f80c3 | ||
|
|
36286da9bd | ||
|
|
2248acb9f2 | ||
|
|
3632a2cc95 | ||
|
|
9447ea12cb | ||
|
|
89c2329fdf | ||
|
|
84012c7adb | ||
|
|
d7395af774 | ||
|
|
be4daff0f3 | ||
|
|
c2459d0a31 | ||
|
|
5f993ed0f7 | ||
|
|
acea24c19c | ||
|
|
82b5d58d04 | ||
|
|
af2990fa08 | ||
|
|
0fa48540e1 | ||
|
|
0f06b96832 | ||
|
|
4f423be6f9 | ||
|
|
bea21ed5ff | ||
|
|
a9cff032f5 | ||
|
|
b2d02d4ace | ||
|
|
066df77abf | ||
|
|
b38a3e6259 | ||
|
|
d78919acf8 | ||
|
|
af96d11188 | ||
|
|
5e1bef26ed | ||
|
|
95333eccd4 | ||
|
|
89e075c56e | ||
|
|
d026498a8c | ||
|
|
bae381e6f8 | ||
|
|
050f7a24c9 | ||
|
|
77ad1ea729 | ||
|
|
bf667c0cfc | ||
|
|
ccb8ef98b4 | ||
|
|
a1f0660339 | ||
|
|
447236ee38 | ||
|
|
ebf3b0dfe1 | ||
|
|
ee9216df8a | ||
|
|
3e34668232 | ||
|
|
8f8f41f184 | ||
|
|
4f7cba8d7c | ||
|
|
00b719c783 | ||
|
|
b03eccec33 | ||
|
|
5805539deb | ||
|
|
1b48fd07a3 | ||
|
|
1e375ec494 | ||
|
|
ee9acf2687 | ||
|
|
1d8e85fcad | ||
|
|
4e2afa7362 | ||
|
|
d06564c7b9 | ||
|
|
ced48d0788 | ||
|
|
e273593343 | ||
|
|
c5767b07a7 | ||
|
|
709ce7e9de | ||
|
|
a8f7908ed4 | ||
|
|
8230786638 | ||
|
|
df4ecc4e32 | ||
|
|
5b755b9501 | ||
|
|
b3c7c8db5c | ||
|
|
a21c537428 | ||
|
|
6d339cd023 | ||
|
|
840c493265 | ||
|
|
14999800e2 | ||
|
|
8e332d0798 | ||
|
|
cd007a20d7 | ||
|
|
8b99af3eef | ||
|
|
d354de806e | ||
|
|
c7ef0c06f8 | ||
|
|
9d14bcb808 | ||
|
|
de03706d8d | ||
|
|
a3caabcafd | ||
|
|
faafa40122 | ||
|
|
d1a6582ad7 | ||
|
|
f81c0b448e | ||
|
|
70347e754c | ||
|
|
a1245baf61 | ||
|
|
007ea43dc8 | ||
|
|
582028f2c2 | ||
|
|
0e2d52026e | ||
|
|
233cc7ecce | ||
|
|
1baf03f51a | ||
|
|
f066a9cab2 | ||
|
|
825d1a02ab | ||
|
|
97cc8b7777 | ||
|
|
72662b5b52 | ||
|
|
ea4e0b6d6f | ||
|
|
6df93c0bb5 | ||
|
|
23a0bb3ce0 | ||
|
|
4cd5256267 | ||
|
|
5362e07a5c | ||
|
|
936bd327bd | ||
|
|
db89619a4a | ||
|
|
b5ce7325fc | ||
|
|
ccaeb089ab | ||
|
|
3240ba3a55 | ||
|
|
bb7be66efe | ||
|
|
8814a0d949 | ||
|
|
9aa488223f | ||
|
|
30d9233365 | ||
|
|
3ac540c687 | ||
|
|
8d561ead21 | ||
|
|
15b650382e | ||
|
|
110a40592b | ||
|
|
d0ce4ff032 | ||
|
|
85c9a9050a | ||
|
|
af42d5b671 | ||
|
|
9580bb0a38 | ||
|
|
9abb167874 | ||
|
|
cd13676a21 | ||
|
|
1dd59bee36 | ||
|
|
59bcbe592b | ||
|
|
0917e17c69 | ||
|
|
eb249ca69a | ||
|
|
97d1175915 | ||
|
|
2141f1073e | ||
|
|
c6287547a3 | ||
|
|
9257c6ddf3 | ||
|
|
480748e1aa | ||
|
|
c015687951 | ||
|
|
1bd1e9cc65 | ||
|
|
c4a4374465 | ||
|
|
90681d47f8 | ||
|
|
936be693ce | ||
|
|
a32b875587 | ||
|
|
4e6798e38e | ||
|
|
6352f7baf4 | ||
|
|
aac9725adc | ||
|
|
900371bb30 | ||
|
|
a58f564d1e | ||
|
|
942154a61f | ||
|
|
7f0a7b0c13 | ||
|
|
37bcac40bb | ||
|
|
02ea99254a | ||
|
|
3849b46f0a | ||
|
|
116bd41c63 | ||
|
|
457ad4c607 | ||
|
|
d0a9bd4c6d | ||
|
|
d3bed549f2 | ||
|
|
fe1aa016b9 |
17
.github/workflows/android.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Android CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew qa
|
||||
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
.classpath
|
||||
captures/
|
||||
project.properties
|
||||
.project
|
||||
.settings
|
||||
|
||||
@@ -62,7 +62,7 @@ The form and manner of this distribution makes it eligible for export under the
|
||||
|
||||
Copyright 2011 Whisper Systems
|
||||
|
||||
Copyright 2013-2017 Open Whisper Systems
|
||||
Copyright 2013-2020 Open Whisper Systems
|
||||
|
||||
Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ host = https://www.transifex.com
|
||||
lang_map = da_DK:da-rDK,he:iw,id:in,kn_IN:kn-rIN,pt_BR:pt-rBR,pt_PT:pt,qu_EC:qu-rEC,sv_SE:sv-rSE,zh_CN:zh-rCN,zh_HK:zh-rHK,zh_TW:zh-rTW
|
||||
|
||||
[signal-android.master]
|
||||
file_filter = res/values-<lang>/strings.xml
|
||||
source_file = res/values/strings.xml
|
||||
file_filter = src/main/res/values-<lang>/strings.xml
|
||||
source_file = src/main/res/values/strings.xml
|
||||
source_lang = en
|
||||
type = ANDROID
|
||||
|
||||
436
app/build.gradle
Normal file
@@ -0,0 +1,436 @@
|
||||
import org.signal.signing.ApkSignerUtil
|
||||
|
||||
import java.security.MessageDigest
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
maven {
|
||||
url "https://repo1.maven.org/maven2"
|
||||
}
|
||||
jcenter {
|
||||
content {
|
||||
includeVersion 'org.jetbrains.trove4j', 'trove4j', '20160824'
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.1'
|
||||
classpath 'androidx.navigation:navigation-safe-args-gradle-plugin:2.1.0'
|
||||
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.10'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'com.google.protobuf'
|
||||
apply plugin: 'androidx.navigation.safeargs'
|
||||
apply plugin: 'witness'
|
||||
apply from: 'translations.gradle'
|
||||
apply from: 'witness-verifications.gradle'
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url "https://raw.github.com/signalapp/maven/master/photoview/releases/"
|
||||
content {
|
||||
includeGroupByRegex "com\\.github\\.chrisbanes.*"
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url "https://raw.github.com/signalapp/maven/master/shortcutbadger/releases/"
|
||||
content {
|
||||
includeGroupByRegex "me\\.leolin.*"
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url "https://raw.github.com/signalapp/maven/master/circular-progress-button/releases/"
|
||||
content {
|
||||
includeGroupByRegex "com\\.github\\.dmytrodanylyk\\.circular-progress-button\\.*"
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url "https://raw.github.com/signalapp/maven/master/sqlcipher/release/"
|
||||
content {
|
||||
includeGroupByRegex "org\\.signal.*"
|
||||
}
|
||||
}
|
||||
maven { // textdrawable
|
||||
url 'https://dl.bintray.com/amulyakhare/maven'
|
||||
content {
|
||||
includeGroupByRegex "com\\.amulyakhare.*"
|
||||
}
|
||||
}
|
||||
google()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
protobuf {
|
||||
protoc {
|
||||
artifact = 'com.google.protobuf:protoc:3.10.0'
|
||||
}
|
||||
generateProtoTasks {
|
||||
all().each { task ->
|
||||
task.builtins {
|
||||
java {
|
||||
option "lite"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0-beta01'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.0.0'
|
||||
implementation 'androidx.legacy:legacy-support-v13:1.0.0'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.preference:preference:1.0.0'
|
||||
implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.0.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation 'androidx.navigation:navigation-fragment:2.1.0'
|
||||
implementation 'androidx.navigation:navigation-ui:2.1.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:1.0.0-alpha05'
|
||||
implementation 'androidx.lifecycle:lifecycle-common-java8:2.1.0'
|
||||
implementation "androidx.camera:camera-core:1.0.0-alpha06"
|
||||
implementation "androidx.camera:camera-camera2:1.0.0-alpha06"
|
||||
|
||||
implementation('com.google.firebase:firebase-messaging:17.3.4') {
|
||||
exclude group: 'com.google.firebase', module: 'firebase-core'
|
||||
exclude group: 'com.google.firebase', module: 'firebase-analytics'
|
||||
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
|
||||
}
|
||||
|
||||
implementation 'com.google.android.gms:play-services-maps:16.1.0'
|
||||
implementation 'com.google.android.gms:play-services-auth:16.0.1'
|
||||
|
||||
implementation 'com.google.android.exoplayer:exoplayer-core:2.9.1'
|
||||
implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.1'
|
||||
|
||||
implementation 'org.conscrypt:conscrypt-android:2.0.0'
|
||||
implementation 'org.signal:aesgcmprovider:0.0.3'
|
||||
|
||||
implementation project(':libsignal-service')
|
||||
|
||||
implementation 'org.signal:ringrtc-android:0.3.1'
|
||||
|
||||
implementation "me.leolin:ShortcutBadger:1.1.16"
|
||||
implementation 'se.emilsjolander:stickylistheaders:2.7.0'
|
||||
implementation 'com.jpardogo.materialtabstrip:library:1.0.9'
|
||||
implementation 'org.apache.httpcomponents:httpclient-android:4.3.5'
|
||||
implementation 'com.github.chrisbanes:PhotoView:2.1.3'
|
||||
implementation 'com.github.bumptech.glide:glide:4.9.0'
|
||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
|
||||
annotationProcessor 'androidx.annotation:annotation:1.1.0'
|
||||
implementation 'com.makeramen:roundedimageview:2.1.0'
|
||||
implementation 'com.pnikosis:materialish-progress:1.5'
|
||||
implementation 'org.greenrobot:eventbus:3.0.0'
|
||||
implementation 'pl.tajchert:waitingdots:0.1.0'
|
||||
implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0'
|
||||
implementation 'com.melnykov:floatingactionbutton:1.3.0'
|
||||
implementation 'com.google.zxing:android-integration:3.1.0'
|
||||
implementation 'mobi.upod:time-duration-picker:1.1.3'
|
||||
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
||||
implementation 'com.google.zxing:core:3.2.1'
|
||||
implementation ('com.davemorrissey.labs:subsampling-scale-image-view:3.6.0') {
|
||||
exclude group: 'com.android.support', module: 'support-annotations'
|
||||
}
|
||||
implementation ('cn.carbswang.android:NumberPickerView:1.0.9') {
|
||||
exclude group: 'com.android.support', module: 'appcompat-v7'
|
||||
}
|
||||
implementation ('com.tomergoldst.android:tooltips:1.0.6') {
|
||||
exclude group: 'com.android.support', module: 'appcompat-v7'
|
||||
}
|
||||
implementation ('com.klinkerapps:android-smsmms:4.0.1') {
|
||||
exclude group: 'com.squareup.okhttp', module: 'okhttp'
|
||||
exclude group: 'com.squareup.okhttp', module: 'okhttp-urlconnection'
|
||||
}
|
||||
implementation 'com.annimon:stream:1.1.8'
|
||||
implementation ('com.takisoft.fix:colorpicker:0.9.1') {
|
||||
exclude group: 'com.android.support', module: 'appcompat-v7'
|
||||
exclude group: 'com.android.support', module: 'recyclerview-v7'
|
||||
}
|
||||
|
||||
implementation 'com.airbnb.android:lottie:3.0.7'
|
||||
|
||||
implementation 'com.codewaves.stickyheadergrid:stickyheadergrid:0.9.4'
|
||||
implementation 'com.github.dmytrodanylyk.circular-progress-button:library:1.1.3-S2'
|
||||
implementation 'org.signal:android-database-sqlcipher:3.5.9-S3'
|
||||
implementation ('com.googlecode.ez-vcard:ez-vcard:0.9.11') {
|
||||
exclude group: 'com.fasterxml.jackson.core'
|
||||
exclude group: 'org.freemarker'
|
||||
}
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'org.assertj:assertj-core:3.11.1'
|
||||
testImplementation 'org.mockito:mockito-core:1.9.5'
|
||||
testImplementation 'org.powermock:powermock-api-mockito:1.6.1'
|
||||
testImplementation 'org.powermock:powermock-module-junit4:1.6.1'
|
||||
testImplementation 'org.powermock:powermock-module-junit4-rule:1.6.1'
|
||||
testImplementation 'org.powermock:powermock-classloading-xstream:1.6.1'
|
||||
|
||||
testImplementation 'androidx.test:core:1.2.0'
|
||||
androidTestImplementation 'androidx.multidex:multidex:2.0.1'
|
||||
androidTestImplementation 'androidx.multidex:multidex-instrumentation:2.0.0'
|
||||
androidTestImplementation 'com.google.dexmaker:dexmaker:1.2'
|
||||
androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:1.2'
|
||||
androidTestImplementation ('org.assertj:assertj-core:1.7.1') {
|
||||
exclude group: 'org.hamcrest', module: 'hamcrest-core'
|
||||
}
|
||||
androidTestImplementation ('com.squareup.assertj:assertj-android:1.1.1') {
|
||||
exclude group: 'org.hamcrest', module: 'hamcrest-core'
|
||||
exclude group: 'com.android.support', module: 'support-annotations'
|
||||
}
|
||||
testImplementation 'org.robolectric:robolectric:4.2'
|
||||
testImplementation 'org.robolectric:shadows-multidex:4.2'
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
configuration = '(play|website)(Debug|Release)RuntimeClasspath'
|
||||
}
|
||||
|
||||
def canonicalVersionCode = 587
|
||||
def canonicalVersionName = "4.53.2"
|
||||
|
||||
def postFixSize = 10
|
||||
def abiPostFix = ['universal' : 0,
|
||||
'armeabi-v7a' : 1,
|
||||
'arm64-v8a' : 2,
|
||||
'x86' : 3,
|
||||
'x86_64' : 4]
|
||||
|
||||
android {
|
||||
flavorDimensions "none"
|
||||
compileSdkVersion 28
|
||||
buildToolsVersion '28.0.3'
|
||||
useLibrary 'org.apache.http.legacy'
|
||||
|
||||
dexOptions {
|
||||
javaMaxHeapSize "4g"
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
versionCode canonicalVersionCode * postFixSize
|
||||
versionName canonicalVersionName
|
||||
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 28
|
||||
multiDexEnabled true
|
||||
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
project.ext.set("archivesBaseName", "Signal");
|
||||
|
||||
buildConfigField "long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L"
|
||||
buildConfigField "String", "SIGNAL_URL", "\"https://textsecure-service.whispersystems.org\""
|
||||
buildConfigField "String", "STORAGE_URL", "\"https://storage.signal.org\""
|
||||
buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn.signal.org\""
|
||||
buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://api.directory.signal.org\""
|
||||
buildConfigField "String", "SIGNAL_SERVICE_STATUS_URL", "\"uptime.signal.org\""
|
||||
buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api.backup.signal.org\""
|
||||
buildConfigField "String", "CONTENT_PROXY_HOST", "\"contentproxy.signal.org\""
|
||||
buildConfigField "int", "CONTENT_PROXY_PORT", "443"
|
||||
buildConfigField "String", "USER_AGENT", "\"OWA\""
|
||||
buildConfigField "boolean", "DEV_BUILD", "false"
|
||||
buildConfigField "String", "MRENCLAVE", "\"cd6cfc342937b23b1bdd3bbf9721aa5615ac9ff50a75c5527d441cd3276826c9\""
|
||||
buildConfigField "String", "KEY_BACKUP_ENCLAVE_NAME", "\"f2e2a5004794a6c1bac5c4949eadbc243dd02e02d1a93f10fe24584fb70815d8\""
|
||||
buildConfigField "String", "KEY_BACKUP_MRENCLAVE", "\"f51f435802ada769e67aaf5744372bb7e7d519eecf996d335eb5b46b872b5789\""
|
||||
buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\""
|
||||
buildConfigField "String[]", "LANGUAGES", "new String[]{\"" + autoResConfig().collect { s -> s.replace('-r', '_') }.join('", "') + '"}'
|
||||
buildConfigField "int", "CANONICAL_VERSION_CODE", "$canonicalVersionCode"
|
||||
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||
}
|
||||
|
||||
resConfigs autoResConfig()
|
||||
|
||||
splits {
|
||||
abi {
|
||||
enable true
|
||||
reset()
|
||||
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||
universalApk true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'LICENSE.txt'
|
||||
exclude 'LICENSE'
|
||||
exclude 'NOTICE'
|
||||
exclude 'asm-license.txt'
|
||||
exclude 'META-INF/LICENSE'
|
||||
exclude 'META-INF/NOTICE'
|
||||
exclude 'META-INF/proguard/androidx-annotations.pro'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'),
|
||||
'proguard/proguard-firebase-messaging.pro',
|
||||
'proguard/proguard-google-play-services.pro',
|
||||
'proguard/proguard-jackson.pro',
|
||||
'proguard/proguard-sqlite.pro',
|
||||
'proguard/proguard-appcompat-v7.pro',
|
||||
'proguard/proguard-square-okhttp.pro',
|
||||
'proguard/proguard-square-okio.pro',
|
||||
'proguard/proguard-spongycastle.pro',
|
||||
'proguard/proguard-rounded-image-view.pro',
|
||||
'proguard/proguard-glide.pro',
|
||||
'proguard/proguard-shortcutbadger.pro',
|
||||
'proguard/proguard-retrofit.pro',
|
||||
'proguard/proguard-webrtc.pro',
|
||||
'proguard/proguard-klinker.pro',
|
||||
'proguard/proguard-retrolambda.pro',
|
||||
'proguard/proguard-okhttp.pro',
|
||||
'proguard/proguard-ez-vcard.pro',
|
||||
'proguard/proguard.cfg'
|
||||
testProguardFiles 'proguard/proguard-automation.pro',
|
||||
'proguard/proguard.cfg'
|
||||
}
|
||||
staging {
|
||||
initWith debug
|
||||
|
||||
buildConfigField "String", "SIGNAL_URL", "\"https://textsecure-service-staging.whispersystems.org\""
|
||||
buildConfigField "String", "STORAGE_URL", "\"https://storage-staging.signal.org\""
|
||||
buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn-staging.signal.org\""
|
||||
buildConfigField "String", "SIGNAL_CONTACT_DISCOVERY_URL", "\"https://api-staging.directory.signal.org\""
|
||||
buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api-staging.backup.signal.org\""
|
||||
buildConfigField "String", "MRENCLAVE", "\"ba4ebb438bc07713819ee6c98d94037747006d7df63fc9e44d2d6f1fec962a79\""
|
||||
buildConfigField "String", "KEY_BACKUP_ENCLAVE_NAME", "\"b5a865941f95887018c86725cc92308d34a3084dc2b4e7bd2de5e5e1690b50c6\""
|
||||
buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\""
|
||||
}
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles = buildTypes.debug.proguardFiles
|
||||
}
|
||||
}
|
||||
|
||||
productFlavors {
|
||||
play {
|
||||
dimension "none"
|
||||
ext.websiteUpdateUrl = "null"
|
||||
buildConfigField "boolean", "PLAY_STORE_DISABLED", "false"
|
||||
buildConfigField "String", "NOPLAY_UPDATE_URL", "$ext.websiteUpdateUrl"
|
||||
}
|
||||
|
||||
website {
|
||||
dimension "none"
|
||||
ext.websiteUpdateUrl = "https://updates.signal.org/android"
|
||||
buildConfigField "boolean", "PLAY_STORE_DISABLED", "true"
|
||||
buildConfigField "String", "NOPLAY_UPDATE_URL", "\"$ext.websiteUpdateUrl\""
|
||||
}
|
||||
}
|
||||
|
||||
android.applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
output.outputFileName = output.outputFileName.replace(".apk", "-${variant.versionName}.apk")
|
||||
def abiName = output.getFilter("ABI") ?: 'universal'
|
||||
def postFix = abiPostFix.get(abiName, 0)
|
||||
|
||||
if (postFix >= postFixSize) throw new AssertionError("postFix is too large")
|
||||
|
||||
output.versionCodeOverride = canonicalVersionCode * postFixSize + postFix
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError true
|
||||
baseline file("lint-baseline.xml")
|
||||
disable "LintError"
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def assembleWebsiteDescriptor = { variant, file ->
|
||||
if (file.exists()) {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
file.eachByte 4096, {bytes, size ->
|
||||
md.update(bytes, 0, size);
|
||||
}
|
||||
|
||||
String digest = md.digest().collect {String.format "%02x", it}.join();
|
||||
String url = variant.productFlavors.get(0).ext.websiteUpdateUrl
|
||||
String apkName = file.getName()
|
||||
|
||||
String descriptor = "{" +
|
||||
"\"versionCode\" : ${canonicalVersionCode * postFixSize + abiPostFix['universal']}," +
|
||||
"\"versionName\" : \"$canonicalVersionName\"," +
|
||||
"\"sha256sum\" : \"$digest\"," +
|
||||
"\"url\" : \"$url/$apkName\"" +
|
||||
"}"
|
||||
|
||||
File descriptorFile = new File(file.getParent(), apkName.replace(".apk", ".json"))
|
||||
|
||||
descriptorFile.write(descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
def signProductionRelease = { variant ->
|
||||
variant.outputs.collect { output ->
|
||||
String apkName = output.outputFile.name
|
||||
File inputFile = new File(output.outputFile.path)
|
||||
File outputFile = new File(output.outputFile.parent, apkName.replace('-unsigned', ''))
|
||||
|
||||
new ApkSignerUtil('sun.security.pkcs11.SunPKCS11',
|
||||
'pkcs11.config',
|
||||
'PKCS11',
|
||||
'file:pkcs11.password').calculateSignature(inputFile.getAbsolutePath(),
|
||||
outputFile.getAbsolutePath())
|
||||
|
||||
inputFile.delete()
|
||||
outputFile
|
||||
}
|
||||
}
|
||||
|
||||
task signProductionPlayRelease {
|
||||
doLast {
|
||||
signProductionRelease(android.applicationVariants.find { (it.name == 'playRelease') })
|
||||
}
|
||||
}
|
||||
|
||||
task signProductionWebsiteRelease {
|
||||
doLast {
|
||||
def variant = android.applicationVariants.find { (it.name == 'websiteRelease') }
|
||||
File signedRelease = signProductionRelease(variant).find { it.name.contains('universal') }
|
||||
assembleWebsiteDescriptor(variant, signedRelease)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.whenTaskAdded { task ->
|
||||
if (task.name.equals("assemblePlayRelease")) {
|
||||
task.finalizedBy signProductionPlayRelease
|
||||
}
|
||||
|
||||
if (task.name.equals("assembleWebsiteRelease")) {
|
||||
task.finalizedBy signProductionWebsiteRelease
|
||||
}
|
||||
}
|
||||
|
||||
def getLastCommitTimestamp() {
|
||||
new ByteArrayOutputStream().withStream { os ->
|
||||
def result = exec {
|
||||
executable = 'git'
|
||||
args = ['log', '-1', '--pretty=format:%ct']
|
||||
standardOutput = os
|
||||
}
|
||||
|
||||
return os.toString() + "000"
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issues format="5" by="lint 3.3.2" client="gradle" variant="playRelease" version="3.3.2">
|
||||
|
||||
<issue
|
||||
id="LintError"
|
||||
message="Unexpected failure during lint analysis of JobManager.java (this is a bug in lint or one of the libraries it depends on)

Stack: `NullPointerException:ClsFileImpl.getMirror(ClsFileImpl.java:343)←ClsElementImpl.getMirror(ClsElementImpl.java:159)←ClsElementImpl.getText(ClsElementImpl.java:202)←InferenceSession.argConstraints(InferenceSession.java:1817)←InferenceSession.isFunctionalTypeMoreSpecific(InferenceSession.java:1748)←InferenceSession.isFunctionalTypeMoreSpecificOnExpression(InferenceSession.java:1729)←JavaMethodsConflictResolver.isFunctionalTypeMoreSpecific(JavaMethodsConflictResolver.java:779)←JavaMethodsConflictResolver.isTypeMoreSpecific(JavaMethodsConflictResolver.java:673)`

You can set environment variable `LINT_PRINT_STACKTRACE=true` to dump a full stacktrace to stdout.">
|
||||
<location
|
||||
file="src/org/thoughtcrime/securesms/jobmanager/JobManager.java"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="MissingPermission"
|
||||
message="Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with `checkPermission`) or explicitly handle a potential `SecurityException`"
|
||||
errorLine1=" List<SubscriptionInfo> list = subscriptionManager.getActiveSubscriptionInfoList();"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/org/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat.java"
|
||||
file="src/main/java/org/thoughtcrime/securesms/util/dualsim/SubscriptionManagerCompat.java"
|
||||
line="101"
|
||||
column="35"/>
|
||||
</issue>
|
||||
@@ -25,7 +18,7 @@
|
||||
errorLine1=" drawables.getColor(1, 0xff000000);"
|
||||
errorLine2=" ~">
|
||||
<location
|
||||
file="src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java"
|
||||
file="src/main/java/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java"
|
||||
line="187"
|
||||
column="36"/>
|
||||
</issue>
|
||||
@@ -12,5 +12,14 @@
|
||||
<issue id="ImpliedQuantity" severity="warning" />
|
||||
|
||||
<issue id="CanvasSize" severity="error" />
|
||||
<issue id="HardcodedText" severity="error" />
|
||||
<issue id="VectorRaster" severity="error" />
|
||||
<issue id="ButtonOrder" severity="error" />
|
||||
<issue id="ExtraTranslation" severity="warning" />
|
||||
|
||||
<issue id="RestrictedApi" severity="error">
|
||||
<ignore path="*/org/thoughtcrime/securesms/mediasend/camerax/VideoCapture.java" />
|
||||
<ignore path="*/org/thoughtcrime/securesms/mediasend/camerax/CameraXModule.java" />
|
||||
</issue>
|
||||
|
||||
</lint>
|
||||
@@ -1,4 +1,5 @@
|
||||
-dontoptimize
|
||||
-dontobfuscate
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
-keep class org.whispersystems.** { *; }
|
||||
-keep class org.thoughtcrime.securesms.** { *; }
|
||||
@@ -6,3 +7,5 @@
|
||||
public void onEvent*(**);
|
||||
}
|
||||
|
||||
# Protobuf lite
|
||||
-keep class * extends com.google.protobuf.GeneratedMessageLite { *; }
|
||||
@@ -27,6 +27,7 @@
|
||||
tools:ignore="ProtectedPermissions"/>
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
|
||||
|
||||
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_MMS"/>
|
||||
@@ -113,22 +114,20 @@
|
||||
<meta-data android:name="firebase_messaging_auto_init_enabled" android:value="false" />
|
||||
|
||||
<activity android:name="org.thoughtcrime.securesms.WebRtcCallActivity"
|
||||
android:theme="@style/TextSecure.LightTheme.WebRTCCall"
|
||||
android:excludeFromRecents="true"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|screenSize|fontScale"
|
||||
android:launchMode="singleTask"/>
|
||||
|
||||
<activity android:name=".CountrySelectionActivity"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".InviteActivity"
|
||||
android:theme="@style/TextSecure.HighlightTheme"
|
||||
android:theme="@style/Signal.Light.NoActionBar.Invite"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:parentActivityName=".ConversationListActivity"
|
||||
android:parentActivityName=".MainActivity"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.thoughtcrime.securesms.ConversationListActivity" />
|
||||
android:value="org.thoughtcrime.securesms.MainActivity" />
|
||||
</activity>
|
||||
|
||||
<activity android:name=".PromptMmsActivity"
|
||||
@@ -189,18 +188,18 @@
|
||||
<data android:scheme="sgnl"
|
||||
android:host="addstickers" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="https"
|
||||
android:host="signal.art"
|
||||
android:pathPrefix="/addstickers"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
|
||||
<activity android:name=".ConversationListActivity"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/TextSecure.LightNoActionBar"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||
android:exported="true" />
|
||||
|
||||
<activity-alias android:name=".RoutingActivity"
|
||||
android:targetActivity=".ConversationListActivity"
|
||||
android:targetActivity=".MainActivity"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
@@ -216,24 +215,14 @@
|
||||
|
||||
</activity-alias>
|
||||
|
||||
<activity android:name=".ConversationListArchiveActivity"
|
||||
android:label="@string/AndroidManifest_archived_conversations"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||
android:parentActivityName=".ConversationListActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.thoughtcrime.securesms.ConversationListActivity" />
|
||||
</activity>
|
||||
|
||||
<activity android:name=".conversation.ConversationActivity"
|
||||
android:windowSoftInputMode="stateUnchanged"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"
|
||||
android:parentActivityName=".ConversationListActivity">
|
||||
android:parentActivityName=".MainActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.thoughtcrime.securesms.ConversationListActivity" />
|
||||
android:value="org.thoughtcrime.securesms.MainActivity" />
|
||||
</activity>
|
||||
|
||||
<activity android:name=".longmessage.LongMessageActivity" />
|
||||
@@ -261,13 +250,13 @@
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".DatabaseUpgradeActivity"
|
||||
<activity android:name=".migrations.ApplicationMigrationActivity"
|
||||
android:theme="@style/NoAnimation.Theme.AppCompat.Light.DarkActionBar"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".ExperienceUpgradeActivity"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
|
||||
android:theme="@style/TextSecure.LightNoActionBar"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
@@ -298,10 +287,11 @@
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".mediasend.MediaSendActivity"
|
||||
android:theme="@style/TextSecure.FullScreenMedia"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
<activity android:name=".mediasend.MediaSendActivity"
|
||||
android:theme="@style/TextSecure.FullScreenMedia"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:launchMode="singleTop"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".PassphraseChangeActivity"
|
||||
android:label="@string/AndroidManifest__change_passphrase"
|
||||
@@ -318,24 +308,12 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".registration.WelcomeActivity"
|
||||
<activity android:name=".registration.RegistrationNavigationActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/TextSecure.LightRegistrationTheme"
|
||||
android:windowSoftInputMode="stateUnchanged"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".RegistrationActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/TextSecure.LightRegistrationTheme"
|
||||
android:windowSoftInputMode="stateUnchanged"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".registration.CaptchaActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/TextSecure.LightNoActionBar"
|
||||
android:windowSoftInputMode="stateUnchanged"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".revealable.ViewOnceMessageActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/TextSecure.FullScreenMedia"
|
||||
@@ -364,10 +342,9 @@
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".MediaOverviewActivity"
|
||||
<activity android:name=".mediaoverview.MediaOverviewActivity"
|
||||
android:theme="@style/TextSecure.LightNoActionBar"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".DummyActivity"
|
||||
@@ -425,7 +402,7 @@
|
||||
android:theme="@style/TextSecure.LightTheme"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".scribbles.StickerSelectActivity"
|
||||
<activity android:name=".scribbles.ImageEditorStickerSelectActivity"
|
||||
android:theme="@style/TextSecure.DarkTheme"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
@@ -469,7 +446,18 @@
|
||||
<activity
|
||||
android:name=".maps.PlacePickerActivity"
|
||||
android:label="@string/PlacePickerActivity_title"
|
||||
android:theme="@style/TextSecure.LightNoActionBar" />
|
||||
android:theme="@style/TextSecure.LightNoActionBar"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity
|
||||
android:name=".usernames.ProfileEditActivityV2"
|
||||
android:theme="@style/TextSecure.LightNoActionBar"
|
||||
android:windowSoftInputMode="adjustResize"/>
|
||||
|
||||
<activity android:name=".MainActivity"
|
||||
android:theme="@style/TextSecure.LightNoActionBar"
|
||||
android:launchMode="singleTask"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize" />
|
||||
|
||||
<service android:enabled="true" android:name="org.thoughtcrime.securesms.service.WebRtcCallService"/>
|
||||
<service android:enabled="true" android:name=".service.ApplicationMigrationService"/>
|
||||
|
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 211 KiB |
|
Before Width: | Height: | Size: 447 KiB After Width: | Height: | Size: 447 KiB |
|
Before Width: | Height: | Size: 307 KiB After Width: | Height: | Size: 307 KiB |
|
Before Width: | Height: | Size: 387 KiB After Width: | Height: | Size: 387 KiB |
|
Before Width: | Height: | Size: 494 KiB After Width: | Height: | Size: 494 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 952 KiB After Width: | Height: | Size: 952 KiB |
|
Before Width: | Height: | Size: 862 KiB After Width: | Height: | Size: 862 KiB |
|
Before Width: | Height: | Size: 364 KiB After Width: | Height: | Size: 364 KiB |
|
Before Width: | Height: | Size: 567 KiB After Width: | Height: | Size: 567 KiB |
|
Before Width: | Height: | Size: 376 KiB After Width: | Height: | Size: 376 KiB |
@@ -18,6 +18,7 @@ package org.thoughtcrime.securesms;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.camera.camera2.Camera2AppConfig;
|
||||
import androidx.camera.core.CameraX;
|
||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||
@@ -33,30 +34,32 @@ import com.google.android.gms.security.ProviderInstaller;
|
||||
|
||||
import org.conscrypt.Conscrypt;
|
||||
import org.signal.aesgcmprovider.AesGcmProvider;
|
||||
import org.signal.ringrtc.CallConnectionFactory;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusRepository;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider;
|
||||
import org.thoughtcrime.securesms.gcm.FcmJobService;
|
||||
import org.thoughtcrime.securesms.insights.InsightsOptOut;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer;
|
||||
import org.thoughtcrime.securesms.jobs.CreateSignedPreKeyJob;
|
||||
import org.thoughtcrime.securesms.jobs.FastJobStorage;
|
||||
import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
|
||||
import org.thoughtcrime.securesms.jobs.JobManagerFactories;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
|
||||
import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob;
|
||||
import org.thoughtcrime.securesms.logging.AndroidLogger;
|
||||
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.logging.PersistentLogger;
|
||||
import org.thoughtcrime.securesms.logging.UncaughtExceptionLogger;
|
||||
import org.thoughtcrime.securesms.mediasend.camerax.CameraXUtil;
|
||||
import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||
import org.thoughtcrime.securesms.ringrtc.RingRtcLogger;
|
||||
import org.thoughtcrime.securesms.service.DirectoryRefreshListener;
|
||||
import org.thoughtcrime.securesms.service.ExpiringMessageManager;
|
||||
import org.thoughtcrime.securesms.service.IncomingMessageObserver;
|
||||
@@ -66,10 +69,11 @@ import org.thoughtcrime.securesms.revealable.ViewOnceMessageManager;
|
||||
import org.thoughtcrime.securesms.service.RotateSenderCertificateListener;
|
||||
import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener;
|
||||
import org.thoughtcrime.securesms.service.UpdateApkRefreshListener;
|
||||
import org.thoughtcrime.securesms.stickers.BlessedPacks;
|
||||
import org.thoughtcrime.securesms.util.FrameRateTracker;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.dynamiclanguage.DynamicLanguageContextWrapper;
|
||||
import org.webrtc.PeerConnectionFactory;
|
||||
import org.webrtc.PeerConnectionFactory.InitializationOptions;
|
||||
import org.webrtc.voiceengine.WebRtcAudioManager;
|
||||
import org.webrtc.voiceengine.WebRtcAudioUtils;
|
||||
import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider;
|
||||
@@ -92,10 +96,9 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||
private static final String TAG = ApplicationContext.class.getSimpleName();
|
||||
|
||||
private ExpiringMessageManager expiringMessageManager;
|
||||
private ViewOnceMessageManager viewOnceMessageManager;
|
||||
private ViewOnceMessageManager viewOnceMessageManager;
|
||||
private TypingStatusRepository typingStatusRepository;
|
||||
private TypingStatusSender typingStatusSender;
|
||||
private JobManager jobManager;
|
||||
private IncomingMessageObserver incomingMessageObserver;
|
||||
private PersistentLogger persistentLogger;
|
||||
|
||||
@@ -113,7 +116,8 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||
initializeLogging();
|
||||
initializeCrashHandling();
|
||||
initializeAppDependencies();
|
||||
initializeJobManager();
|
||||
initializeFirstEverAppLaunch();
|
||||
initializeApplicationMigrations();
|
||||
initializeMessageRetrieval();
|
||||
initializeExpiringMessageManager();
|
||||
initializeRevealableMessageManager();
|
||||
@@ -123,22 +127,28 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||
initializeSignedPreKeyCheck();
|
||||
initializePeriodicTasks();
|
||||
initializeCircumvention();
|
||||
initializeWebRtc();
|
||||
initializeRingRtc();
|
||||
initializePendingMessages();
|
||||
initializeUnidentifiedDeliveryAbilityRefresh();
|
||||
initializeBlobProvider();
|
||||
initializeCameraX();
|
||||
NotificationChannels.create(this);
|
||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
|
||||
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
|
||||
}
|
||||
|
||||
ApplicationDependencies.getJobManager().beginJobLoop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(@NonNull LifecycleOwner owner) {
|
||||
isAppVisible = true;
|
||||
Log.i(TAG, "App is now visible.");
|
||||
ApplicationDependencies.getRecipientCache().warmUp();
|
||||
executePendingContactSync();
|
||||
KeyCachingService.onAppForegrounded(this);
|
||||
MessageNotifier.cancelMessagesPending(this);
|
||||
ApplicationDependencies.getFrameRateTracker().begin();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -147,10 +157,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||
Log.i(TAG, "App is no longer visible.");
|
||||
KeyCachingService.onAppBackgrounded(this);
|
||||
MessageNotifier.setVisibleThread(-1);
|
||||
}
|
||||
|
||||
public JobManager getJobManager() {
|
||||
return jobManager;
|
||||
ApplicationDependencies.getFrameRateTracker().end();
|
||||
}
|
||||
|
||||
public ExpiringMessageManager getExpiringMessageManager() {
|
||||
@@ -213,14 +220,8 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionLogger(originalHandler));
|
||||
}
|
||||
|
||||
private void initializeJobManager() {
|
||||
this.jobManager = new JobManager(this, new JobManager.Configuration.Builder()
|
||||
.setDataSerializer(new JsonDataSerializer())
|
||||
.setJobFactories(JobManagerFactories.getJobFactories(this))
|
||||
.setConstraintFactories(JobManagerFactories.getConstraintFactories(this))
|
||||
.setConstraintObservers(JobManagerFactories.getConstraintObservers(this))
|
||||
.setJobStorage(new FastJobStorage(DatabaseFactory.getJobDatabase(this)))
|
||||
.build());
|
||||
private void initializeApplicationMigrations() {
|
||||
ApplicationMigrations.onApplicationCreate(this, ApplicationDependencies.getJobManager());
|
||||
}
|
||||
|
||||
public void initializeMessageRetrieval() {
|
||||
@@ -228,7 +229,26 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||
}
|
||||
|
||||
private void initializeAppDependencies() {
|
||||
ApplicationDependencies.init(new ApplicationDependencyProvider(this, new SignalServiceNetworkAccess(this)));
|
||||
ApplicationDependencies.init(this, new ApplicationDependencyProvider(this, new SignalServiceNetworkAccess(this)));
|
||||
}
|
||||
|
||||
private void initializeFirstEverAppLaunch() {
|
||||
if (TextSecurePreferences.getFirstInstallVersion(this) == -1) {
|
||||
if (!SQLCipherOpenHelper.databaseFileExists(this)) {
|
||||
Log.i(TAG, "First ever app launch!");
|
||||
|
||||
InsightsOptOut.userRequestedOptOut(this);
|
||||
TextSecurePreferences.setAppMigrationVersion(this, ApplicationMigrations.CURRENT_VERSION);
|
||||
TextSecurePreferences.setJobManagerVersion(this, JobManager.CURRENT_VERSION);
|
||||
TextSecurePreferences.setLastExperienceVersionCode(this, Util.getCanonicalVersionCode());
|
||||
TextSecurePreferences.setHasSeenStickerIntroTooltip(this, true);
|
||||
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.ZOZO.getPackId(), BlessedPacks.ZOZO.getPackKey(), false));
|
||||
ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.BANDIT.getPackId(), BlessedPacks.BANDIT.getPackKey(), false));
|
||||
}
|
||||
|
||||
Log.i(TAG, "Setting first install version to " + BuildConfig.CANONICAL_VERSION_CODE);
|
||||
TextSecurePreferences.setFirstInstallVersion(this, BuildConfig.CANONICAL_VERSION_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeGcmCheck() {
|
||||
@@ -236,14 +256,14 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||
long nextSetTime = TextSecurePreferences.getFcmTokenLastSetTime(this) + TimeUnit.HOURS.toMillis(6);
|
||||
|
||||
if (TextSecurePreferences.getFcmToken(this) == null || nextSetTime <= System.currentTimeMillis()) {
|
||||
this.jobManager.add(new FcmRefreshJob());
|
||||
ApplicationDependencies.getJobManager().add(new FcmRefreshJob());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeSignedPreKeyCheck() {
|
||||
if (!TextSecurePreferences.isSignedPreKeyRegistered(this)) {
|
||||
jobManager.add(new CreateSignedPreKeyJob(this));
|
||||
ApplicationDependencies.getJobManager().add(new CreateSignedPreKeyJob(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,7 +294,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeWebRtc() {
|
||||
private void initializeRingRtc() {
|
||||
try {
|
||||
Set<String> HARDWARE_AEC_BLACKLIST = new HashSet<String>() {{
|
||||
add("Pixel");
|
||||
@@ -303,7 +323,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||
WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true);
|
||||
}
|
||||
|
||||
PeerConnectionFactory.initialize(InitializationOptions.builder(this).createInitializationOptions());
|
||||
CallConnectionFactory.initialize(this, new RingRtcLogger());
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
@@ -330,7 +350,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||
|
||||
private void executePendingContactSync() {
|
||||
if (TextSecurePreferences.needsFullContactSync(this)) {
|
||||
ApplicationContext.getInstance(this).getJobManager().add(new MultiDeviceContactUpdateJob(this, true));
|
||||
ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob(true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,18 +360,12 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
FcmJobService.schedule(this);
|
||||
} else {
|
||||
ApplicationContext.getInstance(this).getJobManager().add(new PushNotificationReceiveJob(this));
|
||||
ApplicationDependencies.getJobManager().add(new PushNotificationReceiveJob(this));
|
||||
}
|
||||
TextSecurePreferences.setNeedsMessagePull(this, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeUnidentifiedDeliveryAbilityRefresh() {
|
||||
if (TextSecurePreferences.isMultiDevice(this) && !TextSecurePreferences.isUnidentifiedDeliveryEnabled(this)) {
|
||||
jobManager.add(new RefreshUnidentifiedDeliveryAbilityJob());
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeBlobProvider() {
|
||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||
BlobProvider.getInstance().onSessionStart(this);
|
||||
@@ -360,12 +374,14 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
private void initializeCameraX() {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
try {
|
||||
CameraX.init(this, Camera2AppConfig.create(this));
|
||||
} catch (Throwable t) {
|
||||
Log.w(TAG, "Failed to initialize CameraX.");
|
||||
}
|
||||
if (CameraXUtil.isSupported()) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
CameraX.init(this, Camera2AppConfig.create(this));
|
||||
} catch (Throwable t) {
|
||||
Log.w(TAG, "Failed to initialize CameraX.");
|
||||
}
|
||||
}, "signal-camerax-initialization").start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,19 +17,13 @@
|
||||
*/
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
@@ -42,11 +36,15 @@ import org.thoughtcrime.securesms.preferences.ChatsPreferenceFragment;
|
||||
import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment;
|
||||
import org.thoughtcrime.securesms.preferences.NotificationsPreferenceFragment;
|
||||
import org.thoughtcrime.securesms.preferences.SmsMmsPreferenceFragment;
|
||||
import org.thoughtcrime.securesms.preferences.StoragePreferenceFragment;
|
||||
import org.thoughtcrime.securesms.preferences.widgets.ProfilePreference;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.usernames.ProfileEditActivityV2;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
|
||||
/**
|
||||
* The Activity for application preference display and management.
|
||||
@@ -67,6 +65,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
private static final String PREFERENCE_CATEGORY_APP_PROTECTION = "preference_category_app_protection";
|
||||
private static final String PREFERENCE_CATEGORY_APPEARANCE = "preference_category_appearance";
|
||||
private static final String PREFERENCE_CATEGORY_CHATS = "preference_category_chats";
|
||||
private static final String PREFERENCE_CATEGORY_STORAGE = "preference_category_storage";
|
||||
private static final String PREFERENCE_CATEGORY_DEVICES = "preference_category_devices";
|
||||
private static final String PREFERENCE_CATEGORY_ADVANCED = "preference_category_advanced";
|
||||
|
||||
@@ -112,7 +111,8 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
if (fragmentManager.getBackStackEntryCount() > 0) {
|
||||
fragmentManager.popBackStack();
|
||||
} else {
|
||||
Intent intent = new Intent(this, ConversationListActivity.class);
|
||||
// TODO [greyson] Navigation
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
@@ -151,14 +151,21 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_APPEARANCE));
|
||||
this.findPreference(PREFERENCE_CATEGORY_CHATS)
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_CHATS));
|
||||
this.findPreference(PREFERENCE_CATEGORY_STORAGE)
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_STORAGE));
|
||||
this.findPreference(PREFERENCE_CATEGORY_DEVICES)
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_DEVICES));
|
||||
this.findPreference(PREFERENCE_CATEGORY_ADVANCED)
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_ADVANCED));
|
||||
|
||||
if (VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
tintIcons(getActivity());
|
||||
}
|
||||
tintIcons();
|
||||
}
|
||||
|
||||
private void tintIcons() {
|
||||
if (Build.VERSION.SDK_INT >= 21) return;
|
||||
|
||||
Preference preference = this.findPreference(PREFERENCE_CATEGORY_SMS_MMS);
|
||||
preference.getIcon().setColorFilter(ThemeUtil.getThemedColor(requireContext(), R.attr.icon_tint), PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -197,38 +204,6 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(11)
|
||||
private void tintIcons(Context context) {
|
||||
Drawable sms = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_textsms_white_24dp));
|
||||
Drawable notifications = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_notifications_white_24dp));
|
||||
Drawable privacy = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_security_white_24dp));
|
||||
Drawable appearance = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_brightness_6_white_24dp));
|
||||
Drawable chats = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_forum_white_24dp));
|
||||
Drawable devices = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_laptop_white_24dp));
|
||||
Drawable advanced = DrawableCompat.wrap(ContextCompat.getDrawable(context, R.drawable.ic_advanced_white_24dp));
|
||||
|
||||
int[] tintAttr = new int[]{R.attr.pref_icon_tint};
|
||||
TypedArray typedArray = context.obtainStyledAttributes(tintAttr);
|
||||
int color = typedArray.getColor(0, 0x0);
|
||||
typedArray.recycle();
|
||||
|
||||
DrawableCompat.setTint(sms, color);
|
||||
DrawableCompat.setTint(notifications, color);
|
||||
DrawableCompat.setTint(privacy, color);
|
||||
DrawableCompat.setTint(appearance, color);
|
||||
DrawableCompat.setTint(chats, color);
|
||||
DrawableCompat.setTint(devices, color);
|
||||
DrawableCompat.setTint(advanced, color);
|
||||
|
||||
this.findPreference(PREFERENCE_CATEGORY_SMS_MMS).setIcon(sms);
|
||||
this.findPreference(PREFERENCE_CATEGORY_NOTIFICATIONS).setIcon(notifications);
|
||||
this.findPreference(PREFERENCE_CATEGORY_APP_PROTECTION).setIcon(privacy);
|
||||
this.findPreference(PREFERENCE_CATEGORY_APPEARANCE).setIcon(appearance);
|
||||
this.findPreference(PREFERENCE_CATEGORY_CHATS).setIcon(chats);
|
||||
this.findPreference(PREFERENCE_CATEGORY_DEVICES).setIcon(devices);
|
||||
this.findPreference(PREFERENCE_CATEGORY_ADVANCED).setIcon(advanced);
|
||||
}
|
||||
|
||||
private class CategoryClickListener implements Preference.OnPreferenceClickListener {
|
||||
private String category;
|
||||
|
||||
@@ -256,6 +231,9 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
case PREFERENCE_CATEGORY_CHATS:
|
||||
fragment = new ChatsPreferenceFragment();
|
||||
break;
|
||||
case PREFERENCE_CATEGORY_STORAGE:
|
||||
fragment = new StoragePreferenceFragment();
|
||||
break;
|
||||
case PREFERENCE_CATEGORY_DEVICES:
|
||||
Intent intent = new Intent(getActivity(), DeviceActivity.class);
|
||||
startActivity(intent);
|
||||
@@ -288,11 +266,14 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||
private class ProfileClickListener implements Preference.OnPreferenceClickListener {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent intent = new Intent(preference.getContext(), CreateProfileActivity.class);
|
||||
intent.putExtra(CreateProfileActivity.EXCLUDE_SYSTEM, true);
|
||||
if (FeatureFlags.USERNAMES) {
|
||||
requireActivity().startActivity(ProfileEditActivityV2.getLaunchIntent(requireContext()));
|
||||
} else {
|
||||
Intent intent = new Intent(preference.getContext(), CreateProfileActivity.class);
|
||||
intent.putExtra(CreateProfileActivity.EXCLUDE_SYSTEM, true);
|
||||
|
||||
getActivity().startActivity(intent);
|
||||
// ((BaseActionBarActivity)getActivity()).startActivitySceneTransition(intent, getActivity().findViewById(R.id.avatar), "avatar");
|
||||
requireActivity().startActivity(intent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,13 @@ import androidx.annotation.Nullable;
|
||||
import android.view.View;
|
||||
|
||||
import org.thoughtcrime.securesms.contactshare.Contact;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.ReactionRecord;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.stickers.StickerLocator;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
@@ -36,12 +37,13 @@ public interface BindableConversationItem extends Unbindable {
|
||||
interface EventListener {
|
||||
void onQuoteClicked(MmsMessageRecord messageRecord);
|
||||
void onLinkPreviewClicked(@NonNull LinkPreview linkPreview);
|
||||
void onMoreTextClicked(@NonNull Address conversationAddress, long messageId, boolean isMms);
|
||||
void onMoreTextClicked(@NonNull RecipientId conversationRecipientId, long messageId, boolean isMms);
|
||||
void onStickerClicked(@NonNull StickerLocator stickerLocator);
|
||||
void onViewOnceMessageClicked(@NonNull MmsMessageRecord messageRecord);
|
||||
void onSharedContactDetailsClicked(@NonNull Contact contact, @NonNull View avatarTransitionView);
|
||||
void onAddToContactsClicked(@NonNull Contact contact);
|
||||
void onMessageSharedContactClicked(@NonNull List<Recipient> choices);
|
||||
void onInviteSharedContactClicked(@NonNull List<Recipient> choices);
|
||||
void onReactionClicked(long messageId, boolean isMms);
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,14 @@ import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.database.loaders.BlockedContactsLoader;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.preferences.BlockedContactListItem;
|
||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
|
||||
@@ -78,6 +80,12 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity
|
||||
getLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
getLoaderManager().restartLoader(0, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle bundle) {
|
||||
super.onActivityCreated(bundle);
|
||||
@@ -107,7 +115,7 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Recipient recipient = ((BlockedContactListItem)view).getRecipient();
|
||||
Intent intent = new Intent(getActivity(), RecipientPreferenceActivity.class);
|
||||
intent.putExtra(RecipientPreferenceActivity.ADDRESS_EXTRA, recipient.getAddress());
|
||||
intent.putExtra(RecipientPreferenceActivity.RECIPIENT_ID, recipient.getId());
|
||||
|
||||
startActivity(intent);
|
||||
}
|
||||
@@ -129,8 +137,8 @@ public class BlockedContactsActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
String address = cursor.getString(1);
|
||||
Recipient recipient = Recipient.from(context, Address.fromSerialized(address), true);
|
||||
RecipientId recipientId = RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RecipientDatabase.ID)));
|
||||
LiveRecipient recipient = Recipient.live(recipientId);
|
||||
|
||||
((BlockedContactListItem) view).set(glideRequests, recipient);
|
||||
}
|
||||
@@ -5,14 +5,14 @@ import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.database.Cursor;
|
||||
import android.os.AsyncTask;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.storage.TextSecureIdentityKeyStore;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
@@ -20,12 +20,16 @@ import org.thoughtcrime.securesms.database.PushDatabase;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.jobs.PushDecryptJob;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.PushDecryptMessageJob;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.thoughtcrime.securesms.util.VerifySpan;
|
||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
||||
|
||||
@@ -46,8 +50,8 @@ public class ConfirmIdentityDialog extends AlertDialog {
|
||||
{
|
||||
super(context);
|
||||
|
||||
Recipient recipient = Recipient.from(context, mismatch.getAddress(), false);
|
||||
String name = recipient.toShortString();
|
||||
Recipient recipient = Recipient.resolved(mismatch.getRecipientId(context));
|
||||
String name = recipient.toShortString(context);
|
||||
String introduction = context.getString(R.string.ConfirmIdentityDialog_your_safety_number_with_s_has_changed, name, name);
|
||||
SpannableString spannableString = new SpannableString(introduction + " " +
|
||||
context.getString(R.string.ConfirmIdentityDialog_you_may_wish_to_verify_your_safety_number_with_this_contact));
|
||||
@@ -59,7 +63,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
|
||||
setTitle(name);
|
||||
setMessage(spannableString);
|
||||
|
||||
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(messageRecord, mismatch, recipient.getAddress()));
|
||||
setButton(AlertDialog.BUTTON_POSITIVE, context.getString(R.string.ConfirmIdentityDialog_accept), new AcceptListener(messageRecord, mismatch, recipient.getId()));
|
||||
setButton(AlertDialog.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), new CancelListener());
|
||||
}
|
||||
|
||||
@@ -78,12 +82,12 @@ public class ConfirmIdentityDialog extends AlertDialog {
|
||||
|
||||
private final MessageRecord messageRecord;
|
||||
private final IdentityKeyMismatch mismatch;
|
||||
private final Address address;
|
||||
private final RecipientId recipientId;
|
||||
|
||||
private AcceptListener(MessageRecord messageRecord, IdentityKeyMismatch mismatch, Address address) {
|
||||
private AcceptListener(MessageRecord messageRecord, IdentityKeyMismatch mismatch, RecipientId recipientId) {
|
||||
this.messageRecord = messageRecord;
|
||||
this.mismatch = mismatch;
|
||||
this.address = address;
|
||||
this.recipientId = recipientId;
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
@@ -94,7 +98,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
synchronized (SESSION_LOCK) {
|
||||
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(address.toPhoneString(), 1);
|
||||
SignalProtocolAddress mismatchAddress = new SignalProtocolAddress(Recipient.resolved(recipientId).requireServiceId(), 1);
|
||||
TextSecureIdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(getContext());
|
||||
|
||||
identityKeyStore.saveIdentity(mismatchAddress, mismatch.getIdentityKey(), true);
|
||||
@@ -137,17 +141,17 @@ public class ConfirmIdentityDialog extends AlertDialog {
|
||||
|
||||
if (messageRecord.isMms()) {
|
||||
mmsDatabase.removeMismatchedIdentity(messageRecord.getId(),
|
||||
mismatch.getAddress(),
|
||||
mismatch.getRecipientId(getContext()),
|
||||
mismatch.getIdentityKey());
|
||||
|
||||
if (messageRecord.getRecipient().isPushGroupRecipient()) {
|
||||
MessageSender.resendGroupMessage(getContext(), messageRecord, mismatch.getAddress());
|
||||
if (messageRecord.getRecipient().isPushGroup()) {
|
||||
MessageSender.resendGroupMessage(getContext(), messageRecord, Recipient.resolved(mismatch.getRecipientId(getContext())).getId());
|
||||
} else {
|
||||
MessageSender.resend(getContext(), messageRecord);
|
||||
}
|
||||
} else {
|
||||
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
|
||||
mismatch.getAddress(),
|
||||
mismatch.getRecipientId(getContext()),
|
||||
mismatch.getIdentityKey());
|
||||
|
||||
MessageSender.resend(getContext(), messageRecord);
|
||||
@@ -160,13 +164,13 @@ public class ConfirmIdentityDialog extends AlertDialog {
|
||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(getContext());
|
||||
|
||||
smsDatabase.removeMismatchedIdentity(messageRecord.getId(),
|
||||
mismatch.getAddress(),
|
||||
mismatch.getRecipientId(getContext()),
|
||||
mismatch.getIdentityKey());
|
||||
|
||||
boolean legacy = !messageRecord.isContentBundleKeyExchange();
|
||||
|
||||
SignalServiceEnvelope envelope = new SignalServiceEnvelope(SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE,
|
||||
messageRecord.getIndividualRecipient().getAddress().toPhoneString(),
|
||||
Optional.of(RecipientUtil.toSignalServiceAddress(getContext(), messageRecord.getIndividualRecipient())),
|
||||
messageRecord.getRecipientDeviceId(),
|
||||
messageRecord.getDateSent(),
|
||||
legacy ? Base64.decode(messageRecord.getBody()) : null,
|
||||
@@ -175,9 +179,7 @@ public class ConfirmIdentityDialog extends AlertDialog {
|
||||
|
||||
long pushId = pushDatabase.insert(envelope);
|
||||
|
||||
ApplicationContext.getInstance(getContext())
|
||||
.getJobManager()
|
||||
.add(new PushDecryptJob(getContext(), pushId, messageRecord.getId()));
|
||||
ApplicationDependencies.getJobManager().add(new PushDecryptMessageJob(getContext(), pushId, messageRecord.getId()));
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@@ -20,16 +20,20 @@ import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
@@ -63,7 +67,7 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
|
||||
protected void onCreate(Bundle icicle, boolean ready) {
|
||||
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
|
||||
int displayMode = TextSecurePreferences.isSmsEnabled(this) ? DisplayMode.FLAG_ALL
|
||||
: DisplayMode.FLAG_PUSH | DisplayMode.FLAG_GROUPS;
|
||||
: DisplayMode.FLAG_PUSH | DisplayMode.FLAG_ACTIVE_GROUPS | DisplayMode.FLAG_INACTIVE_GROUPS;
|
||||
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, displayMode);
|
||||
}
|
||||
|
||||
@@ -112,10 +116,10 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactSelected(String number) {}
|
||||
public void onContactSelected(Optional<RecipientId> recipientId, String number) {}
|
||||
|
||||
@Override
|
||||
public void onContactDeselected(String number) {}
|
||||
public void onContactDeselected(Optional<RecipientId> recipientId, String number) {}
|
||||
|
||||
private static class RefreshDirectoryTask extends AsyncTask<Context, Void, Void> {
|
||||
|
||||
@@ -127,7 +131,6 @@ public abstract class ContactSelectionActivity extends PassphraseRequiredActionB
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Context... params) {
|
||||
|
||||
try {
|
||||
DirectoryHelper.refreshDirectory(params[0], true);
|
||||
} catch (IOException e) {
|
||||
@@ -19,16 +19,10 @@ package org.thoughtcrime.securesms;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -37,6 +31,16 @@ import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.pnikosis.materialishprogress.ProgressWheel;
|
||||
|
||||
import org.thoughtcrime.securesms.components.RecyclerViewFastScroller;
|
||||
@@ -44,14 +48,22 @@ import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
|
||||
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
||||
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
|
||||
import org.thoughtcrime.securesms.contacts.SelectedContact;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.util.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.contacts.sync.DirectoryHelper;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.UsernameUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.adapter.FixedViewsAdapter;
|
||||
import org.thoughtcrime.securesms.util.adapter.RecyclerViewConcatenateAdapterStickyHeader;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||
import org.thoughtcrime.securesms.util.views.SimpleProgressDialog;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
@@ -64,28 +76,41 @@ import java.util.Set;
|
||||
* @author Moxie Marlinspike
|
||||
*
|
||||
*/
|
||||
public class ContactSelectionListFragment extends Fragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>
|
||||
public final class ContactSelectionListFragment extends Fragment
|
||||
implements LoaderManager.LoaderCallbacks<Cursor>
|
||||
{
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = ContactSelectionListFragment.class.getSimpleName();
|
||||
private static final String TAG = Log.tag(ContactSelectionListFragment.class);
|
||||
|
||||
public static final String DISPLAY_MODE = "display_mode";
|
||||
public static final String MULTI_SELECT = "multi_select";
|
||||
public static final String REFRESHABLE = "refreshable";
|
||||
public static final String RECENTS = "recents";
|
||||
|
||||
private TextView emptyText;
|
||||
private Set<String> selectedContacts;
|
||||
private OnContactSelectedListener onContactSelectedListener;
|
||||
private SwipeRefreshLayout swipeRefresh;
|
||||
private View showContactsLayout;
|
||||
private Button showContactsButton;
|
||||
private TextView showContactsDescription;
|
||||
private ProgressWheel showContactsProgress;
|
||||
private String cursorFilter;
|
||||
private RecyclerView recyclerView;
|
||||
private RecyclerViewFastScroller fastScroller;
|
||||
private TextView emptyText;
|
||||
private Set<SelectedContact> selectedContacts;
|
||||
private OnContactSelectedListener onContactSelectedListener;
|
||||
private SwipeRefreshLayout swipeRefresh;
|
||||
private View showContactsLayout;
|
||||
private Button showContactsButton;
|
||||
private TextView showContactsDescription;
|
||||
private ProgressWheel showContactsProgress;
|
||||
private String cursorFilter;
|
||||
private RecyclerView recyclerView;
|
||||
private RecyclerViewFastScroller fastScroller;
|
||||
private ContactSelectionListAdapter cursorRecyclerViewAdapter;
|
||||
|
||||
@Nullable private FixedViewsAdapter footerAdapter;
|
||||
@Nullable private InviteCallback inviteCallback;
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
|
||||
if (context instanceof InviteCallback) {
|
||||
inviteCallback = (InviteCallback) context;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle icicle) {
|
||||
@@ -144,8 +169,8 @@ public class ContactSelectionListFragment extends Fragment
|
||||
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
public @NonNull List<String> getSelectedContacts() {
|
||||
List<String> selected = new LinkedList<>();
|
||||
public @NonNull List<SelectedContact> getSelectedContacts() {
|
||||
List<SelectedContact> selected = new LinkedList<>();
|
||||
if (selectedContacts != null) {
|
||||
selected.addAll(selectedContacts);
|
||||
}
|
||||
@@ -158,14 +183,31 @@ public class ContactSelectionListFragment extends Fragment
|
||||
}
|
||||
|
||||
private void initializeCursor() {
|
||||
ContactSelectionListAdapter adapter = new ContactSelectionListAdapter(getActivity(),
|
||||
GlideApp.with(this),
|
||||
null,
|
||||
new ListClickListener(),
|
||||
isMulti());
|
||||
selectedContacts = adapter.getSelectedContacts();
|
||||
recyclerView.setAdapter(adapter);
|
||||
recyclerView.addItemDecoration(new StickyHeaderDecoration(adapter, true, true));
|
||||
cursorRecyclerViewAdapter = new ContactSelectionListAdapter(requireContext(),
|
||||
GlideApp.with(this),
|
||||
null,
|
||||
new ListClickListener(),
|
||||
isMulti());
|
||||
selectedContacts = cursorRecyclerViewAdapter.getSelectedContacts();
|
||||
|
||||
RecyclerViewConcatenateAdapterStickyHeader concatenateAdapter = new RecyclerViewConcatenateAdapterStickyHeader();
|
||||
|
||||
concatenateAdapter.addAdapter(cursorRecyclerViewAdapter);
|
||||
if (inviteCallback != null) {
|
||||
footerAdapter = new FixedViewsAdapter(createInviteActionView(inviteCallback));
|
||||
footerAdapter.hide();
|
||||
concatenateAdapter.addAdapter(footerAdapter);
|
||||
}
|
||||
|
||||
recyclerView.setAdapter(concatenateAdapter);
|
||||
recyclerView.addItemDecoration(new StickyHeaderDecoration(concatenateAdapter, true, true));
|
||||
}
|
||||
|
||||
private View createInviteActionView(@NonNull InviteCallback inviteCallback) {
|
||||
View view = LayoutInflater.from(requireContext())
|
||||
.inflate(R.layout.contact_selection_invite_action_item, (ViewGroup) requireView(), false);
|
||||
view.setOnClickListener(v -> inviteCallback.onInvite());
|
||||
return view;
|
||||
}
|
||||
|
||||
private void initializeNoContactsPermission() {
|
||||
@@ -192,7 +234,7 @@ public class ContactSelectionListFragment extends Fragment
|
||||
|
||||
public void setQueryFilter(String filter) {
|
||||
this.cursorFilter = filter;
|
||||
this.getLoaderManager().restartLoader(0, null, this);
|
||||
LoaderManager.getInstance(this).restartLoader(0, null, this);
|
||||
}
|
||||
|
||||
public void resetQueryFilter() {
|
||||
@@ -220,28 +262,38 @@ public class ContactSelectionListFragment extends Fragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor data) {
|
||||
public void onLoadFinished(@NonNull Loader<Cursor> loader, @Nullable Cursor data) {
|
||||
swipeRefresh.setVisibility(View.VISIBLE);
|
||||
showContactsLayout.setVisibility(View.GONE);
|
||||
|
||||
((CursorRecyclerViewAdapter) recyclerView.getAdapter()).changeCursor(data);
|
||||
cursorRecyclerViewAdapter.changeCursor(data);
|
||||
|
||||
if (footerAdapter != null) {
|
||||
footerAdapter.show();
|
||||
}
|
||||
|
||||
emptyText.setText(R.string.contact_selection_group_activity__no_contacts);
|
||||
boolean useFastScroller = (recyclerView.getAdapter().getItemCount() > 20);
|
||||
boolean useFastScroller = data != null && data.getCount() > 20;
|
||||
recyclerView.setVerticalScrollBarEnabled(!useFastScroller);
|
||||
if (useFastScroller) {
|
||||
fastScroller.setVisibility(View.VISIBLE);
|
||||
fastScroller.setRecyclerView(recyclerView);
|
||||
} else {
|
||||
fastScroller.setRecyclerView(null);
|
||||
fastScroller.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
|
||||
((CursorRecyclerViewAdapter) recyclerView.getAdapter()).changeCursor(null);
|
||||
cursorRecyclerViewAdapter.changeCursor(null);
|
||||
fastScroller.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private void handleContactPermissionGranted() {
|
||||
final Context context = requireContext();
|
||||
|
||||
new AsyncTask<Void, Void, Boolean>() {
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
@@ -256,7 +308,7 @@ public class ContactSelectionListFragment extends Fragment
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... voids) {
|
||||
try {
|
||||
DirectoryHelper.refreshDirectory(getContext(), false);
|
||||
DirectoryHelper.refreshDirectory(context, false);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
@@ -281,14 +333,48 @@ public class ContactSelectionListFragment extends Fragment
|
||||
private class ListClickListener implements ContactSelectionListAdapter.ItemClickListener {
|
||||
@Override
|
||||
public void onItemClick(ContactSelectionListItem contact) {
|
||||
if (!isMulti() || !selectedContacts.contains(contact.getNumber())) {
|
||||
selectedContacts.add(contact.getNumber());
|
||||
contact.setChecked(true);
|
||||
if (onContactSelectedListener != null) onContactSelectedListener.onContactSelected(contact.getNumber());
|
||||
SelectedContact selectedContact = contact.isUsernameType() ? SelectedContact.forUsername(contact.getRecipientId().orNull(), contact.getNumber())
|
||||
: SelectedContact.forPhone(contact.getRecipientId().orNull(), contact.getNumber());
|
||||
|
||||
if (!isMulti() || !selectedContacts.contains(selectedContact)) {
|
||||
if (contact.isUsernameType()) {
|
||||
AlertDialog loadingDialog = SimpleProgressDialog.show(requireContext());
|
||||
|
||||
SimpleTask.run(getViewLifecycleOwner().getLifecycle(), () -> {
|
||||
return UsernameUtil.fetchUuidForUsername(requireContext(), contact.getNumber());
|
||||
}, uuid -> {
|
||||
loadingDialog.dismiss();
|
||||
if (uuid.isPresent()) {
|
||||
Recipient recipient = Recipient.externalUsername(requireContext(), uuid.get(), contact.getNumber());
|
||||
selectedContacts.add(SelectedContact.forUsername(recipient.getId(), contact.getNumber()));
|
||||
contact.setChecked(true);
|
||||
|
||||
if (onContactSelectedListener != null) {
|
||||
onContactSelectedListener.onContactSelected(Optional.of(recipient.getId()), null);
|
||||
}
|
||||
} else {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.ContactSelectionListFragment_username_not_found)
|
||||
.setMessage(getString(R.string.ContactSelectionListFragment_s_is_not_a_signal_user, contact.getNumber()))
|
||||
.setPositiveButton(R.string.ContactSelectionListFragment_okay, (dialog, which) -> dialog.dismiss())
|
||||
.show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
selectedContacts.add(selectedContact);
|
||||
contact.setChecked(true);
|
||||
|
||||
if (onContactSelectedListener != null) {
|
||||
onContactSelectedListener.onContactSelected(contact.getRecipientId(), contact.getNumber());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
selectedContacts.remove(contact.getNumber());
|
||||
selectedContacts.remove(selectedContact);
|
||||
contact.setChecked(false);
|
||||
if (onContactSelectedListener != null) onContactSelectedListener.onContactDeselected(contact.getNumber());
|
||||
|
||||
if (onContactSelectedListener != null) {
|
||||
onContactSelectedListener.onContactDeselected(contact.getRecipientId(), contact.getNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,8 +388,11 @@ public class ContactSelectionListFragment extends Fragment
|
||||
}
|
||||
|
||||
public interface OnContactSelectedListener {
|
||||
void onContactSelected(String number);
|
||||
void onContactDeselected(String number);
|
||||
void onContactSelected(Optional<RecipientId> recipientId, String number);
|
||||
void onContactDeselected(Optional<RecipientId> recipientId, String number);
|
||||
}
|
||||
|
||||
public interface InviteCallback {
|
||||
void onInvite();
|
||||
}
|
||||
}
|
||||
@@ -36,15 +36,19 @@ import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
|
||||
import org.thoughtcrime.securesms.components.emoji.MediaKeyboard;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileKeyUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceProfileContentUpdateJob;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints;
|
||||
import org.thoughtcrime.securesms.profiles.SystemProfileUtil;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
@@ -147,7 +151,7 @@ public class CreateProfileActivity extends BaseActionBarActivity {
|
||||
|
||||
if (data != null && data.getBooleanExtra("delete", false)) {
|
||||
avatarBytes = null;
|
||||
avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_camera_alt_white_24dp).asDrawable(this, getResources().getColor(R.color.grey_400)));
|
||||
avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_camera_solid_white_24).asDrawable(this, getResources().getColor(R.color.grey_400)));
|
||||
} else {
|
||||
AvatarSelection.circularCropImage(this, inputFile, outputFile, R.string.CropImageActivity_profile_avatar);
|
||||
}
|
||||
@@ -260,14 +264,14 @@ public class CreateProfileActivity extends BaseActionBarActivity {
|
||||
}
|
||||
|
||||
private void initializeProfileAvatar(boolean excludeSystem) {
|
||||
Address ourAddress = Address.fromSerialized(TextSecurePreferences.getLocalNumber(this));
|
||||
RecipientId selfId = Recipient.self().getId();
|
||||
|
||||
if (AvatarHelper.getAvatarFile(this, ourAddress).exists() && AvatarHelper.getAvatarFile(this, ourAddress).length() > 0) {
|
||||
if (AvatarHelper.getAvatarFile(this, selfId).exists() && AvatarHelper.getAvatarFile(this, selfId).length() > 0) {
|
||||
new AsyncTask<Void, Void, byte[]>() {
|
||||
@Override
|
||||
protected byte[] doInBackground(Void... params) {
|
||||
try {
|
||||
return Util.readFully(AvatarHelper.getInputStreamFor(CreateProfileActivity.this, ourAddress));
|
||||
return Util.readFully(AvatarHelper.getInputStreamFor(CreateProfileActivity.this, selfId));
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
return null;
|
||||
@@ -362,6 +366,7 @@ public class CreateProfileActivity extends BaseActionBarActivity {
|
||||
try {
|
||||
accountManager.setProfileName(profileKey, name);
|
||||
TextSecurePreferences.setProfileName(context, name);
|
||||
DatabaseFactory.getRecipientDatabase(context).setProfileName(Recipient.self().getId(), name);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
return false;
|
||||
@@ -369,14 +374,15 @@ public class CreateProfileActivity extends BaseActionBarActivity {
|
||||
|
||||
try {
|
||||
accountManager.setProfileAvatar(profileKey, avatar);
|
||||
AvatarHelper.setAvatar(CreateProfileActivity.this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)), avatarBytes);
|
||||
AvatarHelper.setAvatar(CreateProfileActivity.this, Recipient.self().getId(), avatarBytes);
|
||||
TextSecurePreferences.setProfileAvatarId(CreateProfileActivity.this, new SecureRandom().nextInt());
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
return false;
|
||||
}
|
||||
|
||||
ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceProfileKeyUpdateJob());
|
||||
ApplicationDependencies.getJobManager().add(new MultiDeviceProfileKeyUpdateJob());
|
||||
ApplicationDependencies.getJobManager().add(new MultiDeviceProfileContentUpdateJob());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -149,7 +149,8 @@ public class DatabaseMigrationActivity extends PassphraseRequiredActionBarActivi
|
||||
if (getIntent().hasExtra("next_intent")) {
|
||||
startActivity((Intent)getIntent().getParcelableExtra("next_intent"));
|
||||
} else {
|
||||
startActivity(new Intent(this, ConversationListActivity.class));
|
||||
// TODO [greyson] Navigation
|
||||
startActivity(new Intent(this, MainActivity.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,11 @@ import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.push.AccountManagerFactory;
|
||||
@@ -61,6 +63,7 @@ public class DeviceActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle, boolean ready) {
|
||||
getSupportActionBar().setHomeAsUpIndicator(ContextCompat.getDrawable(this, R.drawable.ic_arrow_left_24));
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setTitle(R.string.AndroidManifest__linked_devices);
|
||||
this.deviceAddFragment = new DeviceAddFragment();
|
||||
@@ -175,7 +178,7 @@ public class DeviceActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
try {
|
||||
Context context = DeviceActivity.this;
|
||||
SignalServiceAccountManager accountManager = AccountManagerFactory.createManager(context);
|
||||
SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
||||
String verificationCode = accountManager.getNewDeviceVerificationCode();
|
||||
String ephemeralId = uri.getQueryParameter("uuid");
|
||||
String publicKeyEncoded = uri.getQueryParameter("pub_key");
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
@@ -13,7 +14,6 @@ import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.devicelist.Device;
|
||||
import org.thoughtcrime.securesms.jobs.RefreshUnidentifiedDeliveryAbilityJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -162,6 +162,7 @@ public class DeviceListFragment extends ListFragment
|
||||
builder.show();
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private void handleDisconnectDevice(final long deviceId) {
|
||||
new ProgressDialogAsyncTask<Void, Void, Void>(getActivity(),
|
||||
R.string.DeviceListActivity_unlinking_device_no_ellipsis,
|
||||
@@ -171,10 +172,6 @@ public class DeviceListFragment extends ListFragment
|
||||
protected Void doInBackground(Void... params) {
|
||||
try {
|
||||
accountManager.removeDevice(deviceId);
|
||||
|
||||
ApplicationContext.getInstance(getContext())
|
||||
.getJobManager()
|
||||
.add(new RefreshUnidentifiedDeliveryAbilityJob());
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, e);
|
||||
Toast.makeText(getActivity(), R.string.DeviceListActivity_network_failed, Toast.LENGTH_LONG).show();
|
||||
@@ -18,8 +18,12 @@ import com.melnykov.fab.FloatingActionButton;
|
||||
import com.nineoldandroids.animation.ArgbEvaluator;
|
||||
|
||||
import org.thoughtcrime.securesms.IntroPagerAdapter.IntroPage;
|
||||
import org.thoughtcrime.securesms.database.model.Sticker;
|
||||
import org.thoughtcrime.securesms.experienceupgrades.StickersIntroFragment;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
@@ -29,11 +33,17 @@ import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ExperienceUpgradeActivity extends BaseActionBarActivity implements TypingIndicatorIntroFragment.Controller, LinkPreviewsIntroFragment.Controller {
|
||||
public class ExperienceUpgradeActivity extends BaseActionBarActivity
|
||||
implements TypingIndicatorIntroFragment.Controller,
|
||||
LinkPreviewsIntroFragment.Controller,
|
||||
StickersIntroFragment.Controller
|
||||
{
|
||||
private static final String TAG = ExperienceUpgradeActivity.class.getSimpleName();
|
||||
private static final String DISMISS_ACTION = "org.thoughtcrime.securesms.ExperienceUpgradeActivity.DISMISS_ACTION";
|
||||
private static final int NOTIFICATION_ID = 1339;
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
|
||||
|
||||
private enum ExperienceUpgrade {
|
||||
SIGNAL_REBRANDING(157,
|
||||
new IntroPage(0xFF2090EA,
|
||||
@@ -86,8 +96,15 @@ public class ExperienceUpgradeActivity extends BaseActionBarActivity implements
|
||||
R.string.ExperienceUpgradeActivity_introducing_link_previews,
|
||||
R.string.ExperienceUpgradeActivity_optional_link_previews_are_now_supported,
|
||||
R.string.ExperienceUpgradeActivity_optional_link_previews_are_now_supported,
|
||||
null,
|
||||
true);
|
||||
null,
|
||||
true),
|
||||
STICKERS(580,
|
||||
new IntroPage(0xFF2090EA, StickersIntroFragment.newInstance()),
|
||||
R.string.ExperienceUpgradeActivity_introducing_stickers,
|
||||
R.string.ExperienceUpgradeActivity_why_use_words_when_you_can_use_stickers,
|
||||
R.string.ExperienceUpgradeActivity_why_use_words_when_you_can_use_stickers,
|
||||
null,
|
||||
true);
|
||||
|
||||
private int version;
|
||||
private List<IntroPage> pages;
|
||||
@@ -157,7 +174,7 @@ public class ExperienceUpgradeActivity extends BaseActionBarActivity implements
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setStatusBarColor(getResources().getColor(R.color.signal_primary_dark));
|
||||
dynamicTheme.onCreate(this);
|
||||
|
||||
final Optional<ExperienceUpgrade> upgrade = getExperienceUpgrade(this);
|
||||
if (!upgrade.isPresent()) {
|
||||
@@ -182,6 +199,12 @@ public class ExperienceUpgradeActivity extends BaseActionBarActivity implements
|
||||
ServiceUtil.getNotificationManager(this).cancel(NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
dynamicTheme.onResume(this);
|
||||
}
|
||||
|
||||
private void onContinue(Optional<ExperienceUpgrade> seenUpgrade) {
|
||||
ServiceUtil.getNotificationManager(this).cancel(NOTIFICATION_ID);
|
||||
int latestVersion = seenUpgrade.isPresent() ? seenUpgrade.get().getVersion()
|
||||
@@ -189,7 +212,8 @@ public class ExperienceUpgradeActivity extends BaseActionBarActivity implements
|
||||
TextSecurePreferences.setLastExperienceVersionCode(this, latestVersion);
|
||||
if (seenUpgrade.isPresent() && seenUpgrade.get().nextIntent != null) {
|
||||
Intent intent = new Intent(this, seenUpgrade.get().nextIntent);
|
||||
Intent nextIntent = new Intent(this, ConversationListActivity.class);
|
||||
// TODO [greyson] Navigation
|
||||
Intent nextIntent = new Intent(this, MainActivity.class);
|
||||
intent.putExtra("next_intent", nextIntent);
|
||||
startActivity(intent);
|
||||
} else {
|
||||
@@ -231,29 +255,9 @@ public class ExperienceUpgradeActivity extends BaseActionBarActivity implements
|
||||
onContinue(Optional.of(ExperienceUpgrade.LINK_PREVIEWS));
|
||||
}
|
||||
|
||||
private final class OnPageChangeListener implements ViewPager.OnPageChangeListener {
|
||||
private final ArgbEvaluator evaluator = new ArgbEvaluator();
|
||||
private final ExperienceUpgrade upgrade;
|
||||
|
||||
public OnPageChangeListener(ExperienceUpgrade upgrade) {
|
||||
this.upgrade = upgrade;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {}
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
final int nextPosition = (position + 1) % upgrade.getPages().size();
|
||||
|
||||
final int color = (Integer)evaluator.evaluate(positionOffset,
|
||||
upgrade.getPage(position).backgroundColor,
|
||||
upgrade.getPage(nextPosition).backgroundColor);
|
||||
getWindow().setBackgroundDrawable(new ColorDrawable(color));
|
||||
}
|
||||
@Override
|
||||
public void onStickersFinished() {
|
||||
onContinue(Optional.of(ExperienceUpgrade.STICKERS));
|
||||
}
|
||||
|
||||
public static class AppUpgradeReceiver extends BroadcastReceiver {
|
||||
@@ -20,11 +20,13 @@ package org.thoughtcrime.securesms;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.thoughtcrime.securesms.avatar.AvatarSelection;
|
||||
@@ -50,7 +52,6 @@ import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
||||
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ContactColors;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||
@@ -60,6 +61,7 @@ import org.thoughtcrime.securesms.groups.GroupManager;
|
||||
import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
@@ -90,7 +92,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
private final static String TAG = GroupCreateActivity.class.getSimpleName();
|
||||
|
||||
public static final String GROUP_ADDRESS_EXTRA = "group_recipient";
|
||||
public static final String GROUP_ID_EXTRA = "group_id";
|
||||
public static final String GROUP_THREAD_EXTRA = "group_thread";
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
||||
@@ -117,7 +119,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
protected void onCreate(Bundle state, boolean ready) {
|
||||
setContentView(R.layout.group_create_activity);
|
||||
//noinspection ConstantConditions
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
initializeAppBar();
|
||||
initializeResources();
|
||||
initializeExistingGroup();
|
||||
}
|
||||
@@ -176,6 +178,12 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
addSelectedContacts(recipients.toArray(new Recipient[recipients.size()]));
|
||||
}
|
||||
|
||||
private void initializeAppBar() {
|
||||
Drawable upIcon = ContextCompat.getDrawable(this, R.drawable.ic_arrow_left_24);
|
||||
getSupportActionBar().setHomeAsUpIndicator(upIcon);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
RecipientsEditor recipientsEditor = ViewUtil.findById(this, R.id.recipients_text);
|
||||
PushRecipientsPanel recipientsPanel = ViewUtil.findById(this, R.id.recipients);
|
||||
@@ -189,15 +197,15 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
recipientsEditor.setHint(R.string.recipients_panel__add_members);
|
||||
recipientsPanel.setPanelChangeListener(this);
|
||||
findViewById(R.id.contacts_button).setOnClickListener(new AddRecipientButtonListener());
|
||||
avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_group_white_24dp).asDrawable(this, ContactColors.UNKNOWN_COLOR.toConversationColor(this)));
|
||||
avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_group_outline_40, R.drawable.ic_group_outline_20).asDrawable(this, ContactColors.UNKNOWN_COLOR.toConversationColor(this)));
|
||||
avatar.setOnClickListener(view -> AvatarSelection.startAvatarSelection(this, false, false));
|
||||
}
|
||||
|
||||
private void initializeExistingGroup() {
|
||||
final Address groupAddress = getIntent().getParcelableExtra(GROUP_ADDRESS_EXTRA);
|
||||
final String groupId = getIntent().getStringExtra(GROUP_ID_EXTRA);
|
||||
|
||||
if (groupAddress != null) {
|
||||
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, groupAddress.toGroupString());
|
||||
if (groupId != null) {
|
||||
new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, groupId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,7 +268,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
Intent intent = new Intent(this, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
||||
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
|
||||
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
|
||||
intent.putExtra(ConversationActivity.RECIPIENT_EXTRA, recipient.getId());
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
@@ -283,14 +291,13 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
switch (reqCode) {
|
||||
case PICK_CONTACT:
|
||||
List<String> selected = data.getStringArrayListExtra("contacts");
|
||||
|
||||
for (String contact : selected) {
|
||||
Address address = Address.fromExternal(this, contact);
|
||||
Recipient recipient = Recipient.from(this, address, false);
|
||||
List<RecipientId> selected = data.getParcelableArrayListExtra(PushContactSelectionActivity.KEY_SELECTED_RECIPIENTS);
|
||||
|
||||
for (RecipientId contact : selected) {
|
||||
Recipient recipient = Recipient.resolved(contact);
|
||||
addSelectedContacts(recipient);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AvatarSelection.REQUEST_CODE_AVATAR:
|
||||
@@ -338,16 +345,17 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
@Override
|
||||
protected GroupActionResult doInBackground(Void... avoid) {
|
||||
List<Address> memberAddresses = new LinkedList<>();
|
||||
List<RecipientId> memberAddresses = new LinkedList<>();
|
||||
|
||||
for (Recipient recipient : members) {
|
||||
memberAddresses.add(recipient.getAddress());
|
||||
memberAddresses.add(recipient.getId());
|
||||
}
|
||||
memberAddresses.add(Address.fromSerialized(TextSecurePreferences.getLocalNumber(activity)));
|
||||
memberAddresses.add(Recipient.self().getId());
|
||||
|
||||
String groupId = DatabaseFactory.getGroupDatabase(activity).getOrCreateGroupForMembers(memberAddresses, true);
|
||||
Recipient groupRecipient = Recipient.from(activity, Address.fromSerialized(groupId), true);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(activity).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.DEFAULT);
|
||||
String groupId = DatabaseFactory.getGroupDatabase(activity).getOrCreateGroupForMembers(memberAddresses, true);
|
||||
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(activity).getOrInsertFromGroupId(groupId);
|
||||
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
|
||||
long threadId = DatabaseFactory.getThreadDatabase(activity).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.DEFAULT);
|
||||
|
||||
return new GroupActionResult(groupRecipient, threadId);
|
||||
}
|
||||
@@ -450,7 +458,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
if (!activity.isFinishing()) {
|
||||
Intent intent = activity.getIntent();
|
||||
intent.putExtra(GROUP_THREAD_EXTRA, result.get().getThreadId());
|
||||
intent.putExtra(GROUP_ADDRESS_EXTRA, result.get().getGroupRecipient().getAddress());
|
||||
intent.putExtra(GROUP_ID_EXTRA, result.get().getGroupRecipient().requireGroupId());
|
||||
activity.setResult(RESULT_OK, intent);
|
||||
activity.finish();
|
||||
}
|
||||
@@ -492,8 +500,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
if (failIfNotPush && !isPush) {
|
||||
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group,
|
||||
recipient.toShortString())));
|
||||
} else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.getAddress().serialize())) {
|
||||
recipient.toShortString(activity))));
|
||||
} else if (TextUtils.equals(TextSecurePreferences.getLocalNumber(activity), recipient.getE164().or(""))) {
|
||||
results.add(new Result(null, false, activity.getString(R.string.GroupCreateActivity_youre_already_in_the_group)));
|
||||
} else {
|
||||
results.add(new Result(recipient, isPush, null));
|
||||
@@ -10,6 +10,7 @@ import android.text.TextUtils;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientExporter;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.LinkedList;
|
||||
@@ -32,7 +33,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
|
||||
|
||||
@Override
|
||||
protected List<Recipient> doInBackground(Void... params) {
|
||||
return DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.getAddress().toGroupString(), true);
|
||||
return DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.requireGroupId(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,7 +67,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
|
||||
|
||||
if (recipient.getContactUri() != null) {
|
||||
Intent intent = new Intent(context, RecipientPreferenceActivity.class);
|
||||
intent.putExtra(RecipientPreferenceActivity.ADDRESS_EXTRA, recipient.getAddress());
|
||||
intent.putExtra(RecipientPreferenceActivity.RECIPIENT_ID, recipient.getId());
|
||||
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
@@ -90,7 +91,7 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
|
||||
|
||||
public GroupMembers(List<Recipient> recipients) {
|
||||
for (Recipient recipient : recipients) {
|
||||
if (isLocalNumber(recipient)) {
|
||||
if (recipient.isLocalNumber()) {
|
||||
members.push(recipient);
|
||||
} else {
|
||||
members.add(recipient);
|
||||
@@ -102,15 +103,10 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
|
||||
List<String> recipientStrings = new LinkedList<>();
|
||||
|
||||
for (Recipient recipient : members) {
|
||||
if (isLocalNumber(recipient)) {
|
||||
if (recipient.isLocalNumber()) {
|
||||
recipientStrings.add(context.getString(R.string.GroupMembersDialog_me));
|
||||
} else {
|
||||
String name = recipient.toShortString();
|
||||
|
||||
if (recipient.getName() == null && !TextUtils.isEmpty(recipient.getProfileName())) {
|
||||
name += " ~" + recipient.getProfileName();
|
||||
}
|
||||
|
||||
String name = getRecipientName(recipient);
|
||||
recipientStrings.add(name);
|
||||
}
|
||||
}
|
||||
@@ -118,12 +114,20 @@ public class GroupMembersDialog extends AsyncTask<Void, Void, List<Recipient>> {
|
||||
return recipientStrings.toArray(new String[members.size()]);
|
||||
}
|
||||
|
||||
private String getRecipientName(Recipient recipient) {
|
||||
if (FeatureFlags.PROFILE_DISPLAY) return recipient.getDisplayName(context);
|
||||
|
||||
String name = recipient.toShortString(context);
|
||||
|
||||
if (recipient.getName(context) == null && !TextUtils.isEmpty(recipient.getProfileName())) {
|
||||
name += " ~" + recipient.getProfileName();
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
public Recipient get(int index) {
|
||||
return members.get(index);
|
||||
}
|
||||
|
||||
private boolean isLocalNumber(Recipient recipient) {
|
||||
return Util.isOwnNumber(context, recipient.getAddress());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.PushDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobs.PushDecryptMessageJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* The central entry point for all envelopes that have been retrieved. Envelopes must be processed
|
||||
* here to guarantee proper ordering.
|
||||
*/
|
||||
public class IncomingMessageProcessor {
|
||||
|
||||
private static final String TAG = Log.tag(IncomingMessageProcessor.class);
|
||||
|
||||
private final Context context;
|
||||
private final ReentrantLock lock;
|
||||
|
||||
public IncomingMessageProcessor(@NonNull Context context) {
|
||||
this.context = context;
|
||||
this.lock = new ReentrantLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return An instance of a Processor that will allow you to process messages in a thread safe
|
||||
* way. Must be closed.
|
||||
*/
|
||||
public Processor acquire() {
|
||||
lock.lock();
|
||||
|
||||
Thread current = Thread.currentThread();
|
||||
Log.d(TAG, "Lock acquired by thread " + current.getId() + " (" + current.getName() + ")");
|
||||
|
||||
return new Processor(context);
|
||||
}
|
||||
|
||||
private void release() {
|
||||
Thread current = Thread.currentThread();
|
||||
Log.d(TAG, "Lock about to be released by thread " + current.getId() + " (" + current.getName() + ")");
|
||||
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
public class Processor implements Closeable {
|
||||
|
||||
private final Context context;
|
||||
private final PushDatabase pushDatabase;
|
||||
private final MmsSmsDatabase mmsSmsDatabase;
|
||||
private final JobManager jobManager;
|
||||
|
||||
private Processor(@NonNull Context context) {
|
||||
this.context = context;
|
||||
this.pushDatabase = DatabaseFactory.getPushDatabase(context);
|
||||
this.mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context);
|
||||
this.jobManager = ApplicationDependencies.getJobManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The id of the {@link PushDecryptMessageJob} that was scheduled to process the message, if
|
||||
* one was created. Otherwise null.
|
||||
*/
|
||||
public @Nullable String processEnvelope(@NonNull SignalServiceEnvelope envelope) {
|
||||
if (envelope.hasSource()) {
|
||||
Recipient.externalPush(context, envelope.getSourceAddress());
|
||||
}
|
||||
|
||||
if (envelope.isReceipt()) {
|
||||
processReceipt(envelope);
|
||||
return null;
|
||||
} else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage() || envelope.isUnidentifiedSender()) {
|
||||
return processMessage(envelope);
|
||||
} else {
|
||||
Log.w(TAG, "Received envelope of unknown type: " + envelope.getType());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private @NonNull String processMessage(@NonNull SignalServiceEnvelope envelope) {
|
||||
Log.i(TAG, "Received message. Inserting in PushDatabase.");
|
||||
|
||||
long id = pushDatabase.insert(envelope);
|
||||
PushDecryptMessageJob job = new PushDecryptMessageJob(context, id);
|
||||
|
||||
jobManager.add(job);
|
||||
|
||||
return job.getId();
|
||||
}
|
||||
|
||||
private void processReceipt(@NonNull SignalServiceEnvelope envelope) {
|
||||
Log.i(TAG, String.format(Locale.ENGLISH, "Received receipt: (XXXXX, %d)", envelope.getTimestamp()));
|
||||
mmsSmsDatabase.incrementDeliveryReceiptCount(new SyncMessageId(Recipient.externalPush(context, envelope.getSourceAddress()).getId(), envelope.getTimestamp()),
|
||||
System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,46 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.AnimRes;
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewAnimationUtils;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver.OnPreDrawListener;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.AnimRes;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.thoughtcrime.securesms.components.ContactFilterToolbar;
|
||||
import org.thoughtcrime.securesms.components.ContactFilterToolbar.OnFilterChangedListener;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.contacts.SelectedContact;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarInviteTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.WindowUtil;
|
||||
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture.Listener;
|
||||
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
@@ -46,7 +52,14 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
|
||||
private Button smsSendButton;
|
||||
private Animation slideInAnimation;
|
||||
private Animation slideOutAnimation;
|
||||
private ImageView heart;
|
||||
private DynamicTheme dynamicTheme = new DynamicNoActionBarInviteTheme();
|
||||
private Toolbar primaryToolbar;
|
||||
|
||||
@Override
|
||||
protected void onPreCreate() {
|
||||
super.onPreCreate();
|
||||
dynamicTheme.onCreate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState, boolean ready) {
|
||||
@@ -55,12 +68,27 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
|
||||
getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false);
|
||||
|
||||
setContentView(R.layout.invite_activity);
|
||||
assert getSupportActionBar() != null;
|
||||
getSupportActionBar().setTitle(R.string.AndroidManifest__invite_friends);
|
||||
|
||||
initializeAppBar();
|
||||
initializeResources();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
dynamicTheme.onResume(this);
|
||||
}
|
||||
|
||||
private void initializeAppBar() {
|
||||
primaryToolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(primaryToolbar);
|
||||
|
||||
assert getSupportActionBar() != null;
|
||||
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setTitle(R.string.AndroidManifest__invite_friends);
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
slideInAnimation = loadAnimation(R.anim.slide_from_bottom);
|
||||
slideOutAnimation = loadAnimation(R.anim.slide_to_bottom);
|
||||
@@ -73,22 +101,18 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
|
||||
inviteText = ViewUtil.findById(this, R.id.invite_text);
|
||||
smsSendFrame = ViewUtil.findById(this, R.id.sms_send_frame);
|
||||
smsSendButton = ViewUtil.findById(this, R.id.send_sms_button);
|
||||
heart = ViewUtil.findById(this, R.id.heart);
|
||||
contactsFragment = (ContactSelectionListFragment)getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
|
||||
|
||||
inviteText.setText(getString(R.string.InviteActivity_lets_switch_to_signal, getString(R.string.install_url)));
|
||||
updateSmsButtonText();
|
||||
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
|
||||
heart.getViewTreeObserver().addOnPreDrawListener(new HeartPreDrawListener());
|
||||
}
|
||||
contactsFragment.setOnContactSelectedListener(this);
|
||||
shareButton.setOnClickListener(new ShareClickListener());
|
||||
smsButton.setOnClickListener(new SmsClickListener());
|
||||
smsCancelButton.setOnClickListener(new SmsCancelClickListener());
|
||||
smsSendButton.setOnClickListener(new SmsSendClickListener());
|
||||
contactFilter.setOnFilterChangedListener(new ContactFilterChangedListener());
|
||||
contactFilter.setNavigationIcon(R.drawable.ic_search_white_24dp);
|
||||
contactFilter.setNavigationIcon(R.drawable.ic_search_conversation_24);
|
||||
}
|
||||
|
||||
private Animation loadAnimation(@AnimRes int animResId) {
|
||||
@@ -98,12 +122,12 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactSelected(String number) {
|
||||
public void onContactSelected(Optional<RecipientId> recipientId, String number) {
|
||||
updateSmsButtonText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactDeselected(String number) {
|
||||
public void onContactDeselected(Optional<RecipientId> recipientId, String number) {
|
||||
updateSmsButtonText();
|
||||
}
|
||||
|
||||
@@ -111,7 +135,7 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
|
||||
new SendSmsInvitesAsyncTask(this, inviteText.getText().toString())
|
||||
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
|
||||
contactsFragment.getSelectedContacts()
|
||||
.toArray(new String[contactsFragment.getSelectedContacts().size()]));
|
||||
.toArray(new SelectedContact[contactsFragment.getSelectedContacts().size()]));
|
||||
}
|
||||
|
||||
private void updateSmsButtonText() {
|
||||
@@ -130,11 +154,42 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
|
||||
}
|
||||
|
||||
private void cancelSmsSelection() {
|
||||
setPrimaryColorsToolbarNormal();
|
||||
contactsFragment.reset();
|
||||
updateSmsButtonText();
|
||||
ViewUtil.animateOut(smsSendFrame, slideOutAnimation, View.GONE);
|
||||
}
|
||||
|
||||
private void setPrimaryColorsToolbarNormal() {
|
||||
primaryToolbar.setBackgroundColor(0);
|
||||
primaryToolbar.getNavigationIcon().setColorFilter(null);
|
||||
primaryToolbar.setTitleTextColor(ThemeUtil.getThemedColor(this, R.attr.title_text_color_primary));
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
getWindow().setStatusBarColor(ThemeUtil.getThemedColor(this, android.R.attr.statusBarColor));
|
||||
getWindow().setNavigationBarColor(ThemeUtil.getThemedColor(this, android.R.attr.navigationBarColor));
|
||||
WindowUtil.setLightStatusBarFromTheme(this);
|
||||
}
|
||||
|
||||
WindowUtil.setLightNavigationBarFromTheme(this);
|
||||
}
|
||||
|
||||
private void setPrimaryColorsToolbarForSms() {
|
||||
primaryToolbar.setBackgroundColor(ContextCompat.getColor(this, R.color.signal_primary));
|
||||
primaryToolbar.getNavigationIcon().setColorFilter(ThemeUtil.getThemedColor(this, R.attr.conversation_subtitle_color), PorterDuff.Mode.SRC_IN);
|
||||
primaryToolbar.setTitleTextColor(ThemeUtil.getThemedColor(this, R.attr.conversation_title_color));
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
getWindow().setStatusBarColor(ContextCompat.getColor(this, R.color.signal_primary));
|
||||
WindowUtil.clearLightStatusBar(getWindow());
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 27) {
|
||||
getWindow().setNavigationBarColor(ContextCompat.getColor(this, R.color.signal_primary));
|
||||
WindowUtil.clearLightNavigationBar(getWindow());
|
||||
}
|
||||
}
|
||||
|
||||
private class ShareClickListener implements OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@@ -153,6 +208,7 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
|
||||
private class SmsClickListener implements OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setPrimaryColorsToolbarForSms();
|
||||
ViewUtil.animateIn(smsSendFrame, slideInAnimation);
|
||||
}
|
||||
}
|
||||
@@ -185,25 +241,8 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
|
||||
}
|
||||
}
|
||||
|
||||
private class HeartPreDrawListener implements OnPreDrawListener {
|
||||
@Override
|
||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
||||
public boolean onPreDraw() {
|
||||
heart.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
final int w = heart.getWidth();
|
||||
final int h = heart.getHeight();
|
||||
Animator reveal = ViewAnimationUtils.createCircularReveal(heart,
|
||||
w / 2, h,
|
||||
0, (float)Math.sqrt(h*h + (w*w/4)));
|
||||
reveal.setInterpolator(new FastOutSlowInInterpolator());
|
||||
reveal.setDuration(800);
|
||||
reveal.start();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private class SendSmsInvitesAsyncTask extends ProgressDialogAsyncTask<String,Void,Void> {
|
||||
private class SendSmsInvitesAsyncTask extends ProgressDialogAsyncTask<SelectedContact,Void,Void> {
|
||||
private final String message;
|
||||
|
||||
SendSmsInvitesAsyncTask(Context context, String message) {
|
||||
@@ -212,18 +251,19 @@ public class InviteActivity extends PassphraseRequiredActionBarActivity implemen
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(String... numbers) {
|
||||
protected Void doInBackground(SelectedContact... contacts) {
|
||||
final Context context = getContext();
|
||||
if (context == null) return null;
|
||||
|
||||
for (String number : numbers) {
|
||||
Recipient recipient = Recipient.from(context, Address.fromExternal(context, number), false);
|
||||
int subscriptionId = recipient.getDefaultSubscriptionId().or(-1);
|
||||
for (SelectedContact contact : contacts) {
|
||||
RecipientId recipientId = contact.getOrCreateRecipientId(context);
|
||||
Recipient recipient = Recipient.resolved(recipientId);
|
||||
int subscriptionId = recipient.getDefaultSubscriptionId().or(-1);
|
||||
|
||||
MessageSender.send(context, new OutgoingTextMessage(recipient, message, subscriptionId), -1L, true, null);
|
||||
|
||||
if (recipient.getContactUri() != null) {
|
||||
DatabaseFactory.getRecipientDatabase(context).setSeenInviteReminder(recipient, true);
|
||||
DatabaseFactory.getRecipientDatabase(context).setHasSentInvite(recipient.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
@@ -46,12 +47,10 @@ public class LinkPreviewsIntroFragment extends Fragment {
|
||||
View view = inflater.inflate(R.layout.experience_upgrade_link_previews_fragment, container, false);
|
||||
|
||||
view.findViewById(R.id.experience_ok_button).setOnClickListener(v -> {
|
||||
ApplicationContext.getInstance(requireContext())
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()),
|
||||
TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()),
|
||||
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(requireContext()),
|
||||
TextSecurePreferences.isLinkPreviewsEnabled(requireContext())));
|
||||
ApplicationDependencies.getJobManager().add(new MultiDeviceConfigurationUpdateJob(TextSecurePreferences.isReadReceiptsEnabled(requireContext()),
|
||||
TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()),
|
||||
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(requireContext()),
|
||||
TextSecurePreferences.isLinkPreviewsEnabled(requireContext())));
|
||||
controller.onLinkPreviewsFinished();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
|
||||
public class MainActivity extends PassphraseRequiredActionBarActivity {
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
|
||||
private final MainNavigator navigator = new MainNavigator(this);
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState, boolean ready) {
|
||||
super.onCreate(savedInstanceState, ready);
|
||||
setContentView(R.layout.main_activity);
|
||||
|
||||
navigator.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreCreate() {
|
||||
super.onPreCreate();
|
||||
dynamicTheme.onCreate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
dynamicTheme.onResume(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (!navigator.onBackPressed()) {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
public @NonNull MainNavigator getNavigator() {
|
||||
return navigator;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
public class MainFragment extends Fragment {
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
|
||||
if (!(requireActivity() instanceof MainActivity)) {
|
||||
throw new IllegalStateException("Can only be used inside of MainActivity!");
|
||||
}
|
||||
}
|
||||
|
||||
protected @NonNull MainNavigator getNavigator() {
|
||||
return MainNavigator.get(requireActivity());
|
||||
}
|
||||
}
|
||||
104
app/src/main/java/org/thoughtcrime/securesms/MainNavigator.java
Normal file
@@ -0,0 +1,104 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import org.thoughtcrime.securesms.conversation.ConversationActivity;
|
||||
import org.thoughtcrime.securesms.conversationlist.ConversationListArchiveFragment;
|
||||
import org.thoughtcrime.securesms.conversationlist.ConversationListFragment;
|
||||
import org.thoughtcrime.securesms.insights.InsightsLauncher;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
||||
public class MainNavigator {
|
||||
|
||||
private final MainActivity activity;
|
||||
|
||||
public MainNavigator(@NonNull MainActivity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
public static MainNavigator get(@NonNull Activity activity) {
|
||||
if (!(activity instanceof MainActivity)) {
|
||||
throw new IllegalArgumentException("Activity must be an instance of MainActivity!");
|
||||
}
|
||||
|
||||
return ((MainActivity) activity).getNavigator();
|
||||
}
|
||||
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
if (savedInstanceState != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
getFragmentManager().beginTransaction()
|
||||
.add(R.id.fragment_container, ConversationListFragment.newInstance())
|
||||
.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if the back pressed was handled in our own custom way, false if it should be given
|
||||
* to the system to do the default behavior.
|
||||
*/
|
||||
public boolean onBackPressed() {
|
||||
Fragment fragment = getFragmentManager().findFragmentById(R.id.fragment_container);
|
||||
|
||||
if (fragment instanceof BackHandler) {
|
||||
return ((BackHandler) fragment).onBackPressed();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void goToConversation(@NonNull RecipientId recipientId, long threadId, int distributionType, long lastSeen, int startingPosition) {
|
||||
Intent intent = ConversationActivity.buildIntent(activity, recipientId, threadId, distributionType, lastSeen, startingPosition);
|
||||
|
||||
activity.startActivity(intent);
|
||||
activity.overridePendingTransition(R.anim.slide_from_end, R.anim.fade_scale_out);
|
||||
}
|
||||
|
||||
public void goToAppSettings() {
|
||||
Intent intent = new Intent(activity, ApplicationPreferencesActivity.class);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
|
||||
public void goToArchiveList() {
|
||||
getFragmentManager().beginTransaction()
|
||||
.setCustomAnimations(R.anim.slide_from_end, R.anim.slide_to_start, R.anim.slide_from_start, R.anim.slide_to_end)
|
||||
.replace(R.id.fragment_container, ConversationListArchiveFragment.newInstance())
|
||||
.addToBackStack(null)
|
||||
.commit();
|
||||
}
|
||||
|
||||
public void goToGroupCreation() {
|
||||
Intent intent = new Intent(activity, GroupCreateActivity.class);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
public void goToInvite() {
|
||||
Intent intent = new Intent(activity, InviteActivity.class);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
public void goToInsights() {
|
||||
InsightsLauncher.showInsightsDashboard(activity.getSupportFragmentManager());
|
||||
}
|
||||
|
||||
private @NonNull FragmentManager getFragmentManager() {
|
||||
return activity.getSupportFragmentManager();
|
||||
}
|
||||
|
||||
public interface BackHandler {
|
||||
/**
|
||||
* @return True if the back pressed was handled in our own custom way, false if it should be given
|
||||
* to the system to do the default behavior.
|
||||
*/
|
||||
boolean onBackPressed();
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
@@ -37,6 +38,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.util.Pair;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
@@ -50,23 +52,22 @@ import androidx.viewpager.widget.ViewPager;
|
||||
import org.thoughtcrime.securesms.animation.DepthPageTransformer;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.components.viewpager.ExtendedOnPageChangedListener;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.MediaDatabase;
|
||||
import org.thoughtcrime.securesms.database.MediaDatabase.MediaRecord;
|
||||
import org.thoughtcrime.securesms.database.loaders.PagingMediaLoader;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity;
|
||||
import org.thoughtcrime.securesms.mediapreview.MediaPreviewFragment;
|
||||
import org.thoughtcrime.securesms.mediapreview.MediaPreviewViewModel;
|
||||
import org.thoughtcrime.securesms.mediapreview.MediaRailAdapter;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.AttachmentUtil;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
|
||||
import org.thoughtcrime.securesms.util.SaveAttachmentTask.Attachment;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
@@ -76,20 +77,23 @@ import java.util.Map;
|
||||
* Activity for displaying media attachments in-app
|
||||
*/
|
||||
public final class MediaPreviewActivity extends PassphraseRequiredActionBarActivity
|
||||
implements RecipientModifiedListener,
|
||||
LoaderManager.LoaderCallbacks<Pair<Cursor, Integer>>,
|
||||
implements LoaderManager.LoaderCallbacks<Pair<Cursor, Integer>>,
|
||||
MediaRailAdapter.RailItemListener,
|
||||
MediaPreviewFragment.Events
|
||||
{
|
||||
|
||||
private final static String TAG = MediaPreviewActivity.class.getSimpleName();
|
||||
|
||||
public static final String ADDRESS_EXTRA = "address";
|
||||
private static final int NOT_IN_A_THREAD = -2;
|
||||
|
||||
public static final String THREAD_ID_EXTRA = "thread_id";
|
||||
public static final String DATE_EXTRA = "date";
|
||||
public static final String SIZE_EXTRA = "size";
|
||||
public static final String CAPTION_EXTRA = "caption";
|
||||
public static final String OUTGOING_EXTRA = "outgoing";
|
||||
public static final String LEFT_IS_RECENT_EXTRA = "left_is_recent";
|
||||
public static final String HIDE_ALL_MEDIA_EXTRA = "came_from_all_media";
|
||||
public static final String SHOW_THREAD_EXTRA = "show_thread";
|
||||
public static final String SORTING_EXTRA = "sorting";
|
||||
|
||||
private ViewPager mediaPager;
|
||||
private View detailsContainer;
|
||||
@@ -102,27 +106,32 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
private String initialMediaType;
|
||||
private long initialMediaSize;
|
||||
private String initialCaption;
|
||||
private Recipient conversationRecipient;
|
||||
private boolean leftIsRecent;
|
||||
private MediaPreviewViewModel viewModel;
|
||||
private ViewPagerListener viewPagerListener;
|
||||
|
||||
private int restartItem = -1;
|
||||
|
||||
private int restartItem = -1;
|
||||
private long threadId = NOT_IN_A_THREAD;
|
||||
private boolean cameFromAllMedia;
|
||||
private boolean showThread;
|
||||
private MediaDatabase.Sorting sorting;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle, boolean ready) {
|
||||
this.setTheme(R.style.TextSecure_DarkTheme);
|
||||
this.setTheme(R.style.TextSecure_MediaPreview);
|
||||
setContentView(R.layout.media_preview_activity);
|
||||
|
||||
setSupportActionBar(findViewById(R.id.toolbar));
|
||||
|
||||
viewModel = ViewModelProviders.of(this).get(MediaPreviewViewModel.class);
|
||||
|
||||
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
|
||||
showSystemUI();
|
||||
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
setContentView(R.layout.media_preview_activity);
|
||||
|
||||
initializeViews();
|
||||
initializeResources();
|
||||
@@ -134,11 +143,6 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
Permissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(Recipient recipient) {
|
||||
Util.runOnMain(this::initializeActionBar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRailItemClicked(int distanceFromActive) {
|
||||
mediaPager.setCurrentItem(mediaPager.getCurrentItem() + distanceFromActive);
|
||||
@@ -154,19 +158,45 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
MediaItem mediaItem = getCurrentMediaItem();
|
||||
|
||||
if (mediaItem != null) {
|
||||
CharSequence relativeTimeSpan;
|
||||
getSupportActionBar().setTitle(getTitleText(mediaItem));
|
||||
getSupportActionBar().setSubtitle(getSubTitleText(mediaItem));
|
||||
}
|
||||
}
|
||||
|
||||
if (mediaItem.date > 0) {
|
||||
relativeTimeSpan = DateUtils.getExtendedRelativeTimeSpanString(this, Locale.getDefault(), mediaItem.date);
|
||||
} else {
|
||||
relativeTimeSpan = getString(R.string.MediaPreviewActivity_draft);
|
||||
private @NonNull String getTitleText(@NonNull MediaItem mediaItem) {
|
||||
String from;
|
||||
if (mediaItem.outgoing) from = getString(R.string.MediaPreviewActivity_you);
|
||||
else if (mediaItem.recipient != null) from = mediaItem.recipient.toShortString(this);
|
||||
else from = "";
|
||||
|
||||
if (showThread) {
|
||||
String to = null;
|
||||
Recipient threadRecipient = mediaItem.threadRecipient;
|
||||
|
||||
if (threadRecipient != null) {
|
||||
if (mediaItem.outgoing || threadRecipient.isGroup()) {
|
||||
if (threadRecipient.isLocalNumber()) {
|
||||
from = getString(R.string.note_to_self);
|
||||
} else {
|
||||
to = threadRecipient.toShortString(this);
|
||||
}
|
||||
} else {
|
||||
to = getString(R.string.MediaPreviewActivity_you);
|
||||
}
|
||||
}
|
||||
|
||||
if (mediaItem.outgoing) getSupportActionBar().setTitle(getString(R.string.MediaPreviewActivity_you));
|
||||
else if (mediaItem.recipient != null) getSupportActionBar().setTitle(mediaItem.recipient.toShortString());
|
||||
else getSupportActionBar().setTitle("");
|
||||
return to != null ? getString(R.string.MediaPreviewActivity_s_to_s, from, to)
|
||||
: from;
|
||||
} else {
|
||||
return from;
|
||||
}
|
||||
}
|
||||
|
||||
getSupportActionBar().setSubtitle(relativeTimeSpan);
|
||||
private @NonNull String getSubTitleText(@NonNull MediaItem mediaItem) {
|
||||
if (mediaItem.date > 0) {
|
||||
return DateUtils.getExtendedRelativeTimeSpanString(this, Locale.getDefault(), mediaItem.date);
|
||||
} else {
|
||||
return getString(R.string.MediaPreviewActivity_draft);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,23 +238,30 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
caption = findViewById(R.id.media_preview_caption);
|
||||
captionContainer = findViewById(R.id.media_preview_caption_container);
|
||||
playbackControlsContainer = findViewById(R.id.media_preview_playback_controls_container);
|
||||
|
||||
View toolbarLayout = findViewById(R.id.toolbar_layout);
|
||||
|
||||
anchorMarginsToBottomInsets(detailsContainer);
|
||||
|
||||
anchorMarginsToTopInsets(toolbarLayout);
|
||||
|
||||
showAndHideWithSystemUI(getWindow(), detailsContainer, toolbarLayout);
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
Address address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
|
||||
Intent intent = getIntent();
|
||||
|
||||
initialMediaUri = getIntent().getData();
|
||||
initialMediaType = getIntent().getType();
|
||||
initialMediaSize = getIntent().getLongExtra(SIZE_EXTRA, 0);
|
||||
initialCaption = getIntent().getStringExtra(CAPTION_EXTRA);
|
||||
leftIsRecent = getIntent().getBooleanExtra(LEFT_IS_RECENT_EXTRA, false);
|
||||
threadId = intent.getLongExtra(THREAD_ID_EXTRA, NOT_IN_A_THREAD);
|
||||
cameFromAllMedia = intent.getBooleanExtra(HIDE_ALL_MEDIA_EXTRA, false);
|
||||
showThread = intent.getBooleanExtra(SHOW_THREAD_EXTRA, false);
|
||||
sorting = MediaDatabase.Sorting.values()[intent.getIntExtra(SORTING_EXTRA, 0)];
|
||||
|
||||
initialMediaUri = intent.getData();
|
||||
initialMediaType = intent.getType();
|
||||
initialMediaSize = intent.getLongExtra(SIZE_EXTRA, 0);
|
||||
initialCaption = intent.getStringExtra(CAPTION_EXTRA);
|
||||
leftIsRecent = intent.getBooleanExtra(LEFT_IS_RECENT_EXTRA, false);
|
||||
restartItem = -1;
|
||||
|
||||
if (address != null) {
|
||||
conversationRecipient = Recipient.from(this, address, true);
|
||||
} else {
|
||||
conversationRecipient = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeObservers() {
|
||||
@@ -233,6 +270,11 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
return;
|
||||
}
|
||||
|
||||
if (!((MediaItemAdapter) mediaPager.getAdapter()).hasFragmentFor(mediaPager.getCurrentItem())) {
|
||||
Log.d(TAG, "MediaItemAdapter wasn't ready. Posting again...");
|
||||
viewModel.resubmitPreviewData();
|
||||
}
|
||||
|
||||
View playbackControls = ((MediaItemAdapter) mediaPager.getAdapter()).getPlaybackControls(mediaPager.getCurrentItem());
|
||||
|
||||
if (previewData.getAlbumThumbnails().isEmpty() && previewData.getCaption() == null && playbackControls == null) {
|
||||
@@ -269,7 +311,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
|
||||
Log.i(TAG, "Loading Part URI: " + initialMediaUri);
|
||||
|
||||
if (conversationRecipient != null) {
|
||||
if (isMediaInDb()) {
|
||||
LoaderManager.getInstance(this).restartLoader(0, null, this);
|
||||
} else {
|
||||
mediaPager.setAdapter(new SingleItemPagerAdapter(getSupportFragmentManager(), initialMediaUri, initialMediaType, initialMediaSize));
|
||||
@@ -292,9 +334,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
}
|
||||
|
||||
private void showOverview() {
|
||||
Intent intent = new Intent(this, MediaOverviewActivity.class);
|
||||
intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, conversationRecipient.getAddress());
|
||||
startActivity(intent);
|
||||
startActivity(MediaOverviewActivity.forThread(this, threadId));
|
||||
}
|
||||
|
||||
private void forward() {
|
||||
@@ -372,6 +412,10 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
menu.findItem(R.id.delete).setVisible(false);
|
||||
}
|
||||
|
||||
if (cameFromAllMedia) {
|
||||
menu.findItem(R.id.media_preview__overview).setVisible(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -391,7 +435,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
}
|
||||
|
||||
private boolean isMediaInDb() {
|
||||
return conversationRecipient != null;
|
||||
return threadId != NOT_IN_A_THREAD;
|
||||
}
|
||||
|
||||
private @Nullable MediaItem getCurrentMediaItem() {
|
||||
@@ -410,7 +454,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
|
||||
@Override
|
||||
public @NonNull Loader<Pair<Cursor, Integer>> onCreateLoader(int id, Bundle args) {
|
||||
return new PagingMediaLoader(this, conversationRecipient, initialMediaUri, leftIsRecent);
|
||||
return new PagingMediaLoader(this, threadId, initialMediaUri, leftIsRecent, sorting);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -439,11 +483,36 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
|
||||
@Override
|
||||
public boolean singleTapOnMedia() {
|
||||
detailsContainer.setVisibility(detailsContainer.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE);
|
||||
|
||||
toggleUiVisibility();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void toggleUiVisibility() {
|
||||
int systemUiVisibility = getWindow().getDecorView().getSystemUiVisibility();
|
||||
if ((systemUiVisibility & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) {
|
||||
showSystemUI();
|
||||
} else {
|
||||
hideSystemUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void hideSystemUI() {
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_FULLSCREEN );
|
||||
}
|
||||
|
||||
private void showSystemUI() {
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN );
|
||||
}
|
||||
|
||||
private class ViewPagerListener extends ExtendedOnPageChangedListener {
|
||||
|
||||
@Override
|
||||
@@ -454,7 +523,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
|
||||
if (adapter != null) {
|
||||
MediaItem item = adapter.getMediaItemFor(position);
|
||||
if (item.recipient != null) item.recipient.addListener(MediaPreviewActivity.this);
|
||||
if (item.recipient != null) item.recipient.live().observe(MediaPreviewActivity.this, r -> initializeActionBar());
|
||||
viewModel.setActiveAlbumRailItem(MediaPreviewActivity.this, position);
|
||||
initializeActionBar();
|
||||
}
|
||||
@@ -467,7 +536,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
|
||||
if (adapter != null) {
|
||||
MediaItem item = adapter.getMediaItemFor(position);
|
||||
if (item.recipient != null) item.recipient.removeListener(MediaPreviewActivity.this);
|
||||
if (item.recipient != null) item.recipient.live().removeObservers(MediaPreviewActivity.this);
|
||||
|
||||
adapter.pause(position);
|
||||
}
|
||||
@@ -515,7 +584,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
|
||||
@Override
|
||||
public MediaItem getMediaItemFor(int position) {
|
||||
return new MediaItem(null, null, uri, mediaType, -1, true);
|
||||
return new MediaItem(null, null, null, uri, mediaType, -1, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -532,6 +601,53 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFragmentFor(int position) {
|
||||
return mediaPreviewFragment != null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void anchorMarginsToBottomInsets(@NonNull View viewToAnchor) {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(viewToAnchor, (view, insets) -> {
|
||||
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
|
||||
|
||||
layoutParams.setMargins(insets.getSystemWindowInsetLeft(),
|
||||
layoutParams.topMargin,
|
||||
insets.getSystemWindowInsetRight(),
|
||||
insets.getSystemWindowInsetBottom());
|
||||
|
||||
view.setLayoutParams(layoutParams);
|
||||
|
||||
return insets;
|
||||
});
|
||||
}
|
||||
|
||||
private static void anchorMarginsToTopInsets(@NonNull View viewToAnchor) {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(viewToAnchor, (view, insets) -> {
|
||||
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
|
||||
|
||||
layoutParams.setMargins(insets.getSystemWindowInsetLeft(),
|
||||
insets.getSystemWindowInsetTop(),
|
||||
insets.getSystemWindowInsetRight(),
|
||||
layoutParams.bottomMargin);
|
||||
|
||||
view.setLayoutParams(layoutParams);
|
||||
|
||||
return insets;
|
||||
});
|
||||
}
|
||||
|
||||
private static void showAndHideWithSystemUI(@NonNull Window window, @NonNull View... views) {
|
||||
window.getDecorView().setOnSystemUiVisibilityChangeListener(visibility -> {
|
||||
boolean hide = (visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
|
||||
|
||||
for (View view : views) {
|
||||
view.animate()
|
||||
.alpha(hide ? 0 : 1)
|
||||
.start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class CursorPagerAdapter extends FragmentStatePagerAdapter implements MediaItemAdapter {
|
||||
@@ -602,12 +718,15 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
|
||||
public MediaItem getMediaItemFor(int position) {
|
||||
cursor.moveToPosition(getCursorPosition(position));
|
||||
MediaRecord mediaRecord = MediaRecord.from(context, cursor);
|
||||
Address address = mediaRecord.getAddress();
|
||||
|
||||
MediaRecord mediaRecord = MediaRecord.from(context, cursor);
|
||||
RecipientId recipientId = mediaRecord.getRecipientId();
|
||||
RecipientId threadRecipientId = mediaRecord.getThreadRecipientId();
|
||||
|
||||
if (mediaRecord.getAttachment().getDataUri() == null) throw new AssertionError();
|
||||
|
||||
return new MediaItem(address != null ? Recipient.from(context, address,true) : null,
|
||||
return new MediaItem(Recipient.live(recipientId).get(),
|
||||
Recipient.live(threadRecipientId).get(),
|
||||
mediaRecord.getAttachment(),
|
||||
mediaRecord.getAttachment().getDataUri(),
|
||||
mediaRecord.getContentType(),
|
||||
@@ -628,6 +747,11 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFragmentFor(int position) {
|
||||
return mediaFragments.containsKey(position);
|
||||
}
|
||||
|
||||
private int getCursorPosition(int position) {
|
||||
if (leftIsRecent) return position;
|
||||
else return cursor.getCount() - 1 - position;
|
||||
@@ -636,6 +760,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
|
||||
private static class MediaItem {
|
||||
private final @Nullable Recipient recipient;
|
||||
private final @Nullable Recipient threadRecipient;
|
||||
private final @Nullable DatabaseAttachment attachment;
|
||||
private final @NonNull Uri uri;
|
||||
private final @NonNull String type;
|
||||
@@ -643,18 +768,20 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
private final boolean outgoing;
|
||||
|
||||
private MediaItem(@Nullable Recipient recipient,
|
||||
@Nullable Recipient threadRecipient,
|
||||
@Nullable DatabaseAttachment attachment,
|
||||
@NonNull Uri uri,
|
||||
@NonNull String type,
|
||||
long date,
|
||||
boolean outgoing)
|
||||
{
|
||||
this.recipient = recipient;
|
||||
this.attachment = attachment;
|
||||
this.uri = uri;
|
||||
this.type = type;
|
||||
this.date = date;
|
||||
this.outgoing = outgoing;
|
||||
this.recipient = recipient;
|
||||
this.threadRecipient = threadRecipient;
|
||||
this.attachment = attachment;
|
||||
this.uri = uri;
|
||||
this.type = type;
|
||||
this.date = date;
|
||||
this.outgoing = outgoing;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -662,5 +789,6 @@ public final class MediaPreviewActivity extends PassphraseRequiredActionBarActiv
|
||||
MediaItem getMediaItemFor(int position);
|
||||
void pause(int position);
|
||||
@Nullable View getPlaybackControls(int position);
|
||||
boolean hasFragmentFor(int position);
|
||||
}
|
||||
}
|
||||
@@ -52,10 +52,11 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.util.DateUtils;
|
||||
import org.thoughtcrime.securesms.util.DynamicDarkActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.ExpirationUtil;
|
||||
@@ -73,14 +74,14 @@ import java.util.Locale;
|
||||
/**
|
||||
* @author Jake McGinty
|
||||
*/
|
||||
public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity implements LoaderCallbacks<Cursor>, RecipientModifiedListener {
|
||||
public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity implements LoaderCallbacks<Cursor> {
|
||||
private final static String TAG = MessageDetailsActivity.class.getSimpleName();
|
||||
|
||||
public final static String MESSAGE_ID_EXTRA = "message_id";
|
||||
public final static String THREAD_ID_EXTRA = "thread_id";
|
||||
public final static String IS_PUSH_GROUP_EXTRA = "is_push_group";
|
||||
public final static String TYPE_EXTRA = "type";
|
||||
public final static String ADDRESS_EXTRA = "address";
|
||||
public static final String MESSAGE_ID_EXTRA = "message_id";
|
||||
public static final String THREAD_ID_EXTRA = "thread_id";
|
||||
public static final String IS_PUSH_GROUP_EXTRA = "is_push_group";
|
||||
public static final String TYPE_EXTRA = "type";
|
||||
public static final String RECIPIENT_EXTRA = "recipient_id";
|
||||
|
||||
private GlideRequests glideRequests;
|
||||
private long threadId;
|
||||
@@ -100,7 +101,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
||||
private ListView recipientsList;
|
||||
private LayoutInflater inflater;
|
||||
|
||||
private DynamicTheme dynamicTheme = new DynamicTheme();
|
||||
private DynamicTheme dynamicTheme = new DynamicDarkActionBarTheme();
|
||||
private DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||
|
||||
private boolean running;
|
||||
@@ -149,10 +150,10 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
||||
assert getSupportActionBar() != null;
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
Recipient recipient = Recipient.from(this, getIntent().getParcelableExtra(ADDRESS_EXTRA), true);
|
||||
recipient.addListener(this);
|
||||
LiveRecipient recipient = Recipient.live(getIntent().getParcelableExtra(RECIPIENT_EXTRA));
|
||||
recipient.observe(this, r -> setActionBarColor(r.getColor()));
|
||||
|
||||
setActionBarColor(recipient.getColor());
|
||||
setActionBarColor(recipient.get().getColor());
|
||||
}
|
||||
|
||||
private void setActionBarColor(MaterialColor color) {
|
||||
@@ -164,11 +165,6 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(final Recipient recipient) {
|
||||
Util.runOnMain(() -> setActionBarColor(recipient.getColor()));
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
inflater = LayoutInflater.from(this);
|
||||
View header = inflater.inflate(R.layout.message_details_header, recipientsList, false);
|
||||
@@ -366,20 +362,20 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
|
||||
|
||||
List<RecipientDeliveryStatus> recipients = new LinkedList<>();
|
||||
|
||||
if (!messageRecord.getRecipient().isGroupRecipient()) {
|
||||
if (!messageRecord.getRecipient().isGroup()) {
|
||||
recipients.add(new RecipientDeliveryStatus(messageRecord.getRecipient(), getStatusFor(messageRecord.getDeliveryReceiptCount(), messageRecord.getReadReceiptCount(), messageRecord.isPending()), messageRecord.isUnidentified(), -1));
|
||||
} else {
|
||||
List<GroupReceiptInfo> receiptInfoList = DatabaseFactory.getGroupReceiptDatabase(context).getGroupReceiptInfo(messageRecord.getId());
|
||||
|
||||
if (receiptInfoList.isEmpty()) {
|
||||
List<Recipient> group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().getAddress().toGroupString(), false);
|
||||
List<Recipient> group = DatabaseFactory.getGroupDatabase(context).getGroupMembers(messageRecord.getRecipient().requireGroupId(), false);
|
||||
|
||||
for (Recipient recipient : group) {
|
||||
recipients.add(new RecipientDeliveryStatus(recipient, RecipientDeliveryStatus.Status.UNKNOWN, false, -1));
|
||||
}
|
||||
} else {
|
||||
for (GroupReceiptInfo info : receiptInfoList) {
|
||||
recipients.add(new RecipientDeliveryStatus(Recipient.from(context, info.getAddress(), true),
|
||||
recipients.add(new RecipientDeliveryStatus(Recipient.resolved(info.getRecipientId()),
|
||||
getStatusFor(info.getStatus(), messageRecord.isPending(), messageRecord.isFailed()),
|
||||
info.isUnidentified(),
|
||||
info.getTimestamp()));
|
||||
@@ -11,7 +11,9 @@ import android.widget.BaseAdapter;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.Conversions;
|
||||
import org.thoughtcrime.securesms.util.adapter.StableIdGenerator;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
@@ -19,11 +21,12 @@ import java.util.List;
|
||||
|
||||
class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.RecyclerListener {
|
||||
|
||||
private final Context context;
|
||||
private final GlideRequests glideRequests;
|
||||
private final MessageRecord record;
|
||||
private final List<RecipientDeliveryStatus> members;
|
||||
private final boolean isPushGroup;
|
||||
private final Context context;
|
||||
private final GlideRequests glideRequests;
|
||||
private final MessageRecord record;
|
||||
private final List<RecipientDeliveryStatus> members;
|
||||
private final boolean isPushGroup;
|
||||
private final StableIdGenerator<RecipientId> idGenerator;
|
||||
|
||||
MessageDetailsRecipientAdapter(@NonNull Context context, @NonNull GlideRequests glideRequests,
|
||||
@NonNull MessageRecord record, @NonNull List<RecipientDeliveryStatus> members,
|
||||
@@ -34,6 +37,7 @@ class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.
|
||||
this.record = record;
|
||||
this.isPushGroup = isPushGroup;
|
||||
this.members = members;
|
||||
this.idGenerator = new StableIdGenerator<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,11 +52,7 @@ class MessageDetailsRecipientAdapter extends BaseAdapter implements AbsListView.
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
try {
|
||||
return Conversions.byteArrayToLong(MessageDigest.getInstance("SHA1").digest(members.get(position).recipient.getAddress().serialize().getBytes()));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
return idGenerator.getId(members.get(position).recipient.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -25,6 +25,8 @@ import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.MessageDetailsRecipientAdapter.RecipientDeliveryStatus;
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.components.DeliveryStatusView;
|
||||
@@ -32,11 +34,11 @@ import org.thoughtcrime.securesms.components.FromTextView;
|
||||
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
||||
import org.thoughtcrime.securesms.database.documents.NetworkFailure;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientForeverObserver;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
/**
|
||||
* A simple view to show the recipients of a message
|
||||
@@ -44,7 +46,7 @@ import org.thoughtcrime.securesms.util.Util;
|
||||
* @author Jake McGinty
|
||||
*/
|
||||
public class MessageRecipientListItem extends RelativeLayout
|
||||
implements RecipientModifiedListener
|
||||
implements RecipientForeverObserver
|
||||
{
|
||||
@SuppressWarnings("unused")
|
||||
private final static String TAG = MessageRecipientListItem.class.getSimpleName();
|
||||
@@ -84,10 +86,12 @@ public class MessageRecipientListItem extends RelativeLayout
|
||||
final RecipientDeliveryStatus member,
|
||||
final boolean isPushGroup)
|
||||
{
|
||||
if (this.member != null) this.member.getRecipient().live().removeForeverObserver(this);
|
||||
|
||||
this.glideRequests = glideRequests;
|
||||
this.member = member;
|
||||
|
||||
member.getRecipient().addListener(this);
|
||||
member.getRecipient().live().observeForever(this);
|
||||
fromView.setText(member.getRecipient());
|
||||
contactPhotoImage.setAvatar(glideRequests, member.getRecipient(), false);
|
||||
setIssueIndicators(record, isPushGroup);
|
||||
@@ -138,7 +142,7 @@ public class MessageRecipientListItem extends RelativeLayout
|
||||
private NetworkFailure getNetworkFailure(final MessageRecord record) {
|
||||
if (record.hasNetworkFailures()) {
|
||||
for (final NetworkFailure failure : record.getNetworkFailures()) {
|
||||
if (failure.getAddress().equals(member.getRecipient().getAddress())) {
|
||||
if (failure.getRecipientId(getContext()).equals(member.getRecipient().getId())) {
|
||||
return failure;
|
||||
}
|
||||
}
|
||||
@@ -149,7 +153,7 @@ public class MessageRecipientListItem extends RelativeLayout
|
||||
private IdentityKeyMismatch getKeyMismatch(final MessageRecord record) {
|
||||
if (record.isIdentityMismatchFailure()) {
|
||||
for (final IdentityKeyMismatch mismatch : record.getIdentityKeyMismatches()) {
|
||||
if (mismatch.getAddress().equals(member.getRecipient().getAddress())) {
|
||||
if (mismatch.getRecipientId(getContext()).equals(member.getRecipient().getId())) {
|
||||
return mismatch;
|
||||
}
|
||||
}
|
||||
@@ -158,14 +162,17 @@ public class MessageRecipientListItem extends RelativeLayout
|
||||
}
|
||||
|
||||
public void unbind() {
|
||||
if (this.member != null && this.member.getRecipient() != null) this.member.getRecipient().removeListener(this);
|
||||
if (this.member != null && this.member.getRecipient() != null) this.member.getRecipient().live().removeForeverObserver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(final Recipient recipient) {
|
||||
Util.runOnMain(() -> {
|
||||
public void onRecipientChanged(@NonNull Recipient recipient) {
|
||||
if (this.member != null && this.member.getRecipient().equals(recipient)) {
|
||||
Log.d(TAG, "onRecipientChanged -- valid");
|
||||
fromView.setText(recipient);
|
||||
contactPhotoImage.setAvatar(glideRequests, recipient, false);
|
||||
});
|
||||
} else {
|
||||
Log.d(TAG, "onRecipientChanged -- invalid");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,12 +22,29 @@ import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.conversation.ConversationActivity;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.UsernameUtil;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Activity container for starting a new conversation.
|
||||
@@ -35,7 +52,9 @@ import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
* @author Moxie Marlinspike
|
||||
*
|
||||
*/
|
||||
public class NewConversationActivity extends ContactSelectionActivity {
|
||||
public class NewConversationActivity extends ContactSelectionActivity
|
||||
implements ContactSelectionListFragment.InviteCallback
|
||||
{
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = NewConversationActivity.class.getSimpleName();
|
||||
@@ -48,11 +67,20 @@ public class NewConversationActivity extends ContactSelectionActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactSelected(String number) {
|
||||
Recipient recipient = Recipient.from(this, Address.fromExternal(this, number), true);
|
||||
public void onContactSelected(Optional<RecipientId> recipientId, String number) {
|
||||
Recipient recipient;
|
||||
if (recipientId.isPresent()) {
|
||||
recipient = Recipient.resolved(recipientId.get());
|
||||
} else {
|
||||
Log.i(TAG, "[onContactSelected] Maybe creating a new recipient.");
|
||||
recipient = Recipient.external(this, number);
|
||||
}
|
||||
launch(recipient);
|
||||
}
|
||||
|
||||
private void launch(Recipient recipient) {
|
||||
Intent intent = new Intent(this, ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
|
||||
intent.putExtra(ConversationActivity.RECIPIENT_EXTRA, recipient.getId());
|
||||
intent.putExtra(ConversationActivity.TEXT_EXTRA, getIntent().getStringExtra(ConversationActivity.TEXT_EXTRA));
|
||||
intent.setDataAndType(getIntent().getData(), getIntent().getType());
|
||||
|
||||
@@ -99,4 +127,9 @@ public class NewConversationActivity extends ContactSelectionActivity {
|
||||
super.onPrepareOptionsMenu(menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvite() {
|
||||
handleInvite();
|
||||
}
|
||||
}
|
||||
@@ -277,7 +277,7 @@ public class PassphrasePromptActivity extends PassphraseActivity {
|
||||
fingerprintManager.authenticate(null, 0, fingerprintCancellationSignal, fingerprintListener, null);
|
||||
} else if (Build.VERSION.SDK_INT >= 21){
|
||||
Log.i(TAG, "firing intent...");
|
||||
Intent intent = keyguardManager.createConfirmDeviceCredentialIntent("Unlock Signal", "");
|
||||
Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(getString(R.string.PassphrasePromptActivity_unlock_signal), "");
|
||||
startActivityForResult(intent, 1);
|
||||
} else {
|
||||
Log.w(TAG, "Not compatible...");
|
||||
@@ -5,16 +5,20 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.migrations.ApplicationMigrationActivity;
|
||||
import org.thoughtcrime.securesms.migrations.ApplicationMigrations;
|
||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess;
|
||||
import org.thoughtcrime.securesms.registration.WelcomeActivity;
|
||||
import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity;
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
||||
@@ -25,20 +29,19 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
||||
|
||||
public static final String LOCALE_EXTRA = "locale_extra";
|
||||
|
||||
private static final int STATE_NORMAL = 0;
|
||||
private static final int STATE_CREATE_PASSPHRASE = 1;
|
||||
private static final int STATE_PROMPT_PASSPHRASE = 2;
|
||||
private static final int STATE_UPGRADE_DATABASE = 3;
|
||||
private static final int STATE_PROMPT_PUSH_REGISTRATION = 4;
|
||||
private static final int STATE_EXPERIENCE_UPGRADE = 5;
|
||||
private static final int STATE_WELCOME_SCREEN = 6;
|
||||
private static final int STATE_NORMAL = 0;
|
||||
private static final int STATE_CREATE_PASSPHRASE = 1;
|
||||
private static final int STATE_PROMPT_PASSPHRASE = 2;
|
||||
private static final int STATE_UI_BLOCKING_UPGRADE = 3;
|
||||
private static final int STATE_EXPERIENCE_UPGRADE = 4;
|
||||
private static final int STATE_WELCOME_PUSH_SCREEN = 5;
|
||||
|
||||
private SignalServiceNetworkAccess networkAccess;
|
||||
private BroadcastReceiver clearKeyReceiver;
|
||||
|
||||
@Override
|
||||
protected final void onCreate(Bundle savedInstanceState) {
|
||||
Log.i(TAG, "onCreate(" + savedInstanceState + ")");
|
||||
Log.d(TAG, "[" + Log.tag(getClass()) + "] onCreate()");
|
||||
this.networkAccess = new SignalServiceNetworkAccess(this);
|
||||
onPreCreate();
|
||||
|
||||
@@ -58,30 +61,42 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
Log.i(TAG, "onResume()");
|
||||
Log.d(TAG, "[" + Log.tag(getClass()) + "] onResume()");
|
||||
super.onResume();
|
||||
|
||||
if (networkAccess.isCensored(this)) {
|
||||
ApplicationContext.getInstance(this).getJobManager().add(new PushNotificationReceiveJob(this));
|
||||
ApplicationDependencies.getJobManager().add(new PushNotificationReceiveJob(this));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
Log.d(TAG, "[" + Log.tag(getClass()) + "] onStart()");
|
||||
super.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
Log.i(TAG, "onPause()");
|
||||
Log.d(TAG, "[" + Log.tag(getClass()) + "] onPause()");
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
Log.d(TAG, "[" + Log.tag(getClass()) + "] onStop()");
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
Log.i(TAG, "onDestroy()");
|
||||
Log.d(TAG, "[" + Log.tag(getClass()) + "] onDestroy()");
|
||||
super.onDestroy();
|
||||
removeClearKeyReceiver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMasterSecretCleared() {
|
||||
Log.i(TAG, "onMasterSecretCleared()");
|
||||
Log.d(TAG, "onMasterSecretCleared()");
|
||||
if (ApplicationContext.getInstance(this).isAppVisible()) routeApplicationState(true);
|
||||
else finish();
|
||||
}
|
||||
@@ -127,16 +142,15 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
||||
}
|
||||
|
||||
private Intent getIntentForState(int state) {
|
||||
Log.i(TAG, "routeApplicationState(), state: " + state);
|
||||
Log.d(TAG, "routeApplicationState(), state: " + state);
|
||||
|
||||
switch (state) {
|
||||
case STATE_CREATE_PASSPHRASE: return getCreatePassphraseIntent();
|
||||
case STATE_PROMPT_PASSPHRASE: return getPromptPassphraseIntent();
|
||||
case STATE_UPGRADE_DATABASE: return getUpgradeDatabaseIntent();
|
||||
case STATE_WELCOME_SCREEN: return getWelcomeIntent();
|
||||
case STATE_PROMPT_PUSH_REGISTRATION: return getPushRegistrationIntent();
|
||||
case STATE_EXPERIENCE_UPGRADE: return getExperienceUpgradeIntent();
|
||||
default: return null;
|
||||
case STATE_CREATE_PASSPHRASE: return getCreatePassphraseIntent();
|
||||
case STATE_PROMPT_PASSPHRASE: return getPromptPassphraseIntent();
|
||||
case STATE_UI_BLOCKING_UPGRADE: return getUiBlockingUpgradeIntent();
|
||||
case STATE_WELCOME_PUSH_SCREEN: return getPushRegistrationIntent();
|
||||
case STATE_EXPERIENCE_UPGRADE: return getExperienceUpgradeIntent();
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,12 +159,10 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
||||
return STATE_CREATE_PASSPHRASE;
|
||||
} else if (locked) {
|
||||
return STATE_PROMPT_PASSPHRASE;
|
||||
} else if (DatabaseUpgradeActivity.isUpdate(this)) {
|
||||
return STATE_UPGRADE_DATABASE;
|
||||
} else if (!TextSecurePreferences.hasSeenWelcomeScreen(this)) {
|
||||
return STATE_WELCOME_SCREEN;
|
||||
} else if (ApplicationMigrations.isUpdate(this) && ApplicationMigrations.isUiBlockingMigrationRunning()) {
|
||||
return STATE_UI_BLOCKING_UPGRADE;
|
||||
} else if (!TextSecurePreferences.hasPromptedPushRegistration(this)) {
|
||||
return STATE_PROMPT_PUSH_REGISTRATION;
|
||||
return STATE_WELCOME_PUSH_SCREEN;
|
||||
} else if (ExperienceUpgradeActivity.isUpdate(this)) {
|
||||
return STATE_EXPERIENCE_UPGRADE;
|
||||
} else {
|
||||
@@ -166,8 +178,8 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
||||
return getRoutedIntent(PassphrasePromptActivity.class, getIntent());
|
||||
}
|
||||
|
||||
private Intent getUpgradeDatabaseIntent() {
|
||||
return getRoutedIntent(DatabaseUpgradeActivity.class,
|
||||
private Intent getUiBlockingUpgradeIntent() {
|
||||
return getRoutedIntent(ApplicationMigrationActivity.class,
|
||||
TextSecurePreferences.hasPromptedPushRegistration(this)
|
||||
? getConversationListIntent()
|
||||
: getPushRegistrationIntent());
|
||||
@@ -177,16 +189,8 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
||||
return getRoutedIntent(ExperienceUpgradeActivity.class, getIntent());
|
||||
}
|
||||
|
||||
private Intent getWelcomeIntent() {
|
||||
return getRoutedIntent(WelcomeActivity.class, getPushRegistrationIntent());
|
||||
}
|
||||
|
||||
private Intent getPushRegistrationIntent() {
|
||||
return getRoutedIntent(RegistrationActivity.class, getCreateProfileIntent());
|
||||
}
|
||||
|
||||
private Intent getCreateProfileIntent() {
|
||||
return getRoutedIntent(CreateProfileActivity.class, getConversationListIntent());
|
||||
return RegistrationNavigationActivity.newIntentForNewRegistration(this);
|
||||
}
|
||||
|
||||
private Intent getRoutedIntent(Class<?> destination, @Nullable Intent nextIntent) {
|
||||
@@ -196,11 +200,11 @@ public abstract class PassphraseRequiredActionBarActivity extends BaseActionBarA
|
||||
}
|
||||
|
||||
private Intent getConversationListIntent() {
|
||||
return new Intent(this, ConversationListActivity.class);
|
||||
// TODO [greyson] Navigation
|
||||
return new Intent(this, MainActivity.class);
|
||||
}
|
||||
|
||||
private void initializeClearKeyReceiver() {
|
||||
Log.i(TAG, "initializeClearKeyReceiver()");
|
||||
this.clearKeyReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
@@ -19,6 +19,11 @@ package org.thoughtcrime.securesms;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.SelectedContact;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -30,6 +35,8 @@ import java.util.List;
|
||||
*/
|
||||
public class PushContactSelectionActivity extends ContactSelectionActivity {
|
||||
|
||||
public static final String KEY_SELECTED_RECIPIENTS = "recipients";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final static String TAG = PushContactSelectionActivity.class.getSimpleName();
|
||||
|
||||
@@ -38,14 +45,13 @@ public class PushContactSelectionActivity extends ContactSelectionActivity {
|
||||
getIntent().putExtra(ContactSelectionListFragment.MULTI_SELECT, true);
|
||||
super.onCreate(icicle, ready);
|
||||
|
||||
getToolbar().setNavigationIcon(R.drawable.ic_check_white_24dp);
|
||||
getToolbar().setNavigationIcon(R.drawable.ic_check_24);
|
||||
getToolbar().setNavigationOnClickListener(v -> {
|
||||
Intent resultIntent = getIntent();
|
||||
List<String> selectedContacts = contactsFragment.getSelectedContacts();
|
||||
Intent resultIntent = getIntent();
|
||||
List<SelectedContact> selectedContacts = contactsFragment.getSelectedContacts();
|
||||
List<RecipientId> recipients = Stream.of(selectedContacts).map(sc -> sc.getOrCreateRecipientId(this)).toList();
|
||||
|
||||
if (selectedContacts != null) {
|
||||
resultIntent.putStringArrayListExtra("contacts", new ArrayList<>(selectedContacts));
|
||||
}
|
||||
resultIntent.putParcelableArrayListExtra(KEY_SELECTED_RECIPIENTS, new ArrayList<>(recipients));
|
||||
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
finish();
|
||||
@@ -9,6 +9,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceConfigurationUpdateJob;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
@@ -37,12 +38,11 @@ public class ReadReceiptsIntroFragment extends Fragment {
|
||||
preference.setChecked(TextSecurePreferences.isReadReceiptsEnabled(getContext()));
|
||||
preference.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
TextSecurePreferences.setReadReceiptsEnabled(getContext(), isChecked);
|
||||
ApplicationContext.getInstance(getContext())
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceConfigurationUpdateJob(isChecked,
|
||||
TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()),
|
||||
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()),
|
||||
TextSecurePreferences.isLinkPreviewsEnabled(getContext())));
|
||||
ApplicationDependencies.getJobManager()
|
||||
.add(new MultiDeviceConfigurationUpdateJob(isChecked,
|
||||
TextSecurePreferences.isTypingIndicatorsEnabled(requireContext()),
|
||||
TextSecurePreferences.isShowUnidentifiedDeliveryIndicatorsEnabled(getContext()),
|
||||
TextSecurePreferences.isLinkPreviewsEnabled(getContext())));
|
||||
});
|
||||
|
||||
return v;
|
||||
@@ -13,84 +13,87 @@ import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import android.util.Pair;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.Loader;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.preference.CheckBoxPreference;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import android.telephony.PhoneNumberUtils;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout;
|
||||
|
||||
import org.thoughtcrime.securesms.color.MaterialColor;
|
||||
import org.thoughtcrime.securesms.color.MaterialColors;
|
||||
import org.thoughtcrime.securesms.components.SwitchPreferenceCompat;
|
||||
import org.thoughtcrime.securesms.components.ThreadPhotoRailView;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.jobs.RotateProfileKeyJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
|
||||
import org.thoughtcrime.securesms.color.MaterialColor;
|
||||
import org.thoughtcrime.securesms.color.MaterialColors;
|
||||
import org.thoughtcrime.securesms.components.ThreadPhotoRailView;
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyParcelable;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase;
|
||||
import org.thoughtcrime.securesms.database.IdentityDatabase.IdentityRecord;
|
||||
import org.thoughtcrime.securesms.database.MediaDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase.VibrateState;
|
||||
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceBlockedUpdateJob;
|
||||
import org.thoughtcrime.securesms.database.loaders.RecipientMediaLoader;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.permissions.Permissions;
|
||||
import org.thoughtcrime.securesms.preferences.CorrectedPreferenceFragment;
|
||||
import org.thoughtcrime.securesms.preferences.widgets.ColorPickerPreference;
|
||||
import org.thoughtcrime.securesms.preferences.widgets.ContactPreference;
|
||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientModifiedListener;
|
||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.Dialogs;
|
||||
import org.thoughtcrime.securesms.util.DynamicDarkToolbarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SignalExecutors;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActivity implements RecipientModifiedListener, LoaderManager.LoaderCallbacks<Cursor>
|
||||
public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActivity implements LoaderManager.LoaderCallbacks<Cursor>
|
||||
{
|
||||
private static final String TAG = RecipientPreferenceActivity.class.getSimpleName();
|
||||
|
||||
public static final String ADDRESS_EXTRA = "recipient_address";
|
||||
public static final String RECIPIENT_ID = "recipient_address";
|
||||
public static final String CAN_HAVE_SAFETY_NUMBER_EXTRA = "can_have_safety_number";
|
||||
|
||||
private static final String PREFERENCE_MUTED = "pref_key_recipient_mute";
|
||||
@@ -104,12 +107,12 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
private static final String PREFERENCE_ABOUT = "pref_key_number";
|
||||
private static final String PREFERENCE_CUSTOM_NOTIFICATIONS = "pref_key_recipient_custom_notifications";
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
|
||||
private final DynamicTheme dynamicTheme = new DynamicDarkToolbarTheme();
|
||||
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||
|
||||
private ImageView avatar;
|
||||
private GlideRequests glideRequests;
|
||||
private Address address;
|
||||
private RecipientId recipientId;
|
||||
private TextView threadPhotoRailLabel;
|
||||
private ThreadPhotoRailView threadPhotoRailView;
|
||||
private CollapsingToolbarLayout toolbarLayout;
|
||||
@@ -124,13 +127,13 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
public void onCreate(Bundle instanceState, boolean ready) {
|
||||
setContentView(R.layout.recipient_preference_activity);
|
||||
this.glideRequests = GlideApp.with(this);
|
||||
this.address = getIntent().getParcelableExtra(ADDRESS_EXTRA);
|
||||
this.recipientId = getIntent().getParcelableExtra(RECIPIENT_ID);
|
||||
|
||||
Recipient recipient = Recipient.from(this, address, true);
|
||||
LiveRecipient recipient = Recipient.live(recipientId);
|
||||
|
||||
initializeToolbar();
|
||||
setHeader(recipient);
|
||||
recipient.addListener(this);
|
||||
setHeader(recipient.get());
|
||||
recipient.observe(this, this::setHeader);
|
||||
|
||||
getSupportLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
@@ -173,13 +176,12 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
this.threadPhotoRailView = ViewUtil.findById(this, R.id.recent_photos);
|
||||
this.threadPhotoRailLabel = ViewUtil.findById(this, R.id.rail_label);
|
||||
|
||||
this.toolbarLayout.setExpandedTitleColor(getResources().getColor(R.color.white));
|
||||
this.toolbarLayout.setCollapsedTitleTextColor(getResources().getColor(R.color.white));
|
||||
this.toolbarLayout.setExpandedTitleColor(ThemeUtil.getThemedColor(this, R.attr.conversation_title_color));
|
||||
this.toolbarLayout.setCollapsedTitleTextColor(ThemeUtil.getThemedColor(this, R.attr.conversation_title_color));
|
||||
|
||||
this.threadPhotoRailView.setListener(mediaRecord -> {
|
||||
Intent intent = new Intent(RecipientPreferenceActivity.this, MediaPreviewActivity.class);
|
||||
intent.putExtra(MediaPreviewActivity.ADDRESS_EXTRA, address);
|
||||
intent.putExtra(MediaPreviewActivity.OUTGOING_EXTRA, mediaRecord.isOutgoing());
|
||||
intent.putExtra(MediaPreviewActivity.THREAD_ID_EXTRA, mediaRecord.getThreadId());
|
||||
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, mediaRecord.getDate());
|
||||
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, mediaRecord.getAttachment().getSize());
|
||||
intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, mediaRecord.getAttachment().getCaption());
|
||||
@@ -188,11 +190,16 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
startActivity(intent);
|
||||
});
|
||||
|
||||
this.threadPhotoRailLabel.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(this, MediaOverviewActivity.class);
|
||||
intent.putExtra(MediaOverviewActivity.ADDRESS_EXTRA, address);
|
||||
startActivity(intent);
|
||||
});
|
||||
SimpleTask.run(
|
||||
() -> DatabaseFactory.getThreadDatabase(this).getThreadIdFor(recipientId),
|
||||
(threadId) -> {
|
||||
if (threadId == null) {
|
||||
Log.i(TAG, "No thread id for recipient.");
|
||||
} else {
|
||||
this.threadPhotoRailLabel.setOnClickListener(v -> startActivity(MediaOverviewActivity.forThread(this, threadId)));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Toolbar toolbar = ViewUtil.findById(this, R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
@@ -202,13 +209,18 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
getWindow().setStatusBarColor(Color.TRANSPARENT);
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.recipient_preference_root), (v, insets) -> {
|
||||
ViewUtil.setTopMargin(toolbar, insets.getSystemWindowInsetTop());
|
||||
return insets;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void setHeader(@NonNull Recipient recipient) {
|
||||
ContactPhoto contactPhoto = recipient.isLocalNumber() ? new ProfileContactPhoto(recipient.getAddress(), String.valueOf(TextSecurePreferences.getProfileAvatarId(this)))
|
||||
ContactPhoto contactPhoto = recipient.isLocalNumber() ? new ProfileContactPhoto(recipient.getId(), String.valueOf(TextSecurePreferences.getProfileAvatarId(this)))
|
||||
: recipient.getContactPhoto();
|
||||
FallbackContactPhoto fallbackPhoto = recipient.isLocalNumber() ? new ResourceContactPhoto(R.drawable.ic_profile_default, R.drawable.ic_person_large)
|
||||
FallbackContactPhoto fallbackPhoto = recipient.isLocalNumber() ? new ResourceContactPhoto(R.drawable.ic_profile_outline_40, R.drawable.ic_profile_outline_20, R.drawable.ic_person_large)
|
||||
: recipient.getFallbackContactPhoto();
|
||||
|
||||
glideRequests.load(contactPhoto)
|
||||
@@ -221,18 +233,13 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
else this.avatar.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
|
||||
this.avatar.setBackgroundColor(recipient.getColor().toActionBarColor(this));
|
||||
this.toolbarLayout.setTitle(recipient.toShortString());
|
||||
this.toolbarLayout.setTitle(recipient.toShortString(this));
|
||||
this.toolbarLayout.setContentScrimColor(recipient.getColor().toActionBarColor(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(final Recipient recipient) {
|
||||
Util.runOnMain(() -> setHeader(recipient));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
return new ThreadMediaLoader(this, address, true);
|
||||
return new RecipientMediaLoader(this, recipientId, RecipientMediaLoader.MediaType.GALLERY, MediaDatabase.Sorting.Newest);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -248,7 +255,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
this.threadPhotoRailView.setCursor(glideRequests, data);
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(ADDRESS_EXTRA, address);
|
||||
bundle.putParcelable(RECIPIENT_ID, recipientId);
|
||||
initFragment(R.id.preference_fragment, new RecipientPreferenceFragment(), null, bundle);
|
||||
}
|
||||
|
||||
@@ -257,12 +264,9 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
this.threadPhotoRailView.setCursor(glideRequests, null);
|
||||
}
|
||||
|
||||
public static class RecipientPreferenceFragment
|
||||
extends CorrectedPreferenceFragment
|
||||
implements RecipientModifiedListener
|
||||
{
|
||||
private Recipient recipient;
|
||||
private boolean canHaveSafetyNumber;
|
||||
public static class RecipientPreferenceFragment extends CorrectedPreferenceFragment {
|
||||
private LiveRecipient recipient;
|
||||
private boolean canHaveSafetyNumber;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
@@ -277,20 +281,20 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
Preference customNotificationsPref = this.findPreference(PREFERENCE_CUSTOM_NOTIFICATIONS);
|
||||
|
||||
if (NotificationChannels.supported()) {
|
||||
((SwitchPreferenceCompat) customNotificationsPref).setChecked(recipient.getNotificationChannel() != null);
|
||||
((SwitchPreferenceCompat) customNotificationsPref).setChecked(recipient.get().getNotificationChannel() != null);
|
||||
customNotificationsPref.setOnPreferenceChangeListener(new CustomNotificationsChangedListener());
|
||||
|
||||
this.findPreference(PREFERENCE_MESSAGE_TONE).setDependency(PREFERENCE_CUSTOM_NOTIFICATIONS);
|
||||
this.findPreference(PREFERENCE_MESSAGE_VIBRATE).setDependency(PREFERENCE_CUSTOM_NOTIFICATIONS);
|
||||
|
||||
if (recipient.getNotificationChannel() != null) {
|
||||
final Context context = getContext();
|
||||
if (recipient.get().getNotificationChannel() != null) {
|
||||
final Context context = requireContext();
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
RecipientDatabase db = DatabaseFactory.getRecipientDatabase(getContext());
|
||||
db.setMessageRingtone(recipient, NotificationChannels.getMessageRingtone(context, recipient));
|
||||
db.setMessageVibrate(recipient, NotificationChannels.getMessageVibrate(context, recipient) ? VibrateState.ENABLED : VibrateState.DISABLED);
|
||||
db.setMessageRingtone(recipient.getId(), NotificationChannels.getMessageRingtone(context, recipient.get()));
|
||||
db.setMessageVibrate(recipient.getId(), NotificationChannels.getMessageVibrate(context, recipient.get()) ? VibrateState.ENABLED : VibrateState.DISABLED);
|
||||
NotificationChannels.ensureCustomChannelConsistency(context);
|
||||
return null;
|
||||
}
|
||||
@@ -336,13 +340,12 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
setSummaries(recipient);
|
||||
setSummaries(recipient.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
this.recipient.removeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -358,9 +361,17 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
|
||||
RecyclerView recyclerView = super.onCreateRecyclerView(inflater, parent, savedInstanceState);
|
||||
recyclerView.setItemAnimator(null);
|
||||
recyclerView.setLayoutAnimation(null);
|
||||
return recyclerView;
|
||||
}
|
||||
|
||||
private void initializeRecipients() {
|
||||
this.recipient = Recipient.from(getActivity(), getArguments().getParcelable(ADDRESS_EXTRA), true);
|
||||
this.recipient.addListener(this);
|
||||
this.recipient = Recipient.live(getArguments().getParcelable(RECIPIENT_ID));
|
||||
this.recipient.observe(this, this::setSummaries);
|
||||
}
|
||||
|
||||
private void setSummaries(Recipient recipient) {
|
||||
@@ -394,6 +405,10 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
vibrateCallPreference.setSummary(vibrateCallSummary.first);
|
||||
vibrateCallPreference.setValueIndex(vibrateCallSummary.second);
|
||||
|
||||
blockPreference.setVisible(RecipientUtil.isBlockable(recipient));
|
||||
if (recipient.isBlocked()) blockPreference.setTitle(R.string.RecipientPreferenceActivity_unblock);
|
||||
else blockPreference.setTitle(R.string.RecipientPreferenceActivity_block);
|
||||
|
||||
if (recipient.isLocalNumber()) {
|
||||
mutePreference.setVisible(false);
|
||||
customPreference.setVisible(false);
|
||||
@@ -406,7 +421,9 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
if (privacyCategory != null) privacyCategory.setVisible(false);
|
||||
if (divider != null) divider.setVisible(false);
|
||||
if (callCategory != null) callCategory.setVisible(false);
|
||||
} if (recipient.isGroupRecipient()) {
|
||||
}
|
||||
|
||||
if (recipient.isGroup()) {
|
||||
if (colorPreference != null) colorPreference.setVisible(false);
|
||||
if (identityPreference != null) identityPreference.setVisible(false);
|
||||
if (callCategory != null) callCategory.setVisible(false);
|
||||
@@ -414,16 +431,19 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
if (aboutDivider != null) aboutDivider.setVisible(false);
|
||||
if (divider != null) divider.setVisible(false);
|
||||
} else {
|
||||
colorPreference.setColors(MaterialColors.CONVERSATION_PALETTE.asConversationColorArray(getActivity()));
|
||||
colorPreference.setColor(recipient.getColor().toActionBarColor(getActivity()));
|
||||
colorPreference.setColors(MaterialColors.CONVERSATION_PALETTE.asConversationColorArray(requireActivity()));
|
||||
colorPreference.setColor(recipient.getColor().toActionBarColor(requireActivity()));
|
||||
|
||||
if (FeatureFlags.PROFILE_DISPLAY) {
|
||||
aboutPreference.setTitle(recipient.getDisplayName(requireContext()));
|
||||
aboutPreference.setSummary(recipient.resolve().getE164().or(""));
|
||||
} else {
|
||||
aboutPreference.setTitle(formatRecipient(recipient));
|
||||
aboutPreference.setSummary(recipient.getCustomLabel());
|
||||
}
|
||||
|
||||
aboutPreference.setTitle(formatAddress(recipient.getAddress()));
|
||||
aboutPreference.setSummary(recipient.getCustomLabel());
|
||||
aboutPreference.setSecure(recipient.getRegistered() == RecipientDatabase.RegisteredState.REGISTERED);
|
||||
|
||||
if (recipient.isBlocked()) blockPreference.setTitle(R.string.RecipientPreferenceActivity_unblock);
|
||||
else blockPreference.setTitle(R.string.RecipientPreferenceActivity_block);
|
||||
|
||||
IdentityUtil.getRemoteIdentityKey(getActivity(), recipient).addListener(new ListenableFuture.Listener<Optional<IdentityRecord>>() {
|
||||
@Override
|
||||
public void onSuccess(Optional<IdentityRecord> result) {
|
||||
@@ -444,12 +464,16 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (recipient.isMmsGroup() && privacyCategory != null) {
|
||||
privacyCategory.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
private @NonNull String formatAddress(@NonNull Address address) {
|
||||
if (address.isPhone()) return PhoneNumberUtils.formatNumber(address.toPhoneString());
|
||||
else if (address.isEmail()) return address.toEmailString();
|
||||
else return "";
|
||||
private @NonNull String formatRecipient(@NonNull Recipient recipient) {
|
||||
if (recipient.getE164().isPresent()) return PhoneNumberUtils.formatNumber(recipient.requireE164());
|
||||
else if (recipient.getEmail().isPresent()) return recipient.requireEmail();
|
||||
else return "";
|
||||
}
|
||||
|
||||
private @NonNull String getRingtoneSummary(@NonNull Context context, @Nullable Uri ringtone) {
|
||||
@@ -478,15 +502,6 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModified(final Recipient recipient) {
|
||||
Util.runOnMain(() -> {
|
||||
if (getContext() != null && getActivity() != null && !getActivity().isFinishing()) {
|
||||
setSummaries(recipient);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class RingtoneChangeListener implements Preference.OnPreferenceChangeListener {
|
||||
|
||||
private final boolean calls;
|
||||
@@ -514,10 +529,10 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
@Override
|
||||
protected Void doInBackground(Uri... params) {
|
||||
if (calls) {
|
||||
DatabaseFactory.getRecipientDatabase(context).setCallRingtone(recipient, params[0]);
|
||||
DatabaseFactory.getRecipientDatabase(context).setCallRingtone(recipient.getId(), params[0]);
|
||||
} else {
|
||||
DatabaseFactory.getRecipientDatabase(context).setMessageRingtone(recipient, params[0]);
|
||||
NotificationChannels.updateMessageRingtone(context, recipient, params[0]);
|
||||
DatabaseFactory.getRecipientDatabase(context).setMessageRingtone(recipient.getId(), params[0]);
|
||||
NotificationChannels.updateMessageRingtone(context, recipient.get(), params[0]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -541,10 +556,10 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
Uri defaultUri;
|
||||
|
||||
if (calls) {
|
||||
current = recipient.getCallRingtone();
|
||||
current = recipient.get().getCallRingtone();
|
||||
defaultUri = TextSecurePreferences.getCallNotificationRingtone(getContext());
|
||||
} else {
|
||||
current = recipient.getMessageRingtone();
|
||||
current = recipient.get().getMessageRingtone();
|
||||
defaultUri = TextSecurePreferences.getNotificationRingtone(getContext());
|
||||
}
|
||||
|
||||
@@ -582,11 +597,11 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
if (call) {
|
||||
DatabaseFactory.getRecipientDatabase(context).setCallVibrate(recipient, vibrateState);
|
||||
DatabaseFactory.getRecipientDatabase(context).setCallVibrate(recipient.getId(), vibrateState);
|
||||
}
|
||||
else {
|
||||
DatabaseFactory.getRecipientDatabase(context).setMessageVibrate(recipient, vibrateState);
|
||||
NotificationChannels.updateMessageVibrate(context, recipient, vibrateState);
|
||||
DatabaseFactory.getRecipientDatabase(context).setMessageVibrate(recipient.getId(), vibrateState);
|
||||
NotificationChannels.updateMessageVibrate(context, recipient.get(), vibrateState);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -605,7 +620,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
|
||||
final int value = (Integer) newValue;
|
||||
final MaterialColor selectedColor = MaterialColors.CONVERSATION_PALETTE.getByColor(context, value);
|
||||
final MaterialColor currentColor = recipient.getColor();
|
||||
final MaterialColor currentColor = recipient.get().getColor();
|
||||
|
||||
if (selectedColor == null) return true;
|
||||
|
||||
@@ -613,12 +628,10 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getRecipientDatabase(context).setColor(recipient, selectedColor);
|
||||
DatabaseFactory.getRecipientDatabase(context).setColor(recipient.getId(), selectedColor);
|
||||
|
||||
if (recipient.resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceContactUpdateJob(context, recipient.getAddress()));
|
||||
if (recipient.get().resolve().getRegistered() == RecipientDatabase.RegisteredState.REGISTERED) {
|
||||
ApplicationDependencies.getJobManager().add(new MultiDeviceContactUpdateJob(recipient.getId()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -632,30 +645,28 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
if (recipient.isMuted()) handleUnmute(preference.getContext());
|
||||
else handleMute(preference.getContext());
|
||||
if (recipient.get().isMuted()) handleUnmute(preference.getContext());
|
||||
else handleMute(preference.getContext());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleMute(@NonNull Context context) {
|
||||
MuteDialog.show(context, until -> setMuted(context, recipient, until));
|
||||
MuteDialog.show(context, until -> setMuted(context, recipient.get(), until));
|
||||
|
||||
setSummaries(recipient);
|
||||
setSummaries(recipient.get());
|
||||
}
|
||||
|
||||
private void handleUnmute(@NonNull Context context) {
|
||||
setMuted(context, recipient, 0);
|
||||
setMuted(context, recipient.get(), 0);
|
||||
}
|
||||
|
||||
private void setMuted(@NonNull final Context context, final Recipient recipient, final long until) {
|
||||
recipient.setMuted(until);
|
||||
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getRecipientDatabase(context)
|
||||
.setMuted(recipient, until);
|
||||
.setMuted(recipient.getId(), until);
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
@@ -674,7 +685,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent verifyIdentityIntent = new Intent(preference.getContext(), VerifyIdentityActivity.class);
|
||||
verifyIdentityIntent.putExtra(VerifyIdentityActivity.ADDRESS_EXTRA, recipient.getAddress());
|
||||
verifyIdentityIntent.putExtra(VerifyIdentityActivity.RECIPIENT_EXTRA, recipient.getId());
|
||||
verifyIdentityIntent.putExtra(VerifyIdentityActivity.IDENTITY_EXTRA, new IdentityKeyParcelable(identityKey.getIdentityKey()));
|
||||
verifyIdentityIntent.putExtra(VerifyIdentityActivity.VERIFIED_EXTRA, identityKey.getVerifiedStatus() == IdentityDatabase.VerifiedStatus.VERIFIED);
|
||||
startActivity(verifyIdentityIntent);
|
||||
@@ -686,8 +697,8 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
private class BlockClickedListener implements Preference.OnPreferenceClickListener {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
if (recipient.isBlocked()) handleUnblock(preference.getContext());
|
||||
else handleBlock(preference.getContext());
|
||||
if (recipient.get().isBlocked()) handleUnblock(preference.getContext());
|
||||
else handleBlock(preference.getContext());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -700,10 +711,10 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
int titleRes = R.string.RecipientPreferenceActivity_block_this_contact_question;
|
||||
int bodyRes = R.string.RecipientPreferenceActivity_you_will_no_longer_receive_messages_and_calls_from_this_contact;
|
||||
|
||||
if (recipient.isGroupRecipient()) {
|
||||
if (recipient.get().isGroup()) {
|
||||
bodyRes = R.string.RecipientPreferenceActivity_block_and_leave_group_description;
|
||||
|
||||
if (recipient.isGroupRecipient() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.getAddress().toGroupString())) {
|
||||
if (recipient.get().isGroup() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.get().requireGroupId())) {
|
||||
titleRes = R.string.RecipientPreferenceActivity_block_and_leave_group;
|
||||
} else {
|
||||
titleRes = R.string.RecipientPreferenceActivity_block_group;
|
||||
@@ -721,7 +732,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.RecipientPreferenceActivity_block, (dialog, which) -> {
|
||||
setBlocked(context, recipient, true);
|
||||
setBlocked(context, recipient.get(), true);
|
||||
}).show();
|
||||
}
|
||||
}.execute();
|
||||
@@ -731,7 +742,7 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
int titleRes = R.string.RecipientPreferenceActivity_unblock_this_contact_question;
|
||||
int bodyRes = R.string.RecipientPreferenceActivity_you_will_once_again_be_able_to_receive_messages_and_calls_from_this_contact;
|
||||
|
||||
if (recipient.isGroupRecipient()) {
|
||||
if (recipient.resolve().isGroup()) {
|
||||
titleRes = R.string.RecipientPreferenceActivity_unblock_this_group_question;
|
||||
bodyRes = R.string.RecipientPreferenceActivity_unblock_this_group_description;
|
||||
}
|
||||
@@ -741,45 +752,17 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
.setMessage(bodyRes)
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.RecipientPreferenceActivity_unblock, (dialog, which) -> setBlocked(context, recipient, false)).show();
|
||||
.setPositiveButton(R.string.RecipientPreferenceActivity_unblock, (dialog, which) -> setBlocked(context, recipient.get(), false)).show();
|
||||
}
|
||||
|
||||
private void setBlocked(@NonNull final Context context, final Recipient recipient, final boolean blocked) {
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
DatabaseFactory.getRecipientDatabase(context)
|
||||
.setBlocked(recipient, blocked);
|
||||
|
||||
if (recipient.isGroupRecipient() && DatabaseFactory.getGroupDatabase(context).isActive(recipient.getAddress().toGroupString())) {
|
||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient);
|
||||
Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, recipient);
|
||||
|
||||
if (threadId != -1 && leaveMessage.isPresent()) {
|
||||
MessageSender.send(context, leaveMessage.get(), threadId, false, null);
|
||||
|
||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||
String groupId = recipient.getAddress().toGroupString();
|
||||
groupDatabase.setActive(groupId, false);
|
||||
groupDatabase.remove(groupId, Address.fromSerialized(TextSecurePreferences.getLocalNumber(context)));
|
||||
} else {
|
||||
Log.w(TAG, "Failed to leave group. Can't block.");
|
||||
Toast.makeText(context, R.string.RecipientPreferenceActivity_error_leaving_group, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
if (blocked && (recipient.resolve().isSystemContact() || recipient.resolve().isProfileSharing())) {
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new RotateProfileKeyJob());
|
||||
}
|
||||
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new MultiDeviceBlockedUpdateJob());
|
||||
return null;
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
if (blocked) {
|
||||
RecipientUtil.block(context, recipient);
|
||||
} else {
|
||||
RecipientUtil.unblock(context, recipient);
|
||||
}
|
||||
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -787,19 +770,24 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
|
||||
@Override
|
||||
public void onMessageClicked() {
|
||||
CommunicationActions.startConversation(getContext(), recipient, null);
|
||||
CommunicationActions.startConversation(getContext(), recipient.get(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSecureCallClicked() {
|
||||
CommunicationActions.startVoiceCall(getActivity(), recipient);
|
||||
CommunicationActions.startVoiceCall(getActivity(), recipient.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSecureVideoClicked() {
|
||||
CommunicationActions.startVideoCall(getActivity(), recipient.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInSecureCallClicked() {
|
||||
try {
|
||||
Intent dialIntent = new Intent(Intent.ACTION_DIAL,
|
||||
Uri.parse("tel:" + recipient.getAddress().serialize()));
|
||||
Uri.parse("tel:" + recipient.get().requireE164()));
|
||||
startActivity(dialIntent);
|
||||
} catch (ActivityNotFoundException anfe) {
|
||||
Log.w(TAG, anfe);
|
||||
@@ -821,11 +809,11 @@ public class RecipientPreferenceActivity extends PassphraseRequiredActionBarActi
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
if (enabled) {
|
||||
String channel = NotificationChannels.createChannelFor(context, recipient);
|
||||
DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, channel);
|
||||
String channel = NotificationChannels.createChannelFor(context, recipient.get());
|
||||
DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient.getId(), channel);
|
||||
} else {
|
||||
NotificationChannels.deleteChannelFor(context, recipient);
|
||||
DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient, null);
|
||||
NotificationChannels.deleteChannelFor(context, recipient.get());
|
||||
DatabaseFactory.getRecipientDatabase(context).setNotificationChannel(recipient.getId(), null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Process;
|
||||
import android.provider.OpenableColumns;
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -39,7 +38,6 @@ import android.widget.ImageView;
|
||||
import org.thoughtcrime.securesms.components.SearchToolbar;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationActivity;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
@@ -47,6 +45,7 @@ import org.thoughtcrime.securesms.mediasend.Media;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.providers.BlobProvider;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.stickers.StickerLocator;
|
||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
@@ -55,6 +54,9 @@ import org.thoughtcrime.securesms.util.FileUtils;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.concurrent.SimpleTask;
|
||||
import org.whispersystems.libsignal.util.Pair;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -72,7 +74,7 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
private static final String TAG = ShareActivity.class.getSimpleName();
|
||||
|
||||
public static final String EXTRA_THREAD_ID = "thread_id";
|
||||
public static final String EXTRA_ADDRESS_MARSHALLED = "address_marshalled";
|
||||
public static final String EXTRA_RECIPIENT_ID = "recipient_id";
|
||||
public static final String EXTRA_DISTRIBUTION_TYPE = "distribution_type";
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicNoActionBarTheme();
|
||||
@@ -95,10 +97,13 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle, boolean ready) {
|
||||
if (!getIntent().hasExtra(ContactSelectionListFragment.DISPLAY_MODE)) {
|
||||
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE,
|
||||
TextSecurePreferences.isSmsEnabled(this)
|
||||
? DisplayMode.FLAG_ALL
|
||||
: DisplayMode.FLAG_PUSH | DisplayMode.FLAG_GROUPS);
|
||||
int mode = DisplayMode.FLAG_PUSH | DisplayMode.FLAG_ACTIVE_GROUPS;
|
||||
|
||||
if (TextSecurePreferences.isSmsEnabled(this)) {
|
||||
mode |= DisplayMode.FLAG_SMS;
|
||||
|
||||
}
|
||||
getIntent().putExtra(ContactSelectionListFragment.DISPLAY_MODE, mode);
|
||||
}
|
||||
|
||||
getIntent().putExtra(ContactSelectionListFragment.REFRESHABLE, false);
|
||||
@@ -216,35 +221,30 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
private void handleResolvedMedia(Intent intent, boolean animate) {
|
||||
long threadId = intent.getLongExtra(EXTRA_THREAD_ID, -1);
|
||||
int distributionType = intent.getIntExtra(EXTRA_DISTRIBUTION_TYPE, -1);
|
||||
Address address = null;
|
||||
long threadId = intent.getLongExtra(EXTRA_THREAD_ID, -1);
|
||||
int distributionType = intent.getIntExtra(EXTRA_DISTRIBUTION_TYPE, -1);
|
||||
RecipientId recipientId = null;
|
||||
|
||||
if (intent.hasExtra(EXTRA_ADDRESS_MARSHALLED)) {
|
||||
Parcel parcel = Parcel.obtain();
|
||||
byte[] marshalled = intent.getByteArrayExtra(EXTRA_ADDRESS_MARSHALLED);
|
||||
parcel.unmarshall(marshalled, 0, marshalled.length);
|
||||
parcel.setDataPosition(0);
|
||||
address = parcel.readParcelable(getClassLoader());
|
||||
parcel.recycle();
|
||||
if (intent.hasExtra(EXTRA_RECIPIENT_ID)) {
|
||||
recipientId = RecipientId.from(intent.getStringExtra(EXTRA_RECIPIENT_ID));
|
||||
}
|
||||
|
||||
boolean hasResolvedDestination = threadId != -1 && address != null && distributionType != -1;
|
||||
boolean hasResolvedDestination = threadId != -1 && recipientId != null && distributionType != -1;
|
||||
|
||||
if (!hasResolvedDestination && animate) {
|
||||
ViewUtil.fadeIn(contactsFragment.getView(), 300);
|
||||
if (hasResolvedDestination) {
|
||||
createConversation(threadId, recipientId, distributionType);
|
||||
} else if (animate) {
|
||||
ViewUtil.fadeIn(contactsFragment.requireView(), 300);
|
||||
ViewUtil.fadeOut(progressWheel, 300);
|
||||
} else if (!hasResolvedDestination) {
|
||||
contactsFragment.getView().setVisibility(View.VISIBLE);
|
||||
progressWheel.setVisibility(View.GONE);
|
||||
} else {
|
||||
createConversation(threadId, address, distributionType);
|
||||
contactsFragment.requireView().setVisibility(View.VISIBLE);
|
||||
progressWheel.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void createConversation(long threadId, Address address, int distributionType) {
|
||||
private void createConversation(long threadId, @NonNull RecipientId recipientId, int distributionType) {
|
||||
final Intent intent = getBaseShareIntent(ConversationActivity.class);
|
||||
intent.putExtra(ConversationActivity.ADDRESS_EXTRA, address);
|
||||
intent.putExtra(ConversationActivity.RECIPIENT_EXTRA, recipientId);
|
||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
||||
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, distributionType);
|
||||
|
||||
@@ -276,14 +276,25 @@ public class ShareActivity extends PassphraseRequiredActionBarActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactSelected(String number) {
|
||||
Recipient recipient = Recipient.from(this, Address.fromExternal(this, number), true);
|
||||
long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient);
|
||||
createConversation(existingThread, recipient.getAddress(), ThreadDatabase.DistributionTypes.DEFAULT);
|
||||
public void onContactSelected(Optional<RecipientId> recipientId, String number) {
|
||||
SimpleTask.run(this.getLifecycle(), () -> {
|
||||
Recipient recipient;
|
||||
if (recipientId.isPresent()) {
|
||||
recipient = Recipient.resolved(recipientId.get());
|
||||
} else {
|
||||
Log.i(TAG, "[onContactSelected] Maybe creating a new recipient.");
|
||||
recipient = Recipient.external(this, number);
|
||||
}
|
||||
|
||||
long existingThread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient);
|
||||
return new Pair<>(existingThread, recipient);
|
||||
}, result -> {
|
||||
createConversation(result.first(), result.second().getId(), ThreadDatabase.DistributionTypes.DEFAULT);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContactDeselected(String number) {
|
||||
public void onContactDeselected(@NonNull Optional<RecipientId> recipientId, String number) {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.thoughtcrime.securesms;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
@@ -10,40 +9,40 @@ import androidx.core.app.TaskStackBuilder;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
|
||||
public class ShortcutLauncherActivity extends AppCompatActivity {
|
||||
|
||||
private static final String KEY_SERIALIZED_ADDRESS = "serialized_address";
|
||||
private static final String KEY_RECIPIENT = "recipient_id";
|
||||
|
||||
public static Intent createIntent(@NonNull Context context, @NonNull Address address) {
|
||||
public static Intent createIntent(@NonNull Context context, @NonNull RecipientId recipientId) {
|
||||
Intent intent = new Intent(context, ShortcutLauncherActivity.class);
|
||||
intent.setAction(Intent.ACTION_MAIN);
|
||||
intent.putExtra(KEY_SERIALIZED_ADDRESS, address.serialize());
|
||||
intent.putExtra(KEY_RECIPIENT, recipientId.serialize());
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
String serializedAddress = getIntent().getStringExtra(KEY_SERIALIZED_ADDRESS);
|
||||
String rawId = getIntent().getStringExtra(KEY_RECIPIENT);
|
||||
|
||||
if (serializedAddress == null) {
|
||||
if (rawId == null) {
|
||||
Toast.makeText(this, R.string.ShortcutLauncherActivity_invalid_shortcut, Toast.LENGTH_SHORT).show();
|
||||
startActivity(new Intent(this, ConversationListActivity.class));
|
||||
// TODO [greyson] Navigation
|
||||
startActivity(new Intent(this, MainActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
Address address = Address.fromSerialized(serializedAddress);
|
||||
Recipient recipient = Recipient.from(this, address, true);
|
||||
Recipient recipient = Recipient.live(RecipientId.from(rawId)).get();
|
||||
// TODO [greyson] Navigation
|
||||
TaskStackBuilder backStack = TaskStackBuilder.create(this)
|
||||
.addNextIntent(new Intent(this, ConversationListActivity.class));
|
||||
.addNextIntent(new Intent(this, MainActivity.class));
|
||||
|
||||
CommunicationActions.startConversation(this, recipient, null, backStack);
|
||||
finish();
|
||||
@@ -12,7 +12,6 @@ import org.thoughtcrime.securesms.conversation.ConversationActivity;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.Rfc5724Uri;
|
||||
@@ -48,13 +47,13 @@ public class SmsSendtoActivity extends Activity {
|
||||
nextIntent.putExtra(ConversationActivity.TEXT_EXTRA, destination.getBody());
|
||||
Toast.makeText(this, R.string.ConversationActivity_specify_recipient, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
Recipient recipient = Recipient.from(this, Address.fromExternal(this, destination.getDestination()), true);
|
||||
Recipient recipient = Recipient.external(this, destination.getDestination());
|
||||
long threadId = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipient);
|
||||
|
||||
nextIntent = new Intent(this, ConversationActivity.class);
|
||||
nextIntent.putExtra(ConversationActivity.TEXT_EXTRA, destination.getBody());
|
||||
nextIntent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
||||
nextIntent.putExtra(ConversationActivity.ADDRESS_EXTRA, recipient.getAddress());
|
||||
nextIntent.putExtra(ConversationActivity.RECIPIENT_EXTRA, recipient.getId());
|
||||
}
|
||||
return nextIntent;
|
||||
}
|
||||