mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 03:09:04 +01:00
Compare commits
135 Commits
release-1.
...
41-alias-f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68738a5a03 | ||
|
|
1a025d0f40 | ||
|
|
a3593ea8ca | ||
|
|
34eaedbd63 | ||
|
|
44e1ce32ce | ||
|
|
7af4b3d3ca | ||
|
|
1423ca7a15 | ||
|
|
baf64e1129 | ||
|
|
88adfabe09 | ||
|
|
969150bff0 | ||
|
|
8fc622f85d | ||
|
|
22eed91019 | ||
|
|
fcb88ed58c | ||
|
|
0d940fc7d7 | ||
|
|
53da13794f | ||
|
|
2ab03f48cc | ||
|
|
436f45554d | ||
|
|
51209b5eec | ||
|
|
822597b4c6 | ||
|
|
7c01bc59c0 | ||
|
|
825d342f9b | ||
|
|
34955fecbb | ||
|
|
5c28b60a6b | ||
|
|
389b2b5b8e | ||
|
|
78abfa3698 | ||
|
|
9c4fb4fd34 | ||
|
|
3d6a336f6d | ||
|
|
e47d41596a | ||
|
|
8cf54bcedb | ||
|
|
89d5145665 | ||
|
|
0706498b03 | ||
|
|
b296500e7a | ||
|
|
60a8b03344 | ||
|
|
ae16a93522 | ||
|
|
c9a2ff71ae | ||
|
|
16f4c60a56 | ||
|
|
76121eb871 | ||
|
|
47c91a96ae | ||
|
|
14befb957b | ||
|
|
4b7a81177c | ||
|
|
b464fe1653 | ||
|
|
09c6f09805 | ||
|
|
a93093182d | ||
|
|
9515e93857 | ||
|
|
efe15df940 | ||
|
|
de611857cf | ||
|
|
8935ec2c2e | ||
|
|
bd00fb1c04 | ||
|
|
3192015cfd | ||
|
|
e776ee02b0 | ||
|
|
c0553ec11f | ||
|
|
75a871a2f8 | ||
|
|
d6d3d5acef | ||
|
|
a361a2613c | ||
|
|
b68dbd6a75 | ||
|
|
f1e89a3ff4 | ||
|
|
056c23167d | ||
|
|
79d5612645 | ||
|
|
a030f92275 | ||
|
|
b3615b4a77 | ||
|
|
8a15fb242a | ||
|
|
e3686186ee | ||
|
|
18ae388137 | ||
|
|
775031e893 | ||
|
|
9f91b91a4f | ||
|
|
280f3ba1fc | ||
|
|
66619fd3a4 | ||
|
|
c7eb0cbb6d | ||
|
|
1617a95bb9 | ||
|
|
6f54718756 | ||
|
|
ea749f2128 | ||
|
|
b4b0d3daa6 | ||
|
|
609c90f57e | ||
|
|
5cf68fa134 | ||
|
|
61c9c6b8eb | ||
|
|
e97608da40 | ||
|
|
0bb80b1a15 | ||
|
|
bda52ea548 | ||
|
|
cf033dc29d | ||
|
|
c12cedc371 | ||
|
|
4b5e9bd64f | ||
|
|
8d55911dab | ||
|
|
e381f83512 | ||
|
|
e4c7f13832 | ||
|
|
b089a204d3 | ||
|
|
85fcb34997 | ||
|
|
d6d132a9cf | ||
|
|
98d1ea7730 | ||
|
|
159fd34c0c | ||
|
|
9e7a387ea4 | ||
|
|
138e520e6c | ||
|
|
5783c1dfd8 | ||
|
|
348968018a | ||
|
|
33c509cd1f | ||
|
|
bea77151bd | ||
|
|
787e62345f | ||
|
|
48f6a3b91f | ||
|
|
a798e25bf2 | ||
|
|
31e4045cf7 | ||
|
|
5334a8c9ca | ||
|
|
d11f1d2805 | ||
|
|
0d1ebddcd2 | ||
|
|
6c296c1348 | ||
|
|
87701e5f07 | ||
|
|
3aae01d152 | ||
|
|
bc298ba68a | ||
|
|
2623eaa149 | ||
|
|
7359b6942a | ||
|
|
3bcc532b4b | ||
|
|
4d08c69779 | ||
|
|
a6cd8937f7 | ||
|
|
e8566906ef | ||
|
|
929102ed60 | ||
|
|
3b871f5932 | ||
|
|
b972d1fc13 | ||
|
|
ccbeee60a7 | ||
|
|
074b10e177 | ||
|
|
031516ccce | ||
|
|
7d2f1abb94 | ||
|
|
00b9c76bb8 | ||
|
|
4d9fab85cb | ||
|
|
bd2514a299 | ||
|
|
e795efc7fc | ||
|
|
6691d2164f | ||
|
|
a384450c36 | ||
|
|
b375e9873c | ||
|
|
cb30c3885a | ||
|
|
6ee81eb24c | ||
|
|
c14ebe82ce | ||
|
|
00e9f894b1 | ||
|
|
499c586a59 | ||
|
|
64f9ce7306 | ||
|
|
071d961ed1 | ||
|
|
cb9efc5fec | ||
|
|
f9e292f734 |
9
.idea/codeStyles/Project.xml
generated
9
.idea/codeStyles/Project.xml
generated
@@ -36,6 +36,9 @@
|
|||||||
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
|
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
|
||||||
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
|
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
|
||||||
</JavaCodeStyleSettings>
|
</JavaCodeStyleSettings>
|
||||||
|
<JetCodeStyleSettings>
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</JetCodeStyleSettings>
|
||||||
<Objective-C-extensions>
|
<Objective-C-extensions>
|
||||||
<file>
|
<file>
|
||||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
||||||
@@ -257,5 +260,11 @@
|
|||||||
</rules>
|
</rules>
|
||||||
</arrangement>
|
</arrangement>
|
||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="kotlin">
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
<option name="PARAMETER_ANNOTATION_WRAP" value="1" />
|
||||||
|
<option name="VARIABLE_ANNOTATION_WRAP" value="1" />
|
||||||
|
<option name="ENUM_CONSTANTS_WRAP" value="1" />
|
||||||
|
</codeStyleSettings>
|
||||||
</code_scheme>
|
</code_scheme>
|
||||||
</component>
|
</component>
|
||||||
20
.idea/runConfigurations/All_in_briar_headless.xml
generated
Normal file
20
.idea/runConfigurations/All_in_briar_headless.xml
generated
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="All in briar-headless" type="AndroidJUnit" factoryName="Android JUnit" nameIsGenerated="true">
|
||||||
|
<module name="briar-headless" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
|
<option name="PACKAGE_NAME" value="" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="" />
|
||||||
|
<option name="METHOD_NAME" value="" />
|
||||||
|
<option name="TEST_OBJECT" value="package" />
|
||||||
|
<option name="VM_PARAMETERS" value="" />
|
||||||
|
<option name="PARAMETERS" value="" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-headless" />
|
||||||
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
|
<value defaultName="singleModule" />
|
||||||
|
</option>
|
||||||
|
<patterns />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
3
.idea/runConfigurations/All_tests.xml
generated
3
.idea/runConfigurations/All_tests.xml
generated
@@ -24,6 +24,7 @@
|
|||||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-android" run_configuration_type="AndroidJUnit" />
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-android" run_configuration_type="AndroidJUnit" />
|
||||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-java" run_configuration_type="AndroidJUnit" />
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-java" run_configuration_type="AndroidJUnit" />
|
||||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-core" run_configuration_type="AndroidJUnit" />
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-core" run_configuration_type="AndroidJUnit" />
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All in briar-headless" run_configuration_type="AndroidJUnit" />
|
||||||
</method>
|
</method>
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All tests in bramble-android" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="All tests in bramble-android" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
|
||||||
<module name="bramble-android" />
|
<module name="bramble-android" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
@@ -11,12 +10,10 @@
|
|||||||
<option name="VM_PARAMETERS" value="-ea" />
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-android" />
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-android" />
|
||||||
<option name="ENV_VARIABLES" />
|
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
<value defaultName="singleModule" />
|
<value defaultName="singleModule" />
|
||||||
</option>
|
</option>
|
||||||
<envs />
|
|
||||||
<patterns />
|
<patterns />
|
||||||
<method />
|
<method />
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All tests in bramble-api" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="All tests in bramble-api" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
|
||||||
<module name="bramble-api" />
|
<module name="bramble-api" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
@@ -11,12 +10,10 @@
|
|||||||
<option name="VM_PARAMETERS" value="-ea" />
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-api" />
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-api" />
|
||||||
<option name="ENV_VARIABLES" />
|
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
<value defaultName="singleModule" />
|
<value defaultName="singleModule" />
|
||||||
</option>
|
</option>
|
||||||
<envs />
|
|
||||||
<patterns />
|
<patterns />
|
||||||
<method />
|
<method />
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All tests in bramble-core" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="All tests in bramble-core" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
|
||||||
<module name="bramble-core" />
|
<module name="bramble-core" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
@@ -11,12 +10,10 @@
|
|||||||
<option name="VM_PARAMETERS" value="-ea" />
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-core" />
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-core" />
|
||||||
<option name="ENV_VARIABLES" />
|
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
<value defaultName="singleModule" />
|
<value defaultName="singleModule" />
|
||||||
</option>
|
</option>
|
||||||
<envs />
|
|
||||||
<patterns />
|
<patterns />
|
||||||
<method />
|
<method />
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All tests in bramble-java" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="All tests in bramble-java" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
|
||||||
<module name="bramble-java" />
|
<module name="bramble-java" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
@@ -11,12 +10,10 @@
|
|||||||
<option name="VM_PARAMETERS" value="-ea -Djava.library.path=libs" />
|
<option name="VM_PARAMETERS" value="-ea -Djava.library.path=libs" />
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-java" />
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-java" />
|
||||||
<option name="ENV_VARIABLES" />
|
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
<value defaultName="singleModule" />
|
<value defaultName="singleModule" />
|
||||||
</option>
|
</option>
|
||||||
<envs />
|
|
||||||
<patterns />
|
<patterns />
|
||||||
<method />
|
<method />
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All tests in briar-android" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="All tests in briar-android" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
|
||||||
<module name="briar-android" />
|
<module name="briar-android" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
@@ -11,12 +10,10 @@
|
|||||||
<option name="VM_PARAMETERS" value="-ea" />
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-android" />
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-android" />
|
||||||
<option name="ENV_VARIABLES" />
|
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
<value defaultName="singleModule" />
|
<value defaultName="singleModule" />
|
||||||
</option>
|
</option>
|
||||||
<envs />
|
|
||||||
<patterns />
|
<patterns />
|
||||||
<method />
|
<method />
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="All tests in briar-core" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="All tests in briar-core" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
|
||||||
<module name="briar-core" />
|
<module name="briar-core" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
@@ -11,12 +10,10 @@
|
|||||||
<option name="VM_PARAMETERS" value="-ea" />
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-core" />
|
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-core" />
|
||||||
<option name="ENV_VARIABLES" />
|
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
<value defaultName="singleModule" />
|
<value defaultName="singleModule" />
|
||||||
</option>
|
</option>
|
||||||
<envs />
|
|
||||||
<patterns />
|
<patterns />
|
||||||
<method />
|
<method />
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
3
.idea/runConfigurations/H2_Performance_Test.xml
generated
3
.idea/runConfigurations/H2_Performance_Test.xml
generated
@@ -1,6 +1,5 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="H2 Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="H2 Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
|
||||||
<module name="bramble-core" />
|
<module name="bramble-core" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
@@ -11,12 +10,10 @@
|
|||||||
<option name="VM_PARAMETERS" value="-ea" />
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="" />
|
<option name="WORKING_DIRECTORY" value="" />
|
||||||
<option name="ENV_VARIABLES" />
|
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
<value defaultName="singleModule" />
|
<value defaultName="singleModule" />
|
||||||
</option>
|
</option>
|
||||||
<envs />
|
|
||||||
<patterns />
|
<patterns />
|
||||||
<method />
|
<method />
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="HyperSQL Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
|
<configuration default="false" name="HyperSQL Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
|
||||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
|
||||||
<module name="bramble-core" />
|
<module name="bramble-core" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
<option name="ALTERNATIVE_JRE_PATH" />
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
@@ -11,12 +10,10 @@
|
|||||||
<option name="VM_PARAMETERS" value="-ea" />
|
<option name="VM_PARAMETERS" value="-ea" />
|
||||||
<option name="PARAMETERS" value="" />
|
<option name="PARAMETERS" value="" />
|
||||||
<option name="WORKING_DIRECTORY" value="" />
|
<option name="WORKING_DIRECTORY" value="" />
|
||||||
<option name="ENV_VARIABLES" />
|
|
||||||
<option name="PASS_PARENT_ENVS" value="true" />
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
<option name="TEST_SEARCH_SCOPE">
|
<option name="TEST_SEARCH_SCOPE">
|
||||||
<value defaultName="singleModule" />
|
<value defaultName="singleModule" />
|
||||||
</option>
|
</option>
|
||||||
<envs />
|
|
||||||
<patterns />
|
<patterns />
|
||||||
<method />
|
<method />
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
17
.idea/runConfigurations/briar_headless.xml
generated
Normal file
17
.idea/runConfigurations/briar_headless.xml
generated
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="briar-headless" type="JetRunConfigurationType" factoryName="Kotlin" singleton="true">
|
||||||
|
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||||
|
<option name="VM_PARAMETERS" value="" />
|
||||||
|
<option name="PROGRAM_PARAMETERS" value="-v" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||||
|
<option name="ALTERNATIVE_JRE_PATH" />
|
||||||
|
<option name="PASS_PARENT_ENVS" value="true" />
|
||||||
|
<option name="MAIN_CLASS_NAME" value="org.briarproject.briar.headless.MainKt" />
|
||||||
|
<option name="WORKING_DIRECTORY" value="" />
|
||||||
|
<module name="briar-headless" />
|
||||||
|
<envs />
|
||||||
|
<method>
|
||||||
|
<option name="Gradle.BeforeRunTask" enabled="true" tasks="jar" externalProjectPath="$PROJECT_DIR$/briar-headless" vmOptions="" scriptParameters="" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
||||||
@@ -3,14 +3,14 @@ apply plugin: 'witness'
|
|||||||
apply from: 'witness.gradle'
|
apply from: 'witness.gradle'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 27
|
compileSdkVersion 28
|
||||||
buildToolsVersion '27.0.3'
|
buildToolsVersion '28.0.3'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 26
|
targetSdkVersion 26
|
||||||
versionCode 10102
|
versionCode 10103
|
||||||
versionName "1.1.2"
|
versionName "1.1.3"
|
||||||
consumerProguardFiles 'proguard-rules.txt'
|
consumerProguardFiles 'proguard-rules.txt'
|
||||||
|
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
@@ -28,7 +28,7 @@ configurations {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(path: ':bramble-core', configuration: 'default')
|
implementation project(path: ':bramble-core', configuration: 'default')
|
||||||
tor 'org.briarproject:tor-android:0.2.9.16@zip'
|
tor 'org.briarproject:tor-android:0.3.4.8@zip'
|
||||||
|
|
||||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
|
|
||||||
|
|||||||
@@ -16,18 +16,27 @@ import org.briarproject.bramble.api.plugin.PluginException;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.util.AndroidUtils;
|
import org.briarproject.bramble.util.AndroidUtils;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_FINISHED;
|
||||||
|
import static android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_STARTED;
|
||||||
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
|
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
|
||||||
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
|
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
|
||||||
import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
|
import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
|
||||||
@@ -37,8 +46,13 @@ import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERA
|
|||||||
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
|
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
|
||||||
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
|
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
|
||||||
import static android.bluetooth.BluetoothAdapter.STATE_ON;
|
import static android.bluetooth.BluetoothAdapter.STATE_ON;
|
||||||
|
import static android.bluetooth.BluetoothDevice.ACTION_FOUND;
|
||||||
|
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
|
||||||
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -47,8 +61,11 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(AndroidBluetoothPlugin.class.getName());
|
Logger.getLogger(AndroidBluetoothPlugin.class.getName());
|
||||||
|
|
||||||
|
private static final int MAX_DISCOVERY_MS = 10_000;
|
||||||
|
|
||||||
private final AndroidExecutor androidExecutor;
|
private final AndroidExecutor androidExecutor;
|
||||||
private final Context appContext;
|
private final Context appContext;
|
||||||
|
private final Clock clock;
|
||||||
|
|
||||||
private volatile boolean wasEnabledByUs = false;
|
private volatile boolean wasEnabledByUs = false;
|
||||||
private volatile BluetoothStateReceiver receiver = null;
|
private volatile BluetoothStateReceiver receiver = null;
|
||||||
@@ -58,12 +75,13 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
|||||||
|
|
||||||
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
|
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
|
||||||
Executor ioExecutor, AndroidExecutor androidExecutor,
|
Executor ioExecutor, AndroidExecutor androidExecutor,
|
||||||
Context appContext, SecureRandom secureRandom, Backoff backoff,
|
Context appContext, SecureRandom secureRandom, Clock clock,
|
||||||
DuplexPluginCallback callback, int maxLatency) {
|
Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
|
||||||
super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
|
super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
|
||||||
maxLatency);
|
maxLatency);
|
||||||
this.androidExecutor = androidExecutor;
|
this.androidExecutor = androidExecutor;
|
||||||
this.appContext = appContext;
|
this.appContext = appContext;
|
||||||
|
this.clock = clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -182,6 +200,74 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
DuplexTransportConnection discoverAndConnect(String uuid) {
|
||||||
|
if (adapter == null) return null;
|
||||||
|
for (String address : discoverDevices()) {
|
||||||
|
try {
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Connecting to " + scrubMacAddress(address));
|
||||||
|
return connectTo(address, uuid);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Could not connect to "
|
||||||
|
+ scrubMacAddress(address));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.info("Could not connect to any devices");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<String> discoverDevices() {
|
||||||
|
List<String> addresses = new ArrayList<>();
|
||||||
|
BlockingQueue<Intent> intents = new LinkedBlockingQueue<>();
|
||||||
|
DiscoveryReceiver receiver = new DiscoveryReceiver(intents);
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(ACTION_DISCOVERY_STARTED);
|
||||||
|
filter.addAction(ACTION_DISCOVERY_FINISHED);
|
||||||
|
filter.addAction(ACTION_FOUND);
|
||||||
|
appContext.registerReceiver(receiver, filter);
|
||||||
|
try {
|
||||||
|
if (adapter.startDiscovery()) {
|
||||||
|
long now = clock.currentTimeMillis();
|
||||||
|
long end = now + MAX_DISCOVERY_MS;
|
||||||
|
while (now < end) {
|
||||||
|
Intent i = intents.poll(end - now, MILLISECONDS);
|
||||||
|
if (i == null) break;
|
||||||
|
String action = i.getAction();
|
||||||
|
if (ACTION_DISCOVERY_STARTED.equals(action)) {
|
||||||
|
LOG.info("Discovery started");
|
||||||
|
} else if (ACTION_DISCOVERY_FINISHED.equals(action)) {
|
||||||
|
LOG.info("Discovery finished");
|
||||||
|
break;
|
||||||
|
} else if (ACTION_FOUND.equals(action)) {
|
||||||
|
BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE);
|
||||||
|
String address = d.getAddress();
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Discovered " + scrubMacAddress(address));
|
||||||
|
if (!addresses.contains(address))
|
||||||
|
addresses.add(address);
|
||||||
|
}
|
||||||
|
now = clock.currentTimeMillis();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.info("Could not start discovery");
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.info("Interrupted while discovering devices");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} finally {
|
||||||
|
LOG.info("Cancelling discovery");
|
||||||
|
adapter.cancelDiscovery();
|
||||||
|
appContext.unregisterReceiver(receiver);
|
||||||
|
}
|
||||||
|
// Shuffle the addresses so we don't always try the same one first
|
||||||
|
Collections.shuffle(addresses);
|
||||||
|
return addresses;
|
||||||
|
}
|
||||||
|
|
||||||
private void tryToClose(@Nullable Closeable c) {
|
private void tryToClose(@Nullable Closeable c) {
|
||||||
try {
|
try {
|
||||||
if (c != null) c.close();
|
if (c != null) c.close();
|
||||||
@@ -207,4 +293,18 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class DiscoveryReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
private final BlockingQueue<Intent> intents;
|
||||||
|
|
||||||
|
private DiscoveryReceiver(BlockingQueue<Intent> intents) {
|
||||||
|
this.intents = intents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context ctx, Intent intent) {
|
||||||
|
intents.add(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
|||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -33,17 +34,19 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
|||||||
private final Context appContext;
|
private final Context appContext;
|
||||||
private final SecureRandom secureRandom;
|
private final SecureRandom secureRandom;
|
||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
|
private final Clock clock;
|
||||||
private final BackoffFactory backoffFactory;
|
private final BackoffFactory backoffFactory;
|
||||||
|
|
||||||
public AndroidBluetoothPluginFactory(Executor ioExecutor,
|
public AndroidBluetoothPluginFactory(Executor ioExecutor,
|
||||||
AndroidExecutor androidExecutor, Context appContext,
|
AndroidExecutor androidExecutor, Context appContext,
|
||||||
SecureRandom secureRandom, EventBus eventBus,
|
SecureRandom secureRandom, EventBus eventBus, Clock clock,
|
||||||
BackoffFactory backoffFactory) {
|
BackoffFactory backoffFactory) {
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.androidExecutor = androidExecutor;
|
this.androidExecutor = androidExecutor;
|
||||||
this.appContext = appContext;
|
this.appContext = appContext;
|
||||||
this.secureRandom = secureRandom;
|
this.secureRandom = secureRandom;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
|
this.clock = clock;
|
||||||
this.backoffFactory = backoffFactory;
|
this.backoffFactory = backoffFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +68,7 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
|||||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||||
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
|
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
|
||||||
connectionLimiter, ioExecutor, androidExecutor, appContext,
|
connectionLimiter, ioExecutor, androidExecutor, appContext,
|
||||||
secureRandom, backoff, callback, MAX_LATENCY);
|
secureRandom, clock, backoff, callback, MAX_LATENCY);
|
||||||
eventBus.addListener(plugin);
|
eventBus.addListener(plugin);
|
||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +1,42 @@
|
|||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
verify = [
|
verify = [
|
||||||
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
|
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
|
||||||
'com.android.tools.analytics-library:protos:26.1.3:protos-26.1.3.jar:818c9f256f141d9dafec03a1aa2b94d240b2c140acfd7ee31a8b3e6c2b9479e3',
|
'com.android.tools.analytics-library:protos:26.2.1:protos-26.2.1.jar:2f371f5b1f551e85ab08be4d6a2873471b3d44afd1ebf6aa3298f3b796bf691f',
|
||||||
'com.android.tools.analytics-library:shared:26.1.3:shared-26.1.3.jar:7110706c7ada96c8b6f5ca80c478291bc7899d46277de2c48527e045442401a3',
|
'com.android.tools.analytics-library:shared:26.2.1:shared-26.2.1.jar:4c1e4e705fa4d45f23aaea230557f6508155012d9c296337787c1d7b26a97f5a',
|
||||||
'com.android.tools.analytics-library:tracker:26.1.3:tracker-26.1.3.jar:4155424bf2ce4872da83332579a1707252bc66cbd77c5144fdc4483d0f2e1418',
|
'com.android.tools.analytics-library:tracker:26.2.1:tracker-26.2.1.jar:4a624ecc976539f755ddb0bb8dfc2dd3d08326cfec59a098dbd70f701ca7fb75',
|
||||||
'com.android.tools.build:apksig:3.1.3:apksig-3.1.3.jar:7e1f8e675a6e768e5b56405e41d6c3cc05befe62e601b04177de1029902c9c89',
|
'com.android.tools.build:aapt2:3.2.1-4818971:aapt2-3.2.1-4818971-linux.jar:f431b6f96c91a2c155144b091a9c97d9805c589fe8efc9c930b6cd346cb60a1e',
|
||||||
'com.android.tools.build:builder-model:3.1.3:builder-model-3.1.3.jar:06ad1c422d679fc698451479cb40ba863849d67bfd1de23f6d2c16d78b024b0b',
|
'com.android.tools.build:apksig:3.2.1:apksig-3.2.1.jar:2b46f2feffea66037aab29e4261b2433c190194a6ef97b958511eb157f2ccba5',
|
||||||
'com.android.tools.build:builder-test-api:3.1.3:builder-test-api-3.1.3.jar:4d989f780436794f0f8b2f50e9e079b786571eac90f26c208ab2ae6d4012f389',
|
'com.android.tools.build:apkzlib:3.2.1:apkzlib-3.2.1.jar:c39ad0313905932431fe81c8899c2cf39a4d92ad6c4edcaa4b25432f461452aa',
|
||||||
'com.android.tools.build:builder:3.1.3:builder-3.1.3.jar:8a1092012c89d0ec1ee2eff09c5708c71ef4482a6862df8d3a44a67fccace01c',
|
'com.android.tools.build:builder-model:3.2.1:builder-model-3.2.1.jar:a9f68e6abcec122f9cb5ad352d3f05a3eb03acbcdca95e4d25c16310c2c965ff',
|
||||||
'com.android.tools.build:gradle-api:3.1.3:gradle-api-3.1.3.jar:01e4df521456aef66514336f1d492346730dd1fb8f6433a89f62da834941ed72',
|
'com.android.tools.build:builder-test-api:3.2.1:builder-test-api-3.2.1.jar:533ac6c2b5884bb54967a33791f2628dfdfac7981af39417a333b43d4379b6be',
|
||||||
'com.android.tools.build:manifest-merger:26.1.3:manifest-merger-26.1.3.jar:1e4fc7e932adb4607082409800e5e6fccb42e6c5360ae5990094bf522f3ada55',
|
'com.android.tools.build:builder:3.2.1:builder-3.2.1.jar:aedcbfd115dbe91d09b4113e66ef50589b558d0aa3b2f133b1d867c9b87fae83',
|
||||||
'com.android.tools.ddms:ddmlib:26.1.3:ddmlib-26.1.3.jar:c54931cd68df5d1ea2923b3b320eae47cd2307a5a916bb8674c0acf93cd1d3cd',
|
'com.android.tools.build:gradle-api:3.2.1:gradle-api-3.2.1.jar:57cf0ac5ac1dca8afdb3f62b94265e776e7dcfa641cc3844fb53a05193de208d',
|
||||||
'com.android.tools.external.com-intellij:intellij-core:26.1.3:intellij-core-26.1.3.jar:af67f5535fef2e1a28b1007a4acb8c5deb6a1e33b8afe7b11d012c9e778ebcec',
|
'com.android.tools.build:manifest-merger:26.2.1:manifest-merger-26.2.1.jar:8830573263361035d38cfdcb51e2db94029c93865b21334f5fbf8a27984281a6',
|
||||||
'com.android.tools.external.com-intellij:kotlin-compiler:26.1.3:kotlin-compiler-26.1.3.jar:c746d2859dc11cc05c84b692b3498d3a621e0929511f8440ee009c6557838fd4',
|
'com.android.tools.ddms:ddmlib:26.2.1:ddmlib-26.2.1.jar:a4bf0a29a19980bf27269465cc782064656750b77c26728f82f9e148b705218b',
|
||||||
'com.android.tools.external.org-jetbrains:uast:26.1.3:uast-26.1.3.jar:3f3f6651d0c7685a77ecb22e9c82d6b49fdf24322c17360768dc530678f43265',
|
'com.android.tools.external.com-intellij:intellij-core:26.2.1:intellij-core-26.2.1.jar:4925ad1892c2687cb1a63427d440ef519c8c59215fefe0dc5d541d5d411fcafe',
|
||||||
'com.android.tools.layoutlib:layoutlib-api:26.1.3:layoutlib-api-26.1.3.jar:10bc73ce706c45629872d6a999dbe12116df64e24f47ff93b7b13121ff57b4b0',
|
'com.android.tools.external.com-intellij:kotlin-compiler:26.2.1:kotlin-compiler-26.2.1.jar:daa064fd708f340ee25fb9823c4c74104ac77f1370b76d907eb9ae6daec0a2ae',
|
||||||
'com.android.tools.lint:lint-api:26.1.3:lint-api-26.1.3.jar:6f97323f9af8deda86278717885b5c927f3766757db89709f52d11d42b6fb751',
|
'com.android.tools.external.org-jetbrains:uast:26.2.1:uast-26.2.1.jar:f10f7258d2ab9189562cc0f9ad838c0378fdba439229173390a99de02ebac75b',
|
||||||
'com.android.tools.lint:lint-checks:26.1.3:lint-checks-26.1.3.jar:73c3d53784c9ce3e6d5968506581918e0179645d20809927ca4a001dd766b001',
|
'com.android.tools.layoutlib:layoutlib-api:26.2.1:layoutlib-api-26.2.1.jar:ddbf4fca123733fa011595b1cc1f4ac2937ed327b60990711fafc33c775c2ade',
|
||||||
'com.android.tools.lint:lint-gradle-api:26.1.3:lint-gradle-api-26.1.3.jar:7ca3c4866ec21dc21d53a9d86f752b77ace6f6c610a0c9dc877313856c733d9d',
|
'com.android.tools.lint:lint-api:26.2.1:lint-api-26.2.1.jar:3b57e739de567b98bc9ab56c2c0ee66fc026b4adf5843e8f9804ca0666a6f66e',
|
||||||
'com.android.tools.lint:lint-gradle:26.1.3:lint-gradle-26.1.3.jar:db0c354b8f4b6f6637e31f91c564785a59ff896325331fcbc3de7458e0b6c067',
|
'com.android.tools.lint:lint-checks:26.2.1:lint-checks-26.2.1.jar:c86f4cc9aaee722ee4ad70062f7b5af91e9b041914af27adc09f545ab0fb3bc6',
|
||||||
'com.android.tools.lint:lint-kotlin:26.1.3:lint-kotlin-26.1.3.jar:94e2b0f4565a241561cfb8fc1222bb3f132a3b98d2a90421dbb72ee8358e7d68',
|
'com.android.tools.lint:lint-gradle-api:26.2.1:lint-gradle-api-26.2.1.jar:2283e7af32e301565f2a797e531f0fc8c648077d457afb3ffdddbee638976c2f',
|
||||||
'com.android.tools.lint:lint:26.1.3:lint-26.1.3.jar:8d5f32c989c6d191d712e90ad3ca2d1c409313599551d04d834caa44d26c78df',
|
'com.android.tools.lint:lint-gradle:26.2.1:lint-gradle-26.2.1.jar:8fd90b2f3ec788cbb9801c07ab3e1ea2255aa31a6093157d7ea0ff13d0315ecb',
|
||||||
'com.android.tools:annotations:26.1.3:annotations-26.1.3.jar:c950430b24ac5d58fc97e7283b8f0115f99587e76e08b4e1e2aaa780f2d77323',
|
'com.android.tools.lint:lint-kotlin:26.2.1:lint-kotlin-26.2.1.jar:7a6a5d2b18f69cf1b900d857c2632b4c683713c533295933b8b759f8cab4a877',
|
||||||
'com.android.tools:common:26.1.3:common-26.1.3.jar:7c31a90581a148ab219f615a59667f0dded7fa39b248529784474da3c2274ef2',
|
'com.android.tools.lint:lint:26.2.1:lint-26.2.1.jar:7848b82ae988b90dee259ae7c7e86e05cbf52db6cd21c8bbd38ce7df08f3f8c5',
|
||||||
'com.android.tools:dvlib:26.1.3:dvlib-26.1.3.jar:0cae87906f53d3f1088366a916ed180a7312b6d9919b90797f238875c8492855',
|
'com.android.tools:annotations:26.2.1:annotations-26.2.1.jar:7391c6a1e080174b96e64ceb078dadd31ce4d8a2d2fee0ec65be202126f90f24',
|
||||||
'com.android.tools:repository:26.1.3:repository-26.1.3.jar:52d4539cc68db91b261e2a33b2c8206b26e05539078758dc28cfb3854adb4f59',
|
'com.android.tools:common:26.2.1:common-26.2.1.jar:a50aab2d6411ff68f4004a87c7e93d87d8e980a0ec3b352246549897ea2d78e5',
|
||||||
'com.android.tools:sdk-common:26.1.3:sdk-common-26.1.3.jar:1948603ca9ff22c7ebb3178000bffa3a9dd2ca1cc5cb0c793cae08468b8fcfc1',
|
'com.android.tools:dvlib:26.2.1:dvlib-26.2.1.jar:72a83bf2839b1df9b1fbf67ba45d1bfb9f966cd774da4320c762b2be8f1688aa',
|
||||||
'com.android.tools:sdklib:26.1.3:sdklib-26.1.3.jar:4adcfaad9514607098d2c51503c39811112d3050f4d1e744c01c7f08f591032b',
|
'com.android.tools:repository:26.2.1:repository-26.2.1.jar:fa74dae09103faef703df38550ad8fa244c5b6d1bf90d6198be932292b3d9cc1',
|
||||||
|
'com.android.tools:sdk-common:26.2.1:sdk-common-26.2.1.jar:759d4b292ca69a35cf961fca377b54158fc6c88108978006999442e80a011cf4',
|
||||||
|
'com.android.tools:sdklib:26.2.1:sdklib-26.2.1.jar:248df7ad5eac4aeb6f96c394c76760de4b7b89ac056e54d0c21a739368b91b45',
|
||||||
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
|
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
|
||||||
'com.google.code.gson:gson:2.7:gson-2.7.jar:2d43eb5ea9e133d2ee2405cc14f5ee08951b8361302fdd93494a3a997b508d32',
|
'com.google.code.gson:gson:2.8.0:gson-2.8.0.jar:c6221763bd79c4f1c3dc7f750b5f29a0bb38b367b81314c4f71896e340c40825',
|
||||||
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
|
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
|
||||||
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
|
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
|
||||||
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
||||||
'com.google.errorprone:error_prone_annotations:2.0.18:error_prone_annotations-2.0.18.jar:cb4cfad870bf563a07199f3ebea5763f0dec440fcda0b318640b1feaa788656b',
|
'com.google.errorprone:error_prone_annotations:2.0.18:error_prone_annotations-2.0.18.jar:cb4cfad870bf563a07199f3ebea5763f0dec440fcda0b318640b1feaa788656b',
|
||||||
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
||||||
'com.google.guava:guava:22.0:guava-22.0.jar:1158e94c7de4da480873f0b4ab4a1da14c0d23d4b1902cc94a58a6f0f9ab579e',
|
'com.google.guava:guava:23.0:guava-23.0.jar:7baa80df284117e5b945b19b98d367a85ea7b7801bd358ff657946c3bd1b6596',
|
||||||
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:40ceb7157feb263949e0f503fe5f71689333a621021aa20ce0d0acee3badaa0f',
|
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:40ceb7157feb263949e0f503fe5f71689333a621021aa20ce0d0acee3badaa0f',
|
||||||
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
|
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
|
||||||
'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4',
|
'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4',
|
||||||
@@ -43,8 +45,8 @@ dependencyVerification {
|
|||||||
'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce',
|
'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce',
|
||||||
'com.sun.istack:istack-commons-runtime:2.21:istack-commons-runtime-2.21.jar:c33e67a0807095f02a0e2da139412dd7c4f9cc1a4c054b3e434f96831ba950f4',
|
'com.sun.istack:istack-commons-runtime:2.21:istack-commons-runtime-2.21.jar:c33e67a0807095f02a0e2da139412dd7c4f9cc1a4c054b3e434f96831ba950f4',
|
||||||
'com.sun.xml.fastinfoset:FastInfoset:1.2.13:FastInfoset-1.2.13.jar:27a77db909f3c2833c0b1a37c55af1db06045118ad2eed96ce567b6632bce038',
|
'com.sun.xml.fastinfoset:FastInfoset:1.2.13:FastInfoset-1.2.13.jar:27a77db909f3c2833c0b1a37c55af1db06045118ad2eed96ce567b6632bce038',
|
||||||
'commons-codec:commons-codec:1.6:commons-codec-1.6.jar:54b34e941b8e1414bd3e40d736efd3481772dc26db3296f6aa45cec9f6203d86',
|
'commons-codec:commons-codec:1.9:commons-codec-1.9.jar:ad19d2601c3abf0b946b5c3a4113e226a8c1e3305e395b90013b78dd94a723ce',
|
||||||
'commons-logging:commons-logging:1.1.1:commons-logging-1.1.1.jar:ce6f913cad1f0db3aad70186d65c5bc7ffcc9a99e3fe8e0b137312819f7c362f',
|
'commons-logging:commons-logging:1.2:commons-logging-1.2.jar:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636',
|
||||||
'it.unimi.dsi:fastutil:7.2.0:fastutil-7.2.0.jar:74fa208043740642f7e6eb09faba15965218ad2f50ce3020efb100136e4b591c',
|
'it.unimi.dsi:fastutil:7.2.0:fastutil-7.2.0.jar:74fa208043740642f7e6eb09faba15965218ad2f50ce3020efb100136e4b591c',
|
||||||
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
@@ -55,13 +57,13 @@ dependencyVerification {
|
|||||||
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
||||||
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||||
'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
|
'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
|
||||||
'org.apache.httpcomponents:httpclient:4.2.6:httpclient-4.2.6.jar:362e9324ee7c697e21279e20077b52737ddef3f1b2c1a7abe5ad34b465145550',
|
'org.apache.httpcomponents:httpclient:4.5.2:httpclient-4.5.2.jar:0dffc621400d6c632f55787d996b8aeca36b30746a716e079a985f24d8074057',
|
||||||
'org.apache.httpcomponents:httpcore:4.2.5:httpcore-4.2.5.jar:e5e82da4cc66c8d917bbf743e3c0752efe8522735e7fc9dbddb65bccea81cfe9',
|
'org.apache.httpcomponents:httpcore:4.4.5:httpcore-4.4.5.jar:64d5453874cab7e40a7065cb01a9a9ca1053845a9786b478878b679e0580cec3',
|
||||||
'org.apache.httpcomponents:httpmime:4.1:httpmime-4.1.jar:31629566148e8a47688ae43b420abc3ecd783ed15b33bebc00824bf24c9b15aa',
|
'org.apache.httpcomponents:httpmime:4.5.2:httpmime-4.5.2.jar:231a3f7e4962053db2be8461d5422e68fc458a3a7dd7d8ada803a348e21f8f07',
|
||||||
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
||||||
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
||||||
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
||||||
'org.briarproject:tor-android:0.2.9.16:tor-android-0.2.9.16.zip:515e33dda6a30853c885a2de2c79ae1ab9ad8b6db44f5db8890333ec2e24f4ae',
|
'org.briarproject:tor-android:0.3.4.8:tor-android-0.3.4.8.zip:989a0352d9d8d8172cd6c2137654e165e5d2beb10ed1211bab3814e224ad1926',
|
||||||
'org.codehaus.groovy:groovy-all:2.4.12:groovy-all-2.4.12.jar:6a56af4bd48903d56bec62821876cadefafd007360cc6bd0d8f7aa8d72b38be4',
|
'org.codehaus.groovy:groovy-all:2.4.12:groovy-all-2.4.12.jar:6a56af4bd48903d56bec62821876cadefafd007360cc6bd0d8f7aa8d72b38be4',
|
||||||
'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
|
'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
|
||||||
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
|
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
|
||||||
@@ -70,9 +72,10 @@ dependencyVerification {
|
|||||||
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||||
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
||||||
'org.jetbrains.kotlin:kotlin-reflect:1.2.0:kotlin-reflect-1.2.0.jar:4f48a872bad6e4d9c053f4ad610d11e4012ad7e58dc19a03dd5eb811f36069dd',
|
'org.jetbrains.kotlin:kotlin-reflect:1.2.0:kotlin-reflect-1.2.0.jar:4f48a872bad6e4d9c053f4ad610d11e4012ad7e58dc19a03dd5eb811f36069dd',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0:kotlin-stdlib-jre7-1.2.0.jar:c7a20fb951d437797afe8980aff6c1e5a03f310c661ba58ba1d4fa90cb0f2926',
|
'org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71:kotlin-stdlib-common-1.2.71.jar:63999687ff2fce8a592dd180ffbbf8f1d21c26b4044c55cdc74ff3cf3b3cf328',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jre8:1.2.0:kotlin-stdlib-jre8-1.2.0.jar:633524eee6ef1941f7cb1dab7ee3927b0a221ceee9047aeb5515f4cbb990c82a',
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71:kotlin-stdlib-jdk7-1.2.71.jar:b136bd61b240e07d4d92ce00d3bd1dbf584400a7bf5f220c2f3cd22446858082',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib:1.2.0:kotlin-stdlib-1.2.0.jar:05cfd9f5ac0b41910703a8925f7211a495909b27a2ffdd1c5106f1689aeafcd4',
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71:kotlin-stdlib-jdk8-1.2.71.jar:ac3c8abf47790b64b4f7e2509a53f0c145e061ac1612a597520535d199946ea9',
|
||||||
|
'org.jetbrains.kotlin:kotlin-stdlib:1.2.71:kotlin-stdlib-1.2.71.jar:4c895c270b87f5fec2a2796e1d89c15407ee821de961527c28588bb46afbc68b',
|
||||||
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
|
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
|
||||||
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||||
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
|
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
|
||||||
@@ -81,11 +84,11 @@ dependencyVerification {
|
|||||||
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
||||||
'org.jvnet.staxex:stax-ex:1.7.7:stax-ex-1.7.7.jar:a31ff7d77163c0deb09e7fee59ad35ae44c2cee2cc8552a116ccd1583d813fb4',
|
'org.jvnet.staxex:stax-ex:1.7.7:stax-ex-1.7.7.jar:a31ff7d77163c0deb09e7fee59ad35ae44c2cee2cc8552a116ccd1583d813fb4',
|
||||||
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
||||||
'org.ow2.asm:asm-analysis:5.1:asm-analysis-5.1.jar:a34658f5c5de4b573eef21131cc32cc25f7b66407944f312b28ec2e56abb1fa9',
|
'org.ow2.asm:asm-analysis:6.0:asm-analysis-6.0.jar:2f1a6387219c3a6cc4856481f221b03bd9f2408a326d416af09af5d6f608c1f4',
|
||||||
'org.ow2.asm:asm-commons:5.1:asm-commons-5.1.jar:97b3786e1f55e74bddf8ad102bf50e33bbcbc1f6b7fd7b36f0bbbb25cd4981be',
|
'org.ow2.asm:asm-commons:6.0:asm-commons-6.0.jar:f1bce5c648a96a017bdcd01fe5d59af9845297fd7b79b81c015a6fbbd9719abf',
|
||||||
'org.ow2.asm:asm-tree:5.1:asm-tree-5.1.jar:c0de2bbc4cb8297419659813ecd4ed1d077ed1dd5c1f5544cc5143e493e84c10',
|
'org.ow2.asm:asm-tree:6.0:asm-tree-6.0.jar:887998fb69727c8759e4d253f856822801e33f9fd4caa566b3ac58ee92106215',
|
||||||
'org.ow2.asm:asm-util:5.1:asm-util-5.1.jar:ee032c39ae5e3cd099148fbba9a2124f9ed613e5cb93e03ee0fa8808ce364040',
|
'org.ow2.asm:asm-util:6.0:asm-util-6.0.jar:356afebdb0f870175262e5188f8709a3b17aa2a5a6a4b0340b04d4b449bca5f6',
|
||||||
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
||||||
'org.ow2.asm:asm:5.1:asm-5.1.jar:d2da399a9967c69f0a21739256fa79d284222c223082cacadc17372244764b54',
|
'org.ow2.asm:asm:6.0:asm-6.0.jar:dd8971c74a4e697899a8e95caae4ea8760ea6c486dc6b97b1795e75760420461',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,4 +38,18 @@ public abstract class StringMap extends Hashtable<String, String> {
|
|||||||
public void putInt(String key, int value) {
|
public void putInt(String key, int value) {
|
||||||
put(key, String.valueOf(value));
|
put(key, String.valueOf(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getLong(String key, long defaultValue) {
|
||||||
|
String s = get(key);
|
||||||
|
if (s == null) return defaultValue;
|
||||||
|
try {
|
||||||
|
return Long.valueOf(s);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putLong(String key, long value) {
|
||||||
|
put(key, String.valueOf(value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,12 +48,8 @@ public abstract class BdfMessageValidator implements MessageValidator {
|
|||||||
throw new InvalidMessageException(
|
throw new InvalidMessageException(
|
||||||
"Timestamp is too far in the future");
|
"Timestamp is too far in the future");
|
||||||
}
|
}
|
||||||
byte[] body = m.getBody();
|
|
||||||
if (body.length == 0) {
|
|
||||||
throw new InvalidMessageException("Message is too short");
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
BdfList bodyList = clientHelper.toList(body);
|
BdfList bodyList = clientHelper.toList(m.getBody());
|
||||||
BdfMessageContext result = validateMessage(m, g, bodyList);
|
BdfMessageContext result = validateMessage(m, g, bodyList);
|
||||||
Metadata meta = metadataEncoder.encode(result.getDictionary());
|
Metadata meta = metadataEncoder.encode(result.getDictionary());
|
||||||
return new MessageContext(meta, result.getDependencies());
|
return new MessageContext(meta, result.getDependencies());
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ import org.briarproject.bramble.api.identity.Author;
|
|||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class Contact {
|
public class Contact {
|
||||||
@@ -13,13 +17,21 @@ public class Contact {
|
|||||||
private final ContactId id;
|
private final ContactId id;
|
||||||
private final Author author;
|
private final Author author;
|
||||||
private final AuthorId localAuthorId;
|
private final AuthorId localAuthorId;
|
||||||
|
@Nullable
|
||||||
|
private final String alias;
|
||||||
private final boolean verified, active;
|
private final boolean verified, active;
|
||||||
|
|
||||||
public Contact(ContactId id, Author author, AuthorId localAuthorId,
|
public Contact(ContactId id, Author author, AuthorId localAuthorId,
|
||||||
boolean verified, boolean active) {
|
@Nullable String alias, boolean verified, boolean active) {
|
||||||
|
if (alias != null) {
|
||||||
|
int aliasLength = toUtf8(alias).length;
|
||||||
|
if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.author = author;
|
this.author = author;
|
||||||
this.localAuthorId = localAuthorId;
|
this.localAuthorId = localAuthorId;
|
||||||
|
this.alias = alias;
|
||||||
this.verified = verified;
|
this.verified = verified;
|
||||||
this.active = active;
|
this.active = active;
|
||||||
}
|
}
|
||||||
@@ -36,6 +48,11 @@ public class Contact {
|
|||||||
return localAuthorId;
|
return localAuthorId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getAlias() {
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isVerified() {
|
public boolean isVerified() {
|
||||||
return verified;
|
return verified;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,14 @@ import org.briarproject.bramble.api.db.DbException;
|
|||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
|
import org.briarproject.bramble.api.identity.AuthorInfo;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface ContactManager {
|
public interface ContactManager {
|
||||||
|
|
||||||
@@ -93,6 +96,12 @@ public interface ContactManager {
|
|||||||
void setContactActive(Transaction txn, ContactId c, boolean active)
|
void setContactActive(Transaction txn, ContactId c, boolean active)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an alias name for the contact or unsets it if alias is null.
|
||||||
|
*/
|
||||||
|
void setContactAlias(ContactId c, @Nullable String alias)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if a contact with this name and public key already exists
|
* Return true if a contact with this name and public key already exists
|
||||||
*/
|
*/
|
||||||
@@ -105,6 +114,16 @@ public interface ContactManager {
|
|||||||
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
|
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link AuthorInfo} for the given author.
|
||||||
|
*/
|
||||||
|
AuthorInfo getAuthorInfo(AuthorId a) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link AuthorInfo} for the given author.
|
||||||
|
*/
|
||||||
|
AuthorInfo getAuthorInfo(Transaction txn, AuthorId a) throws DbException;
|
||||||
|
|
||||||
interface ContactHook {
|
interface ContactHook {
|
||||||
|
|
||||||
void addingContact(Transaction txn, Contact c) throws DbException;
|
void addingContact(Transaction txn, Contact c) throws DbException;
|
||||||
|
|||||||
@@ -76,6 +76,19 @@ public interface DatabaseComponent {
|
|||||||
*/
|
*/
|
||||||
void endTransaction(Transaction txn);
|
void endTransaction(Transaction txn);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the given task within a transaction.
|
||||||
|
*/
|
||||||
|
<E extends Exception> void transaction(boolean readOnly,
|
||||||
|
DbRunnable<E> task) throws DbException, E;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the given task within a transaction and returns the result of the
|
||||||
|
* task.
|
||||||
|
*/
|
||||||
|
<R, E extends Exception> R transactionWithResult(boolean readOnly,
|
||||||
|
DbCallable<R, E> task) throws DbException, E;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a contact associated with the given local and remote pseudonyms,
|
* Stores a contact associated with the given local and remote pseudonyms,
|
||||||
* and returns an ID for the contact.
|
* and returns an ID for the contact.
|
||||||
@@ -502,6 +515,12 @@ public interface DatabaseComponent {
|
|||||||
void setContactActive(Transaction txn, ContactId c, boolean active)
|
void setContactActive(Transaction txn, ContactId c, boolean active)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an alias name for the contact or unsets it if alias is null.
|
||||||
|
*/
|
||||||
|
void setContactAlias(Transaction txn, ContactId c, @Nullable String alias)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the given group's visibility to the given contact.
|
* Sets the given group's visibility to the given contact.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package org.briarproject.bramble.api.db;
|
||||||
|
|
||||||
|
public interface DbCallable<R, E extends Exception> {
|
||||||
|
|
||||||
|
R call(Transaction txn) throws DbException, E;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package org.briarproject.bramble.api.db;
|
||||||
|
|
||||||
|
public interface DbRunnable<E extends Exception> {
|
||||||
|
|
||||||
|
void run(Transaction txn) throws DbException, E;
|
||||||
|
}
|
||||||
@@ -6,6 +6,10 @@ public interface MigrationListener {
|
|||||||
* This is called when a migration is started while opening the database.
|
* This is called when a migration is started while opening the database.
|
||||||
* It will be called once for each migration being applied.
|
* It will be called once for each migration being applied.
|
||||||
*/
|
*/
|
||||||
void onMigrationRun();
|
void onDatabaseMigration();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is called when compaction is started while opening the database.
|
||||||
|
*/
|
||||||
|
void onDatabaseCompaction();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_K
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class Author implements Nameable {
|
public class Author implements Nameable {
|
||||||
|
|
||||||
public enum Status {
|
|
||||||
NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current version of the author structure.
|
* The current version of the author structure.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package org.briarproject.bramble.api.identity;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
public class AuthorInfo {
|
||||||
|
|
||||||
|
public enum Status {
|
||||||
|
NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES;
|
||||||
|
|
||||||
|
public boolean isContact() {
|
||||||
|
return this == UNVERIFIED || this == VERIFIED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Status status;
|
||||||
|
@Nullable
|
||||||
|
private final String alias;
|
||||||
|
|
||||||
|
public AuthorInfo(Status status, @Nullable String alias) {
|
||||||
|
this.status = status;
|
||||||
|
this.alias = alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthorInfo(Status status) {
|
||||||
|
this(status, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Status getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getAlias() {
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.api.identity;
|
|||||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author.Status;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -37,14 +36,4 @@ public interface IdentityManager {
|
|||||||
*/
|
*/
|
||||||
LocalAuthor getLocalAuthor(Transaction txn) throws DbException;
|
LocalAuthor getLocalAuthor(Transaction txn) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the {@link Status} of the given author.
|
|
||||||
*/
|
|
||||||
Status getAuthorStatus(AuthorId a) throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the {@link Status} of the given author.
|
|
||||||
*/
|
|
||||||
Status getAuthorStatus(Transaction txn, AuthorId a) throws DbException;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public interface KeyAgreementConstants {
|
|||||||
/**
|
/**
|
||||||
* The connection timeout in milliseconds.
|
* The connection timeout in milliseconds.
|
||||||
*/
|
*/
|
||||||
long CONNECTION_TIMEOUT = 20 * 1000;
|
long CONNECTION_TIMEOUT = 60_000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The transport identifier for Bluetooth.
|
* The transport identifier for Bluetooth.
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ public interface LifecycleManager {
|
|||||||
*/
|
*/
|
||||||
enum LifecycleState {
|
enum LifecycleState {
|
||||||
|
|
||||||
STARTING, MIGRATING_DATABASE, STARTING_SERVICES, RUNNING, STOPPING;
|
STARTING, MIGRATING_DATABASE, COMPACTING_DATABASE, STARTING_SERVICES,
|
||||||
|
RUNNING, STOPPING;
|
||||||
|
|
||||||
public boolean isAfter(LifecycleState state) {
|
public boolean isAfter(LifecycleState state) {
|
||||||
return ordinal() > state.ordinal();
|
return ordinal() > state.ordinal();
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ public interface TorConstants {
|
|||||||
|
|
||||||
TransportId ID = new TransportId("org.briarproject.bramble.tor");
|
TransportId ID = new TransportId("org.briarproject.bramble.tor");
|
||||||
|
|
||||||
String PROP_ONION = "onion";
|
String PROP_ONION_V2 = "onion";
|
||||||
|
String PROP_ONION_V3 = "onion3";
|
||||||
|
|
||||||
int SOCKS_PORT = 59050;
|
int SOCKS_PORT = 59050;
|
||||||
int CONTROL_PORT = 59051;
|
int CONTROL_PORT = 59051;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ public class Message {
|
|||||||
private final byte[] body;
|
private final byte[] body;
|
||||||
|
|
||||||
public Message(MessageId id, GroupId groupId, long timestamp, byte[] body) {
|
public Message(MessageId id, GroupId groupId, long timestamp, byte[] body) {
|
||||||
|
if (body.length == 0) throw new IllegalArgumentException();
|
||||||
if (body.length > MAX_MESSAGE_BODY_LENGTH)
|
if (body.length > MAX_MESSAGE_BODY_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
|
public class SettableClock implements Clock {
|
||||||
|
|
||||||
|
private final AtomicLong time;
|
||||||
|
|
||||||
|
public SettableClock(AtomicLong time) {
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long currentTimeMillis() {
|
||||||
|
return time.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sleep(long milliseconds) throws InterruptedException {
|
||||||
|
Thread.sleep(milliseconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ dependencies {
|
|||||||
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
||||||
implementation 'org.bitlet:weupnp:0.1.4'
|
implementation 'org.bitlet:weupnp:0.1.4'
|
||||||
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
||||||
implementation 'org.whispersystems:curve25519-java:0.4.1'
|
implementation 'org.whispersystems:curve25519-java:0.5.0'
|
||||||
implementation 'org.briarproject:jtorctl:0.3'
|
implementation 'org.briarproject:jtorctl:0.3'
|
||||||
|
|
||||||
apt 'com.google.dagger:dagger-compiler:2.0.2'
|
apt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import org.briarproject.bramble.api.db.NoSuchContactException;
|
|||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
|
import org.briarproject.bramble.api.identity.AuthorInfo;
|
||||||
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
import org.briarproject.bramble.api.transport.KeyManager;
|
||||||
|
|
||||||
@@ -18,21 +21,32 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
|
||||||
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
|
||||||
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
|
||||||
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class ContactManagerImpl implements ContactManager {
|
class ContactManagerImpl implements ContactManager {
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final KeyManager keyManager;
|
private final KeyManager keyManager;
|
||||||
|
private final IdentityManager identityManager;
|
||||||
private final List<ContactHook> hooks;
|
private final List<ContactHook> hooks;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager) {
|
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager,
|
||||||
|
IdentityManager identityManager) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.keyManager = keyManager;
|
this.keyManager = keyManager;
|
||||||
|
this.identityManager = identityManager;
|
||||||
hooks = new CopyOnWriteArrayList<>();
|
hooks = new CopyOnWriteArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +162,17 @@ class ContactManagerImpl implements ContactManager {
|
|||||||
db.setContactActive(txn, c, active);
|
db.setContactActive(txn, c, active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContactAlias(ContactId c, @Nullable String alias)
|
||||||
|
throws DbException {
|
||||||
|
if (alias != null) {
|
||||||
|
int aliasLength = toUtf8(alias).length;
|
||||||
|
if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
db.transaction(false, txn -> db.setContactAlias(txn, c, alias));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
|
public boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
|
||||||
AuthorId localAuthorId) throws DbException {
|
AuthorId localAuthorId) throws DbException {
|
||||||
@@ -176,4 +201,23 @@ class ContactManagerImpl implements ContactManager {
|
|||||||
db.removeContact(txn, c);
|
db.removeContact(txn, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorInfo getAuthorInfo(AuthorId a) throws DbException {
|
||||||
|
return db.transactionWithResult(true, txn -> getAuthorInfo(txn, a));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthorInfo getAuthorInfo(Transaction txn, AuthorId authorId)
|
||||||
|
throws DbException {
|
||||||
|
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
|
||||||
|
if (localAuthor.getId().equals(authorId))
|
||||||
|
return new AuthorInfo(OURSELVES);
|
||||||
|
Collection<Contact> contacts = db.getContactsByAuthorId(txn, authorId);
|
||||||
|
if (contacts.isEmpty()) return new AuthorInfo(UNKNOWN);
|
||||||
|
if (contacts.size() > 1) throw new AssertionError();
|
||||||
|
Contact c = contacts.iterator().next();
|
||||||
|
if (c.isVerified()) return new AuthorInfo(VERIFIED, c.getAlias());
|
||||||
|
else return new AuthorInfo(UNVERIFIED, c.getAlias());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -616,6 +616,12 @@ interface Database<T> {
|
|||||||
void setContactActive(T txn, ContactId c, boolean active)
|
void setContactActive(T txn, ContactId c, boolean active)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an alias name for a contact.
|
||||||
|
*/
|
||||||
|
void setContactAlias(T txn, ContactId c, @Nullable String alias)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the given group's visibility to the given contact to either
|
* Sets the given group's visibility to the given contact to either
|
||||||
* {@link Visibility VISIBLE} or {@link Visibility SHARED}.
|
* {@link Visibility VISIBLE} or {@link Visibility SHARED}.
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
|
|||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.ContactExistsException;
|
import org.briarproject.bramble.api.db.ContactExistsException;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
|
import org.briarproject.bramble.api.db.DbCallable;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.db.DbRunnable;
|
||||||
import org.briarproject.bramble.api.db.Metadata;
|
import org.briarproject.bramble.api.db.Metadata;
|
||||||
import org.briarproject.bramble.api.db.MigrationListener;
|
import org.briarproject.bramble.api.db.MigrationListener;
|
||||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||||
@@ -166,6 +168,31 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
for (Event e : transaction.getEvents()) eventBus.broadcast(e);
|
for (Event e : transaction.getEvents()) eventBus.broadcast(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <E extends Exception> void transaction(boolean readOnly,
|
||||||
|
DbRunnable<E> task) throws DbException, E {
|
||||||
|
Transaction txn = startTransaction(readOnly);
|
||||||
|
try {
|
||||||
|
task.run(txn);
|
||||||
|
commitTransaction(txn);
|
||||||
|
} finally {
|
||||||
|
endTransaction(txn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, E extends Exception> R transactionWithResult(boolean readOnly,
|
||||||
|
DbCallable<R, E> task) throws DbException, E {
|
||||||
|
Transaction txn = startTransaction(readOnly);
|
||||||
|
try {
|
||||||
|
R result = task.call(txn);
|
||||||
|
commitTransaction(txn);
|
||||||
|
return result;
|
||||||
|
} finally {
|
||||||
|
endTransaction(txn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private T unbox(Transaction transaction) {
|
private T unbox(Transaction transaction) {
|
||||||
if (transaction.isCommitted()) throw new IllegalStateException();
|
if (transaction.isCommitted()) throw new IllegalStateException();
|
||||||
return txnClass.cast(transaction.unbox());
|
return txnClass.cast(transaction.unbox());
|
||||||
@@ -832,6 +859,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
transaction.attach(new ContactStatusChangedEvent(c, active));
|
transaction.attach(new ContactStatusChangedEvent(c, active));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContactAlias(Transaction transaction, ContactId c,
|
||||||
|
String alias) throws DbException {
|
||||||
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
|
T txn = unbox(transaction);
|
||||||
|
if (!db.containsContact(txn, c))
|
||||||
|
throw new NoSuchContactException();
|
||||||
|
db.setContactAlias(txn, c, alias);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setGroupVisibility(Transaction transaction, ContactId c,
|
public void setGroupVisibility(Transaction transaction, ContactId c,
|
||||||
GroupId g, Visibility v) throws DbException {
|
GroupId g, Visibility v) throws DbException {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package org.briarproject.bramble.db;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
|
|
||||||
|
import static java.util.concurrent.TimeUnit.DAYS;
|
||||||
|
|
||||||
interface DatabaseConstants {
|
interface DatabaseConstants {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,4 +25,16 @@ interface DatabaseConstants {
|
|||||||
*/
|
*/
|
||||||
String SCHEMA_VERSION_KEY = "schemaVersion";
|
String SCHEMA_VERSION_KEY = "schemaVersion";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link Settings} key under which the time of the last database
|
||||||
|
* compaction is stored.
|
||||||
|
*/
|
||||||
|
String LAST_COMPACTED_KEY = "lastCompacted";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum time between database compactions in milliseconds. When the
|
||||||
|
* database is opened it will be compacted if more than this amount of time
|
||||||
|
* has passed since the last compaction.
|
||||||
|
*/
|
||||||
|
long MAX_COMPACTION_INTERVAL_MS = DAYS.toMillis(30);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package org.briarproject.bramble.db;
|
||||||
|
|
||||||
|
class DatabaseTypes {
|
||||||
|
|
||||||
|
private final String hashType, secretType, binaryType;
|
||||||
|
private final String counterType, stringType;
|
||||||
|
|
||||||
|
public DatabaseTypes(String hashType, String secretType, String binaryType,
|
||||||
|
String counterType, String stringType) {
|
||||||
|
this.hashType = hashType;
|
||||||
|
this.secretType = secretType;
|
||||||
|
this.binaryType = binaryType;
|
||||||
|
this.counterType = counterType;
|
||||||
|
this.stringType = stringType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces database type placeholders in a statement with the actual types.
|
||||||
|
* These placeholders are currently supported:
|
||||||
|
* <li> _HASH
|
||||||
|
* <li> _SECRET
|
||||||
|
* <li> _BINARY
|
||||||
|
* <li> _COUNTER
|
||||||
|
* <li> _STRING
|
||||||
|
*/
|
||||||
|
String replaceTypes(String s) {
|
||||||
|
s = s.replaceAll("_HASH", hashType);
|
||||||
|
s = s.replaceAll("_SECRET", secretType);
|
||||||
|
s = s.replaceAll("_BINARY", binaryType);
|
||||||
|
s = s.replaceAll("_COUNTER", counterType);
|
||||||
|
s = s.replaceAll("_STRING", stringType);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import java.io.File;
|
|||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.DriverManager;
|
import java.sql.DriverManager;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -29,6 +30,8 @@ class H2Database extends JdbcDatabase {
|
|||||||
private static final String BINARY_TYPE = "BINARY";
|
private static final String BINARY_TYPE = "BINARY";
|
||||||
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
|
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
|
||||||
private static final String STRING_TYPE = "VARCHAR";
|
private static final String STRING_TYPE = "VARCHAR";
|
||||||
|
private static final DatabaseTypes dbTypes = new DatabaseTypes(HASH_TYPE,
|
||||||
|
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE);
|
||||||
|
|
||||||
private final DatabaseConfig config;
|
private final DatabaseConfig config;
|
||||||
private final String url;
|
private final String url;
|
||||||
@@ -39,8 +42,7 @@ class H2Database extends JdbcDatabase {
|
|||||||
@Inject
|
@Inject
|
||||||
H2Database(DatabaseConfig config, MessageFactory messageFactory,
|
H2Database(DatabaseConfig config, MessageFactory messageFactory,
|
||||||
Clock clock) {
|
Clock clock) {
|
||||||
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
super(dbTypes, messageFactory, clock);
|
||||||
messageFactory, clock);
|
|
||||||
this.config = config;
|
this.config = config;
|
||||||
File dir = config.getDatabaseDirectory();
|
File dir = config.getDatabaseDirectory();
|
||||||
String path = new File(dir, "db").getAbsolutePath();
|
String path = new File(dir, "db").getAbsolutePath();
|
||||||
@@ -106,4 +108,22 @@ class H2Database extends JdbcDatabase {
|
|||||||
String getUrl() {
|
String getUrl() {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void compactAndClose() throws DbException {
|
||||||
|
Connection c = null;
|
||||||
|
Statement s = null;
|
||||||
|
try {
|
||||||
|
c = createConnection();
|
||||||
|
closeAllConnections();
|
||||||
|
s = c.createStatement();
|
||||||
|
s.execute("SHUTDOWN COMPACT");
|
||||||
|
s.close();
|
||||||
|
c.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
tryToClose(s);
|
||||||
|
tryToClose(c);
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ class HyperSqlDatabase extends JdbcDatabase {
|
|||||||
private static final String COUNTER_TYPE =
|
private static final String COUNTER_TYPE =
|
||||||
"INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY(START WITH 1)";
|
"INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY(START WITH 1)";
|
||||||
private static final String STRING_TYPE = "VARCHAR";
|
private static final String STRING_TYPE = "VARCHAR";
|
||||||
|
private static final DatabaseTypes dbTypes = new DatabaseTypes(HASH_TYPE,
|
||||||
|
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE);
|
||||||
|
|
||||||
private final DatabaseConfig config;
|
private final DatabaseConfig config;
|
||||||
private final String url;
|
private final String url;
|
||||||
@@ -40,8 +42,7 @@ class HyperSqlDatabase extends JdbcDatabase {
|
|||||||
@Inject
|
@Inject
|
||||||
HyperSqlDatabase(DatabaseConfig config, MessageFactory messageFactory,
|
HyperSqlDatabase(DatabaseConfig config, MessageFactory messageFactory,
|
||||||
Clock clock) {
|
Clock clock) {
|
||||||
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
super(dbTypes, messageFactory, clock);
|
||||||
messageFactory, clock);
|
|
||||||
this.config = config;
|
this.config = config;
|
||||||
File dir = config.getDatabaseDirectory();
|
File dir = config.getDatabaseDirectory();
|
||||||
String path = new File(dir, "db").getAbsolutePath();
|
String path = new File(dir, "db").getAbsolutePath();
|
||||||
@@ -61,20 +62,24 @@ class HyperSqlDatabase extends JdbcDatabase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws DbException {
|
public void close() throws DbException {
|
||||||
|
Connection c = null;
|
||||||
|
Statement s = null;
|
||||||
try {
|
try {
|
||||||
super.closeAllConnections();
|
super.closeAllConnections();
|
||||||
Connection c = createConnection();
|
c = createConnection();
|
||||||
Statement s = c.createStatement();
|
s = c.createStatement();
|
||||||
s.executeQuery("SHUTDOWN");
|
s.executeQuery("SHUTDOWN");
|
||||||
s.close();
|
s.close();
|
||||||
c.close();
|
c.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
|
tryToClose(s);
|
||||||
|
tryToClose(c);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getFreeSpace() throws DbException {
|
public long getFreeSpace() {
|
||||||
File dir = config.getDatabaseDirectory();
|
File dir = config.getDatabaseDirectory();
|
||||||
long maxSize = config.getMaxSize();
|
long maxSize = config.getMaxSize();
|
||||||
long free = dir.getFreeSpace();
|
long free = dir.getFreeSpace();
|
||||||
@@ -104,4 +109,22 @@ class HyperSqlDatabase extends JdbcDatabase {
|
|||||||
String hex = StringUtils.toHexString(key.getBytes());
|
String hex = StringUtils.toHexString(key.getBytes());
|
||||||
return DriverManager.getConnection(url + ";crypt_key=" + hex);
|
return DriverManager.getConnection(url + ";crypt_key=" + hex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void compactAndClose() throws DbException {
|
||||||
|
Connection c = null;
|
||||||
|
Statement s = null;
|
||||||
|
try {
|
||||||
|
super.closeAllConnections();
|
||||||
|
c = createConnection();
|
||||||
|
s = c.createStatement();
|
||||||
|
s.executeQuery("SHUTDOWN COMPACT");
|
||||||
|
s.close();
|
||||||
|
c.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
tryToClose(s);
|
||||||
|
tryToClose(c);
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ import java.util.logging.Logger;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static java.sql.Types.INTEGER;
|
import static java.sql.Types.INTEGER;
|
||||||
|
import static java.sql.Types.VARCHAR;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
||||||
@@ -67,9 +68,13 @@ import static org.briarproject.bramble.api.sync.ValidationManager.State.DELIVERE
|
|||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
|
import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
|
||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
|
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
|
||||||
import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPACE;
|
import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPACE;
|
||||||
|
import static org.briarproject.bramble.db.DatabaseConstants.LAST_COMPACTED_KEY;
|
||||||
|
import static org.briarproject.bramble.db.DatabaseConstants.MAX_COMPACTION_INTERVAL_MS;
|
||||||
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
|
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
|
||||||
import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
|
import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A generic database implementation that can be used with any JDBC-compatible
|
* A generic database implementation that can be used with any JDBC-compatible
|
||||||
@@ -79,7 +84,7 @@ import static org.briarproject.bramble.util.LogUtils.logException;
|
|||||||
abstract class JdbcDatabase implements Database<Connection> {
|
abstract class JdbcDatabase implements Database<Connection> {
|
||||||
|
|
||||||
// Package access for testing
|
// Package access for testing
|
||||||
static final int CODE_SCHEMA_VERSION = 40;
|
static final int CODE_SCHEMA_VERSION = 41;
|
||||||
|
|
||||||
// Rotation period offsets for incoming transport keys
|
// Rotation period offsets for incoming transport keys
|
||||||
private static final int OFFSET_PREV = -1;
|
private static final int OFFSET_PREV = -1;
|
||||||
@@ -109,6 +114,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
+ " authorId _HASH NOT NULL,"
|
+ " authorId _HASH NOT NULL,"
|
||||||
+ " formatVersion INT NOT NULL,"
|
+ " formatVersion INT NOT NULL,"
|
||||||
+ " name _STRING NOT NULL,"
|
+ " name _STRING NOT NULL,"
|
||||||
|
+ " alias _STRING," // Null if no alias exists
|
||||||
+ " publicKey _BINARY NOT NULL,"
|
+ " publicKey _BINARY NOT NULL,"
|
||||||
+ " localAuthorId _HASH NOT NULL,"
|
+ " localAuthorId _HASH NOT NULL,"
|
||||||
+ " verified BOOLEAN NOT NULL,"
|
+ " verified BOOLEAN NOT NULL,"
|
||||||
@@ -306,10 +312,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
Logger.getLogger(JdbcDatabase.class.getName());
|
Logger.getLogger(JdbcDatabase.class.getName());
|
||||||
|
|
||||||
// Different database libraries use different names for certain types
|
// Different database libraries use different names for certain types
|
||||||
private final String hashType, secretType, binaryType;
|
|
||||||
private final String counterType, stringType;
|
|
||||||
private final MessageFactory messageFactory;
|
private final MessageFactory messageFactory;
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
|
private final DatabaseTypes dbTypes;
|
||||||
|
|
||||||
// Locking: connectionsLock
|
// Locking: connectionsLock
|
||||||
private final LinkedList<Connection> connections = new LinkedList<>();
|
private final LinkedList<Connection> connections = new LinkedList<>();
|
||||||
@@ -317,20 +322,16 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
private int openConnections = 0; // Locking: connectionsLock
|
private int openConnections = 0; // Locking: connectionsLock
|
||||||
private boolean closed = false; // Locking: connectionsLock
|
private boolean closed = false; // Locking: connectionsLock
|
||||||
|
|
||||||
@Nullable
|
|
||||||
protected abstract Connection createConnection() throws SQLException;
|
protected abstract Connection createConnection() throws SQLException;
|
||||||
|
|
||||||
|
protected abstract void compactAndClose() throws DbException;
|
||||||
|
|
||||||
private final Lock connectionsLock = new ReentrantLock();
|
private final Lock connectionsLock = new ReentrantLock();
|
||||||
private final Condition connectionsChanged = connectionsLock.newCondition();
|
private final Condition connectionsChanged = connectionsLock.newCondition();
|
||||||
|
|
||||||
JdbcDatabase(String hashType, String secretType, String binaryType,
|
JdbcDatabase(DatabaseTypes databaseTypes, MessageFactory messageFactory,
|
||||||
String counterType, String stringType,
|
Clock clock) {
|
||||||
MessageFactory messageFactory, Clock clock) {
|
this.dbTypes = databaseTypes;
|
||||||
this.hashType = hashType;
|
|
||||||
this.secretType = secretType;
|
|
||||||
this.binaryType = binaryType;
|
|
||||||
this.counterType = counterType;
|
|
||||||
this.stringType = stringType;
|
|
||||||
this.messageFactory = messageFactory;
|
this.messageFactory = messageFactory;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
}
|
}
|
||||||
@@ -344,13 +345,16 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
// Open the database and create the tables and indexes if necessary
|
// Open the database and create the tables and indexes if necessary
|
||||||
|
boolean compact;
|
||||||
Connection txn = startTransaction();
|
Connection txn = startTransaction();
|
||||||
try {
|
try {
|
||||||
if (reopen) {
|
if (reopen) {
|
||||||
checkSchemaVersion(txn, listener);
|
Settings s = getSettings(txn, DB_SETTINGS_NAMESPACE);
|
||||||
|
compact = migrateSchema(txn, s, listener) || isCompactionDue(s);
|
||||||
} else {
|
} else {
|
||||||
createTables(txn);
|
createTables(txn);
|
||||||
storeSchemaVersion(txn, CODE_SCHEMA_VERSION);
|
initialiseSettings(txn);
|
||||||
|
compact = false;
|
||||||
}
|
}
|
||||||
createIndexes(txn);
|
createIndexes(txn);
|
||||||
commitTransaction(txn);
|
commitTransaction(txn);
|
||||||
@@ -358,6 +362,25 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
abortTransaction(txn);
|
abortTransaction(txn);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
// Compact the database if necessary
|
||||||
|
if (compact) {
|
||||||
|
if (listener != null) listener.onDatabaseCompaction();
|
||||||
|
long start = now();
|
||||||
|
compactAndClose();
|
||||||
|
logDuration(LOG, "Compacting database", start);
|
||||||
|
// Allow the next transaction to reopen the DB
|
||||||
|
synchronized (connectionsLock) {
|
||||||
|
closed = false;
|
||||||
|
}
|
||||||
|
txn = startTransaction();
|
||||||
|
try {
|
||||||
|
storeLastCompacted(txn);
|
||||||
|
commitTransaction(txn);
|
||||||
|
} catch (DbException e) {
|
||||||
|
abortTransaction(txn);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -365,17 +388,18 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
* version used by the current code and applies any suitable migrations to
|
* version used by the current code and applies any suitable migrations to
|
||||||
* the data if necessary.
|
* the data if necessary.
|
||||||
*
|
*
|
||||||
|
* @return true if any migrations were applied, false if the schema was
|
||||||
|
* already current
|
||||||
* @throws DataTooNewException if the data uses a newer schema than the
|
* @throws DataTooNewException if the data uses a newer schema than the
|
||||||
* current code
|
* current code
|
||||||
* @throws DataTooOldException if the data uses an older schema than the
|
* @throws DataTooOldException if the data uses an older schema than the
|
||||||
* current code and cannot be migrated
|
* current code and cannot be migrated
|
||||||
*/
|
*/
|
||||||
private void checkSchemaVersion(Connection txn,
|
private boolean migrateSchema(Connection txn, Settings s,
|
||||||
@Nullable MigrationListener listener) throws DbException {
|
@Nullable MigrationListener listener) throws DbException {
|
||||||
Settings s = getSettings(txn, DB_SETTINGS_NAMESPACE);
|
|
||||||
int dataSchemaVersion = s.getInt(SCHEMA_VERSION_KEY, -1);
|
int dataSchemaVersion = s.getInt(SCHEMA_VERSION_KEY, -1);
|
||||||
if (dataSchemaVersion == -1) throw new DbException();
|
if (dataSchemaVersion == -1) throw new DbException();
|
||||||
if (dataSchemaVersion == CODE_SCHEMA_VERSION) return;
|
if (dataSchemaVersion == CODE_SCHEMA_VERSION) return false;
|
||||||
if (CODE_SCHEMA_VERSION < dataSchemaVersion)
|
if (CODE_SCHEMA_VERSION < dataSchemaVersion)
|
||||||
throw new DataTooNewException();
|
throw new DataTooNewException();
|
||||||
// Apply any suitable migrations in order
|
// Apply any suitable migrations in order
|
||||||
@@ -384,7 +408,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (start == dataSchemaVersion) {
|
if (start == dataSchemaVersion) {
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Migrating from schema " + start + " to " + end);
|
LOG.info("Migrating from schema " + start + " to " + end);
|
||||||
if (listener != null) listener.onMigrationRun();
|
if (listener != null) listener.onDatabaseMigration();
|
||||||
// Apply the migration
|
// Apply the migration
|
||||||
m.migrate(txn);
|
m.migrate(txn);
|
||||||
// Store the new schema version
|
// Store the new schema version
|
||||||
@@ -394,11 +418,24 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
if (dataSchemaVersion != CODE_SCHEMA_VERSION)
|
if (dataSchemaVersion != CODE_SCHEMA_VERSION)
|
||||||
throw new DataTooOldException();
|
throw new DataTooOldException();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Package access for testing
|
// Package access for testing
|
||||||
List<Migration<Connection>> getMigrations() {
|
List<Migration<Connection>> getMigrations() {
|
||||||
return Arrays.asList(new Migration38_39(), new Migration39_40());
|
return Arrays.asList(
|
||||||
|
new Migration38_39(),
|
||||||
|
new Migration39_40(),
|
||||||
|
new Migration40_41(dbTypes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCompactionDue(Settings s) {
|
||||||
|
long lastCompacted = s.getLong(LAST_COMPACTED_KEY, 0);
|
||||||
|
long elapsed = clock.currentTimeMillis() - lastCompacted;
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info(elapsed + " ms since last compaction");
|
||||||
|
return elapsed > MAX_COMPACTION_INTERVAL_MS;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storeSchemaVersion(Connection txn, int version)
|
private void storeSchemaVersion(Connection txn, int version)
|
||||||
@@ -408,6 +445,19 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
mergeSettings(txn, s, DB_SETTINGS_NAMESPACE);
|
mergeSettings(txn, s, DB_SETTINGS_NAMESPACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void storeLastCompacted(Connection txn) throws DbException {
|
||||||
|
Settings s = new Settings();
|
||||||
|
s.putLong(LAST_COMPACTED_KEY, clock.currentTimeMillis());
|
||||||
|
mergeSettings(txn, s, DB_SETTINGS_NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialiseSettings(Connection txn) throws DbException {
|
||||||
|
Settings s = new Settings();
|
||||||
|
s.putInt(SCHEMA_VERSION_KEY, CODE_SCHEMA_VERSION);
|
||||||
|
s.putLong(LAST_COMPACTED_KEY, clock.currentTimeMillis());
|
||||||
|
mergeSettings(txn, s, DB_SETTINGS_NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
private void tryToClose(@Nullable ResultSet rs) {
|
private void tryToClose(@Nullable ResultSet rs) {
|
||||||
try {
|
try {
|
||||||
if (rs != null) rs.close();
|
if (rs != null) rs.close();
|
||||||
@@ -416,7 +466,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryToClose(@Nullable Statement s) {
|
protected void tryToClose(@Nullable Statement s) {
|
||||||
try {
|
try {
|
||||||
if (s != null) s.close();
|
if (s != null) s.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@@ -424,24 +474,32 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void tryToClose(@Nullable Connection c) {
|
||||||
|
try {
|
||||||
|
if (c != null) c.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void createTables(Connection txn) throws DbException {
|
private void createTables(Connection txn) throws DbException {
|
||||||
Statement s = null;
|
Statement s = null;
|
||||||
try {
|
try {
|
||||||
s = txn.createStatement();
|
s = txn.createStatement();
|
||||||
s.executeUpdate(insertTypeNames(CREATE_SETTINGS));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_SETTINGS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_LOCAL_AUTHORS));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_LOCAL_AUTHORS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_CONTACTS));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_CONTACTS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_GROUPS));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_GROUPS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_GROUP_METADATA));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_GROUP_METADATA));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_GROUP_VISIBILITIES));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_GROUP_VISIBILITIES));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_MESSAGES));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_MESSAGES));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_MESSAGE_METADATA));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_MESSAGE_METADATA));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_MESSAGE_DEPENDENCIES));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_MESSAGE_DEPENDENCIES));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_OFFERS));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_OFFERS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_STATUSES));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_STATUSES));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_TRANSPORTS));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_TRANSPORTS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_OUTGOING_KEYS));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_OUTGOING_KEYS));
|
||||||
s.executeUpdate(insertTypeNames(CREATE_INCOMING_KEYS));
|
s.executeUpdate(dbTypes.replaceTypes(CREATE_INCOMING_KEYS));
|
||||||
s.close();
|
s.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s);
|
tryToClose(s);
|
||||||
@@ -466,15 +524,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String insertTypeNames(String s) {
|
|
||||||
s = s.replaceAll("_HASH", hashType);
|
|
||||||
s = s.replaceAll("_SECRET", secretType);
|
|
||||||
s = s.replaceAll("_BINARY", binaryType);
|
|
||||||
s = s.replaceAll("_COUNTER", counterType);
|
|
||||||
s = s.replaceAll("_STRING", stringType);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Connection startTransaction() throws DbException {
|
public Connection startTransaction() throws DbException {
|
||||||
Connection txn;
|
Connection txn;
|
||||||
@@ -489,7 +538,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
if (txn == null) {
|
if (txn == null) {
|
||||||
// Open a new connection
|
// Open a new connection
|
||||||
txn = createConnection();
|
txn = createConnection();
|
||||||
if (txn == null) throw new DbException();
|
|
||||||
txn.setAutoCommit(false);
|
txn.setAutoCommit(false);
|
||||||
connectionsLock.lock();
|
connectionsLock.lock();
|
||||||
try {
|
try {
|
||||||
@@ -1201,8 +1249,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT authorId, formatVersion, name, publicKey,"
|
String sql = "SELECT authorId, formatVersion, name, alias,"
|
||||||
+ " localAuthorId, verified, active"
|
+ " publicKey, localAuthorId, verified, active"
|
||||||
+ " FROM contacts"
|
+ " FROM contacts"
|
||||||
+ " WHERE contactId = ?";
|
+ " WHERE contactId = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
@@ -1212,15 +1260,17 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
AuthorId authorId = new AuthorId(rs.getBytes(1));
|
AuthorId authorId = new AuthorId(rs.getBytes(1));
|
||||||
int formatVersion = rs.getInt(2);
|
int formatVersion = rs.getInt(2);
|
||||||
String name = rs.getString(3);
|
String name = rs.getString(3);
|
||||||
byte[] publicKey = rs.getBytes(4);
|
String alias = rs.getString(4);
|
||||||
AuthorId localAuthorId = new AuthorId(rs.getBytes(5));
|
byte[] publicKey = rs.getBytes(5);
|
||||||
boolean verified = rs.getBoolean(6);
|
AuthorId localAuthorId = new AuthorId(rs.getBytes(6));
|
||||||
boolean active = rs.getBoolean(7);
|
boolean verified = rs.getBoolean(7);
|
||||||
|
boolean active = rs.getBoolean(8);
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
Author author =
|
Author author =
|
||||||
new Author(authorId, formatVersion, name, publicKey);
|
new Author(authorId, formatVersion, name, publicKey);
|
||||||
return new Contact(c, author, localAuthorId, verified, active);
|
return new Contact(c, author, localAuthorId, alias, verified,
|
||||||
|
active);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs);
|
tryToClose(rs);
|
||||||
tryToClose(ps);
|
tryToClose(ps);
|
||||||
@@ -1235,7 +1285,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT contactId, authorId, formatVersion, name,"
|
String sql = "SELECT contactId, authorId, formatVersion, name,"
|
||||||
+ " publicKey, localAuthorId, verified, active"
|
+ " alias, publicKey, localAuthorId, verified, active"
|
||||||
+ " FROM contacts";
|
+ " FROM contacts";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
rs = ps.executeQuery();
|
rs = ps.executeQuery();
|
||||||
@@ -1245,14 +1295,15 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
AuthorId authorId = new AuthorId(rs.getBytes(2));
|
AuthorId authorId = new AuthorId(rs.getBytes(2));
|
||||||
int formatVersion = rs.getInt(3);
|
int formatVersion = rs.getInt(3);
|
||||||
String name = rs.getString(4);
|
String name = rs.getString(4);
|
||||||
byte[] publicKey = rs.getBytes(5);
|
String alias = rs.getString(5);
|
||||||
|
byte[] publicKey = rs.getBytes(6);
|
||||||
Author author =
|
Author author =
|
||||||
new Author(authorId, formatVersion, name, publicKey);
|
new Author(authorId, formatVersion, name, publicKey);
|
||||||
AuthorId localAuthorId = new AuthorId(rs.getBytes(6));
|
AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
|
||||||
boolean verified = rs.getBoolean(7);
|
boolean verified = rs.getBoolean(8);
|
||||||
boolean active = rs.getBoolean(8);
|
boolean active = rs.getBoolean(9);
|
||||||
contacts.add(new Contact(contactId, author, localAuthorId,
|
contacts.add(new Contact(contactId, author, localAuthorId,
|
||||||
verified, active));
|
alias, verified, active));
|
||||||
}
|
}
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -1293,8 +1344,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
String sql = "SELECT contactId, formatVersion, name, publicKey,"
|
String sql = "SELECT contactId, formatVersion, name, alias,"
|
||||||
+ " localAuthorId, verified, active"
|
+ " publicKey, localAuthorId, verified, active"
|
||||||
+ " FROM contacts"
|
+ " FROM contacts"
|
||||||
+ " WHERE authorId = ?";
|
+ " WHERE authorId = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
@@ -1305,14 +1356,15 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ContactId c = new ContactId(rs.getInt(1));
|
ContactId c = new ContactId(rs.getInt(1));
|
||||||
int formatVersion = rs.getInt(2);
|
int formatVersion = rs.getInt(2);
|
||||||
String name = rs.getString(3);
|
String name = rs.getString(3);
|
||||||
byte[] publicKey = rs.getBytes(4);
|
String alias = rs.getString(4);
|
||||||
AuthorId localAuthorId = new AuthorId(rs.getBytes(5));
|
byte[] publicKey = rs.getBytes(5);
|
||||||
boolean verified = rs.getBoolean(6);
|
AuthorId localAuthorId = new AuthorId(rs.getBytes(6));
|
||||||
boolean active = rs.getBoolean(7);
|
boolean verified = rs.getBoolean(7);
|
||||||
|
boolean active = rs.getBoolean(8);
|
||||||
Author author =
|
Author author =
|
||||||
new Author(remote, formatVersion, name, publicKey);
|
new Author(remote, formatVersion, name, publicKey);
|
||||||
contacts.add(new Contact(c, author, localAuthorId, verified,
|
contacts.add(new Contact(c, author, localAuthorId, alias,
|
||||||
active));
|
verified, active));
|
||||||
}
|
}
|
||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -1508,7 +1560,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
if (raw == null) throw new MessageDeletedException();
|
if (raw == null) throw new MessageDeletedException();
|
||||||
if (raw.length < MESSAGE_HEADER_LENGTH) throw new AssertionError();
|
if (raw.length <= MESSAGE_HEADER_LENGTH) throw new AssertionError();
|
||||||
byte[] body = new byte[raw.length - MESSAGE_HEADER_LENGTH];
|
byte[] body = new byte[raw.length - MESSAGE_HEADER_LENGTH];
|
||||||
System.arraycopy(raw, MESSAGE_HEADER_LENGTH, body, 0, body.length);
|
System.arraycopy(raw, MESSAGE_HEADER_LENGTH, body, 0, body.length);
|
||||||
return new Message(m, g, timestamp, body);
|
return new Message(m, g, timestamp, body);
|
||||||
@@ -2737,6 +2789,25 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContactAlias(Connection txn, ContactId c,
|
||||||
|
@Nullable String alias) throws DbException {
|
||||||
|
PreparedStatement ps = null;
|
||||||
|
try {
|
||||||
|
String sql = "UPDATE contacts SET alias = ? WHERE contactId = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
if (alias == null) ps.setNull(1, VARCHAR);
|
||||||
|
else ps.setString(1, alias);
|
||||||
|
ps.setInt(2, c.getInt());
|
||||||
|
int affected = ps.executeUpdate();
|
||||||
|
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||||
|
ps.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
tryToClose(ps);
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setGroupVisibility(Connection txn, ContactId c, GroupId g,
|
public void setGroupVisibility(Connection txn, ContactId c, GroupId g,
|
||||||
boolean shared) throws DbException {
|
boolean shared) throws DbException {
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package org.briarproject.bramble.db;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
|
class Migration40_41 implements Migration<Connection> {
|
||||||
|
|
||||||
|
private static final Logger LOG = getLogger(Migration40_41.class.getName());
|
||||||
|
|
||||||
|
private final DatabaseTypes dbTypes;
|
||||||
|
|
||||||
|
public Migration40_41(DatabaseTypes databaseTypes) {
|
||||||
|
this.dbTypes = databaseTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getStartVersion() {
|
||||||
|
return 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getEndVersion() {
|
||||||
|
return 41;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void migrate(Connection txn) throws DbException {
|
||||||
|
Statement s = null;
|
||||||
|
try {
|
||||||
|
s = txn.createStatement();
|
||||||
|
s.execute("ALTER TABLE contacts"
|
||||||
|
+ dbTypes.replaceTypes(" ADD alias VARCHAR"));
|
||||||
|
} catch (SQLException e) {
|
||||||
|
tryToClose(s);
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryToClose(@Nullable Statement s) {
|
||||||
|
try {
|
||||||
|
if (s != null) s.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +1,21 @@
|
|||||||
package org.briarproject.bramble.identity;
|
package org.briarproject.bramble.identity;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author.Status;
|
|
||||||
import org.briarproject.bramble.api.identity.AuthorFactory;
|
import org.briarproject.bramble.api.identity.AuthorFactory;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
|
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.UNKNOWN;
|
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.UNVERIFIED;
|
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.VERIFIED;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
|
|
||||||
@@ -118,26 +110,4 @@ class IdentityManagerImpl implements IdentityManager {
|
|||||||
return db.getLocalAuthors(txn).iterator().next();
|
return db.getLocalAuthors(txn).iterator().next();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Status getAuthorStatus(AuthorId authorId) throws DbException {
|
|
||||||
Transaction txn = db.startTransaction(true);
|
|
||||||
try {
|
|
||||||
return getAuthorStatus(txn, authorId);
|
|
||||||
} finally {
|
|
||||||
db.endTransaction(txn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Status getAuthorStatus(Transaction txn, AuthorId authorId)
|
|
||||||
throws DbException {
|
|
||||||
if (getLocalAuthor(txn).getId().equals(authorId)) return OURSELVES;
|
|
||||||
Collection<Contact> contacts = db.getContactsByAuthorId(txn, authorId);
|
|
||||||
if (contacts.isEmpty()) return UNKNOWN;
|
|
||||||
for (Contact c : contacts) {
|
|
||||||
if (c.isVerified()) return VERIFIED;
|
|
||||||
}
|
|
||||||
return UNVERIFIED;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import javax.inject.Inject;
|
|||||||
import static java.util.logging.Level.FINE;
|
import static java.util.logging.Level.FINE;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.COMPACTING_DATABASE;
|
||||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.MIGRATING_DATABASE;
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.MIGRATING_DATABASE;
|
||||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
||||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING;
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING;
|
||||||
@@ -159,11 +160,17 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMigrationRun() {
|
public void onDatabaseMigration() {
|
||||||
state = MIGRATING_DATABASE;
|
state = MIGRATING_DATABASE;
|
||||||
eventBus.broadcast(new LifecycleEvent(MIGRATING_DATABASE));
|
eventBus.broadcast(new LifecycleEvent(MIGRATING_DATABASE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDatabaseCompaction() {
|
||||||
|
state = COMPACTING_DATABASE;
|
||||||
|
eventBus.broadcast(new LifecycleEvent(COMPACTING_DATABASE));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopServices() {
|
public void stopServices() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import org.briarproject.bramble.api.plugin.event.EnableBluetoothEvent;
|
|||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
|
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
@@ -46,6 +45,9 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
|
|||||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
|
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.macToBytes;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.macToString;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -96,6 +98,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
|||||||
abstract DuplexTransportConnection connectTo(String address, String uuid)
|
abstract DuplexTransportConnection connectTo(String address, String uuid)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
abstract DuplexTransportConnection discoverAndConnect(String uuid);
|
||||||
|
|
||||||
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
|
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
|
||||||
Executor ioExecutor, SecureRandom secureRandom,
|
Executor ioExecutor, SecureRandom secureRandom,
|
||||||
Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
|
Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
|
||||||
@@ -193,7 +198,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
|||||||
address = getBluetoothAddress();
|
address = getBluetoothAddress();
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Local address " + scrubMacAddress(address));
|
LOG.info("Local address " + scrubMacAddress(address));
|
||||||
if (!StringUtils.isNullOrEmpty(address)) {
|
if (!isNullOrEmpty(address)) {
|
||||||
p.put(PROP_ADDRESS, address);
|
p.put(PROP_ADDRESS, address);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
@@ -256,9 +261,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
|||||||
// Try to connect to known devices in parallel
|
// Try to connect to known devices in parallel
|
||||||
for (Entry<ContactId, TransportProperties> e : contacts.entrySet()) {
|
for (Entry<ContactId, TransportProperties> e : contacts.entrySet()) {
|
||||||
String address = e.getValue().get(PROP_ADDRESS);
|
String address = e.getValue().get(PROP_ADDRESS);
|
||||||
if (StringUtils.isNullOrEmpty(address)) continue;
|
if (isNullOrEmpty(address)) continue;
|
||||||
String uuid = e.getValue().get(PROP_UUID);
|
String uuid = e.getValue().get(PROP_UUID);
|
||||||
if (StringUtils.isNullOrEmpty(uuid)) continue;
|
if (isNullOrEmpty(uuid)) continue;
|
||||||
ContactId c = e.getKey();
|
ContactId c = e.getKey();
|
||||||
ioExecutor.execute(() -> {
|
ioExecutor.execute(() -> {
|
||||||
if (!isRunning() || !shouldAllowContactConnections()) return;
|
if (!isRunning() || !shouldAllowContactConnections()) return;
|
||||||
@@ -309,9 +314,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
|||||||
if (!isRunning() || !shouldAllowContactConnections()) return null;
|
if (!isRunning() || !shouldAllowContactConnections()) return null;
|
||||||
if (!connectionLimiter.canOpenContactConnection()) return null;
|
if (!connectionLimiter.canOpenContactConnection()) return null;
|
||||||
String address = p.get(PROP_ADDRESS);
|
String address = p.get(PROP_ADDRESS);
|
||||||
if (StringUtils.isNullOrEmpty(address)) return null;
|
if (isNullOrEmpty(address)) return null;
|
||||||
String uuid = p.get(PROP_UUID);
|
String uuid = p.get(PROP_UUID);
|
||||||
if (StringUtils.isNullOrEmpty(uuid)) return null;
|
if (isNullOrEmpty(uuid)) return null;
|
||||||
DuplexTransportConnection conn = connect(address, uuid);
|
DuplexTransportConnection conn = connect(address, uuid);
|
||||||
if (conn == null) return null;
|
if (conn == null) return null;
|
||||||
// TODO: Why don't we reset the backoff here?
|
// TODO: Why don't we reset the backoff here?
|
||||||
@@ -326,9 +331,6 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
|
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
|
||||||
if (!isRunning()) return null;
|
if (!isRunning()) return null;
|
||||||
// There's no point listening if we can't discover our own address
|
|
||||||
String address = getBluetoothAddress();
|
|
||||||
if (address == null) return null;
|
|
||||||
// No truncation necessary because COMMIT_LENGTH = 16
|
// No truncation necessary because COMMIT_LENGTH = 16
|
||||||
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
|
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid);
|
if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid);
|
||||||
@@ -346,7 +348,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
|||||||
}
|
}
|
||||||
BdfList descriptor = new BdfList();
|
BdfList descriptor = new BdfList();
|
||||||
descriptor.add(TRANSPORT_ID_BLUETOOTH);
|
descriptor.add(TRANSPORT_ID_BLUETOOTH);
|
||||||
descriptor.add(StringUtils.macToBytes(address));
|
String address = getBluetoothAddress();
|
||||||
|
if (address != null) descriptor.add(macToBytes(address));
|
||||||
return new BluetoothKeyAgreementListener(descriptor, ss);
|
return new BluetoothKeyAgreementListener(descriptor, ss);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,18 +357,25 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
|||||||
public DuplexTransportConnection createKeyAgreementConnection(
|
public DuplexTransportConnection createKeyAgreementConnection(
|
||||||
byte[] commitment, BdfList descriptor) {
|
byte[] commitment, BdfList descriptor) {
|
||||||
if (!isRunning()) return null;
|
if (!isRunning()) return null;
|
||||||
String address;
|
|
||||||
try {
|
|
||||||
address = parseAddress(descriptor);
|
|
||||||
} catch (FormatException e) {
|
|
||||||
LOG.info("Invalid address in key agreement descriptor");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// No truncation necessary because COMMIT_LENGTH = 16
|
// No truncation necessary because COMMIT_LENGTH = 16
|
||||||
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
|
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
|
||||||
if (LOG.isLoggable(INFO))
|
DuplexTransportConnection conn;
|
||||||
LOG.info("Connecting to key agreement UUID " + uuid);
|
if (descriptor.size() == 1) {
|
||||||
DuplexTransportConnection conn = connect(address, uuid);
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Discovering address for key agreement UUID " + uuid);
|
||||||
|
conn = discoverAndConnect(uuid);
|
||||||
|
} else {
|
||||||
|
String address;
|
||||||
|
try {
|
||||||
|
address = parseAddress(descriptor);
|
||||||
|
} catch (FormatException e) {
|
||||||
|
LOG.info("Invalid address in key agreement descriptor");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Connecting to key agreement UUID " + uuid);
|
||||||
|
conn = connect(address, uuid);
|
||||||
|
}
|
||||||
if (conn != null) connectionLimiter.keyAgreementConnectionOpened(conn);
|
if (conn != null) connectionLimiter.keyAgreementConnectionOpened(conn);
|
||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
@@ -373,7 +383,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
|||||||
private String parseAddress(BdfList descriptor) throws FormatException {
|
private String parseAddress(BdfList descriptor) throws FormatException {
|
||||||
byte[] mac = descriptor.getRaw(1);
|
byte[] mac = descriptor.getRaw(1);
|
||||||
if (mac.length != 6) throw new FormatException();
|
if (mac.length != 6) throw new FormatException();
|
||||||
return StringUtils.macToString(mac);
|
return macToString(mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import org.briarproject.bramble.api.system.Clock;
|
|||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||||
import org.briarproject.bramble.util.IoUtils;
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
@@ -70,9 +69,11 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_
|
|||||||
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER;
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER;
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WITH_BRIDGES;
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WITH_BRIDGES;
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION;
|
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V2;
|
||||||
|
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
|
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -87,7 +88,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private static final String OWNER = "__OwningControllerProcess";
|
private static final String OWNER = "__OwningControllerProcess";
|
||||||
private static final int COOKIE_TIMEOUT_MS = 3000;
|
private static final int COOKIE_TIMEOUT_MS = 3000;
|
||||||
private static final int COOKIE_POLLING_INTERVAL_MS = 200;
|
private static final int COOKIE_POLLING_INTERVAL_MS = 200;
|
||||||
private static final Pattern ONION = Pattern.compile("[a-z2-7]{16}");
|
private static final Pattern ONION_V2 = Pattern.compile("[a-z2-7]{16}");
|
||||||
|
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
|
||||||
|
|
||||||
private final Executor ioExecutor, connectionStatusExecutor;
|
private final Executor ioExecutor, connectionStatusExecutor;
|
||||||
private final NetworkManager networkManager;
|
private final NetworkManager networkManager;
|
||||||
@@ -362,7 +364,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
// If there's already a port number stored in config, reuse it
|
// If there's already a port number stored in config, reuse it
|
||||||
String portString = settings.get(PREF_TOR_PORT);
|
String portString = settings.get(PREF_TOR_PORT);
|
||||||
int port;
|
int port;
|
||||||
if (StringUtils.isNullOrEmpty(portString)) port = 0;
|
if (isNullOrEmpty(portString)) port = 0;
|
||||||
else port = Integer.parseInt(portString);
|
else port = Integer.parseInt(portString);
|
||||||
// Bind a server socket to receive connections from Tor
|
// Bind a server socket to receive connections from Tor
|
||||||
ServerSocket ss = null;
|
ServerSocket ss = null;
|
||||||
@@ -427,11 +429,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Publish the hidden service's onion hostname in transport properties
|
// Publish the hidden service's onion hostname in transport properties
|
||||||
String hostname = response.get(HS_ADDRESS);
|
String onion2 = response.get(HS_ADDRESS);
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Hidden service " + scrubOnion(hostname));
|
LOG.info("Hidden service " + scrubOnion(onion2));
|
||||||
TransportProperties p = new TransportProperties();
|
TransportProperties p = new TransportProperties();
|
||||||
p.put(PROP_ONION, hostname);
|
p.put(PROP_ONION_V2, onion2);
|
||||||
callback.mergeLocalProperties(p);
|
callback.mergeLocalProperties(p);
|
||||||
if (privKey == null) {
|
if (privKey == null) {
|
||||||
// Save the hidden service's private key for next time
|
// Save the hidden service's private key for next time
|
||||||
@@ -530,26 +532,41 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public DuplexTransportConnection createConnection(TransportProperties p) {
|
public DuplexTransportConnection createConnection(TransportProperties p) {
|
||||||
if (!isRunning()) return null;
|
if (!isRunning()) return null;
|
||||||
String onion = p.get(PROP_ONION);
|
String bestOnion = null;
|
||||||
if (StringUtils.isNullOrEmpty(onion)) return null;
|
String onion2 = p.get(PROP_ONION_V2);
|
||||||
if (!ONION.matcher(onion).matches()) {
|
String onion3 = p.get(PROP_ONION_V3);
|
||||||
// not scrubbing this address, so we are able to find the problem
|
if (!isNullOrEmpty(onion2)) {
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("Invalid hostname: " + onion);
|
if (ONION_V2.matcher(onion2).matches()) {
|
||||||
return null;
|
bestOnion = onion2;
|
||||||
|
} else {
|
||||||
|
// Don't scrub the address so we can find the problem
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Invalid v2 hostname: " + onion2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (!isNullOrEmpty(onion3)) {
|
||||||
|
if (ONION_V3.matcher(onion3).matches()) {
|
||||||
|
bestOnion = onion3;
|
||||||
|
} else {
|
||||||
|
// Don't scrub the address so we can find the problem
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Invalid v3 hostname: " + onion3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bestOnion == null) return null;
|
||||||
Socket s = null;
|
Socket s = null;
|
||||||
try {
|
try {
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Connecting to " + scrubOnion(onion));
|
LOG.info("Connecting to " + scrubOnion(bestOnion));
|
||||||
s = torSocketFactory.createSocket(onion + ".onion", 80);
|
s = torSocketFactory.createSocket(bestOnion + ".onion", 80);
|
||||||
s.setSoTimeout(socketTimeout);
|
s.setSoTimeout(socketTimeout);
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Connected to " + scrubOnion(onion));
|
LOG.info("Connected to " + scrubOnion(bestOnion));
|
||||||
return new TorTransportConnection(this, s);
|
return new TorTransportConnection(this, s);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("Could not connect to " + scrubOnion(onion) + ": " +
|
LOG.info("Could not connect to " + scrubOnion(bestOnion)
|
||||||
e.toString());
|
+ ": " + e.toString());
|
||||||
}
|
}
|
||||||
tryToClose(s);
|
tryToClose(s);
|
||||||
return null;
|
return null;
|
||||||
@@ -627,6 +644,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
if (s.getNamespace().equals(ID.getString())) {
|
if (s.getNamespace().equals(ID.getString())) {
|
||||||
LOG.info("Tor settings updated");
|
LOG.info("Tor settings updated");
|
||||||
settings = s.getSettings();
|
settings = s.getSettings();
|
||||||
|
// Works around a bug introduced in Tor 0.3.4.8. Could be
|
||||||
|
// replaced with callback.transportDisabled() when fixed.
|
||||||
|
disableNetwork();
|
||||||
updateConnectionStatus(networkManager.getNetworkStatus());
|
updateConnectionStatus(networkManager.getNetworkStatus());
|
||||||
}
|
}
|
||||||
} else if (e instanceof NetworkStatusEvent) {
|
} else if (e instanceof NetworkStatusEvent) {
|
||||||
@@ -634,6 +654,16 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void disableNetwork() {
|
||||||
|
connectionStatusExecutor.execute(() -> {
|
||||||
|
try {
|
||||||
|
enableNetwork(false);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logException(LOG, WARNING, ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void updateConnectionStatus(NetworkStatus status) {
|
private void updateConnectionStatus(NetworkStatus status) {
|
||||||
connectionStatusExecutor.execute(() -> {
|
connectionStatusExecutor.execute(() -> {
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ class MessageFactoryImpl implements MessageFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Message createMessage(GroupId g, long timestamp, byte[] body) {
|
public Message createMessage(GroupId g, long timestamp, byte[] body) {
|
||||||
|
if (body.length == 0) throw new IllegalArgumentException();
|
||||||
if (body.length > MAX_MESSAGE_BODY_LENGTH)
|
if (body.length > MAX_MESSAGE_BODY_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
MessageId id = getMessageId(g, timestamp, body);
|
MessageId id = getMessageId(g, timestamp, body);
|
||||||
@@ -54,7 +55,7 @@ class MessageFactoryImpl implements MessageFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Message createMessage(byte[] raw) {
|
public Message createMessage(byte[] raw) {
|
||||||
if (raw.length < MESSAGE_HEADER_LENGTH)
|
if (raw.length <= MESSAGE_HEADER_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
if (raw.length > MAX_MESSAGE_LENGTH)
|
if (raw.length > MAX_MESSAGE_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
|||||||
@@ -124,7 +124,8 @@ class SyncRecordReaderImpl implements SyncRecordReader {
|
|||||||
if (!hasMessage()) throw new FormatException();
|
if (!hasMessage()) throw new FormatException();
|
||||||
if (nextRecord == null) throw new AssertionError();
|
if (nextRecord == null) throw new AssertionError();
|
||||||
byte[] payload = nextRecord.getPayload();
|
byte[] payload = nextRecord.getPayload();
|
||||||
if (payload.length < MESSAGE_HEADER_LENGTH) throw new FormatException();
|
if (payload.length <= MESSAGE_HEADER_LENGTH)
|
||||||
|
throw new FormatException();
|
||||||
// Validate timestamp
|
// Validate timestamp
|
||||||
long timestamp = ByteUtils.readUint64(payload, UniqueId.LENGTH);
|
long timestamp = ByteUtils.readUint64(payload, UniqueId.LENGTH);
|
||||||
if (timestamp < 0) throw new FormatException();
|
if (timestamp < 0) throw new FormatException();
|
||||||
|
|||||||
@@ -79,18 +79,6 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
|||||||
assertSame(meta, messageContext.getMetadata());
|
assertSame(meta, messageContext.getMetadata());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = InvalidMessageException.class)
|
|
||||||
public void testRejectsTooShortMessage() throws Exception {
|
|
||||||
Message invalidMessage = getMessage(groupId, 0);
|
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(clock).currentTimeMillis();
|
|
||||||
will(returnValue(timestamp));
|
|
||||||
}});
|
|
||||||
|
|
||||||
failIfSubclassIsCalled.validateMessage(invalidMessage, group);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAcceptsMinLengthMessage() throws Exception {
|
public void testAcceptsMinLengthMessage() throws Exception {
|
||||||
Message shortMessage = getMessage(groupId, 1);
|
Message shortMessage = getMessage(groupId, 1);
|
||||||
|
|||||||
@@ -5,12 +5,17 @@ import org.briarproject.bramble.api.contact.ContactId;
|
|||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
|
import org.briarproject.bramble.api.identity.AuthorInfo;
|
||||||
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
import org.briarproject.bramble.api.transport.KeyManager;
|
import org.briarproject.bramble.api.transport.KeyManager;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
|
import org.briarproject.bramble.test.DbExpectations;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.jmock.Mockery;
|
import org.jmock.Mockery;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -20,10 +25,20 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
|
||||||
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
|
||||||
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
|
||||||
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class ContactManagerImplTest extends BrambleMockTestCase {
|
public class ContactManagerImplTest extends BrambleMockTestCase {
|
||||||
@@ -31,16 +46,20 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
private final Mockery context = new Mockery();
|
private final Mockery context = new Mockery();
|
||||||
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||||
private final KeyManager keyManager = context.mock(KeyManager.class);
|
private final KeyManager keyManager = context.mock(KeyManager.class);
|
||||||
|
private final IdentityManager identityManager =
|
||||||
|
context.mock(IdentityManager.class);
|
||||||
private final ContactManager contactManager;
|
private final ContactManager contactManager;
|
||||||
private final ContactId contactId = new ContactId(42);
|
private final ContactId contactId = new ContactId(42);
|
||||||
private final Author remote = getAuthor();
|
private final Author remote = getAuthor();
|
||||||
private final AuthorId local = new AuthorId(getRandomId());
|
private final AuthorId local = new AuthorId(getRandomId());
|
||||||
|
private final LocalAuthor localAuthor = getLocalAuthor();
|
||||||
|
private final String alias = getRandomString(MAX_AUTHOR_NAME_LENGTH);
|
||||||
private final boolean verified = false, active = true;
|
private final boolean verified = false, active = true;
|
||||||
private final Contact contact =
|
private final Contact contact =
|
||||||
new Contact(contactId, remote, local, verified, active);
|
new Contact(contactId, remote, local, alias, verified, active);
|
||||||
|
|
||||||
public ContactManagerImplTest() {
|
public ContactManagerImplTest() {
|
||||||
contactManager = new ContactManagerImpl(db, keyManager);
|
contactManager = new ContactManagerImpl(db, keyManager, identityManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -105,7 +124,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(db).startTransaction(true);
|
oneOf(db).startTransaction(true);
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
oneOf(db).getContactsByAuthorId(txn, remote.getId());
|
oneOf(db).getContactsByAuthorId(txn, remote.getId());
|
||||||
will(returnValue(Collections.emptyList()));
|
will(returnValue(emptyList()));
|
||||||
oneOf(db).endTransaction(txn);
|
oneOf(db).endTransaction(txn);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
@@ -131,7 +150,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
public void testActiveContacts() throws Exception {
|
public void testActiveContacts() throws Exception {
|
||||||
Collection<Contact> activeContacts = Collections.singletonList(contact);
|
Collection<Contact> activeContacts = Collections.singletonList(contact);
|
||||||
Collection<Contact> contacts = new ArrayList<>(activeContacts);
|
Collection<Contact> contacts = new ArrayList<>(activeContacts);
|
||||||
contacts.add(new Contact(new ContactId(3), remote, local, true, false));
|
contacts.add(new Contact(new ContactId(3), remote, local, alias, true,
|
||||||
|
false));
|
||||||
Transaction txn = new Transaction(null, true);
|
Transaction txn = new Transaction(null, true);
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(db).startTransaction(true);
|
oneOf(db).startTransaction(true);
|
||||||
@@ -171,6 +191,23 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
contactManager.setContactActive(txn, contactId, active);
|
contactManager.setContactActive(txn, contactId, active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetContactAlias() throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
context.checking(new DbExpectations() {{
|
||||||
|
oneOf(db).transaction(with(equal(false)), withDbRunnable(txn));
|
||||||
|
oneOf(db).setContactAlias(txn, contactId, alias);
|
||||||
|
}});
|
||||||
|
|
||||||
|
contactManager.setContactAlias(contactId, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testSetContactAliasTooLong() throws Exception {
|
||||||
|
contactManager.setContactAlias(contactId,
|
||||||
|
getRandomString(MAX_AUTHOR_NAME_LENGTH + 1));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testContactExists() throws Exception {
|
public void testContactExists() throws Exception {
|
||||||
Transaction txn = new Transaction(null, true);
|
Transaction txn = new Transaction(null, true);
|
||||||
@@ -186,4 +223,79 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
assertTrue(contactManager.contactExists(remote.getId(), local));
|
assertTrue(contactManager.contactExists(remote.getId(), local));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAuthorStatus() throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, true);
|
||||||
|
Collection<Contact> contacts = singletonList(
|
||||||
|
new Contact(new ContactId(1), remote, localAuthor.getId(),
|
||||||
|
alias, false, true));
|
||||||
|
|
||||||
|
context.checking(new DbExpectations() {{
|
||||||
|
oneOf(db).transactionWithResult(with(equal(true)),
|
||||||
|
withDbCallable(txn));
|
||||||
|
oneOf(identityManager).getLocalAuthor(txn);
|
||||||
|
will(returnValue(localAuthor));
|
||||||
|
oneOf(db).getContactsByAuthorId(txn, remote.getId());
|
||||||
|
will(returnValue(contacts));
|
||||||
|
}});
|
||||||
|
AuthorInfo authorInfo =
|
||||||
|
contactManager.getAuthorInfo(txn, remote.getId());
|
||||||
|
assertEquals(UNVERIFIED, authorInfo.getStatus());
|
||||||
|
assertEquals(alias, contact.getAlias());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAuthorStatusTransaction() throws DbException {
|
||||||
|
Transaction txn = new Transaction(null, true);
|
||||||
|
|
||||||
|
// check unknown author
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(identityManager).getLocalAuthor(txn);
|
||||||
|
will(returnValue(localAuthor));
|
||||||
|
oneOf(db).getContactsByAuthorId(txn, remote.getId());
|
||||||
|
will(returnValue(emptyList()));
|
||||||
|
}});
|
||||||
|
AuthorInfo authorInfo =
|
||||||
|
contactManager.getAuthorInfo(txn, remote.getId());
|
||||||
|
assertEquals(UNKNOWN, authorInfo.getStatus());
|
||||||
|
assertNull(authorInfo.getAlias());
|
||||||
|
|
||||||
|
// check unverified contact
|
||||||
|
Collection<Contact> contacts = singletonList(
|
||||||
|
new Contact(new ContactId(1), remote, localAuthor.getId(),
|
||||||
|
alias, false, true));
|
||||||
|
checkAuthorStatusContext(txn, remote.getId(), contacts);
|
||||||
|
authorInfo = contactManager.getAuthorInfo(txn, remote.getId());
|
||||||
|
assertEquals(UNVERIFIED, authorInfo.getStatus());
|
||||||
|
assertEquals(alias, contact.getAlias());
|
||||||
|
|
||||||
|
// check verified contact
|
||||||
|
contacts = singletonList(new Contact(new ContactId(1), remote,
|
||||||
|
localAuthor.getId(), alias, true, true));
|
||||||
|
checkAuthorStatusContext(txn, remote.getId(), contacts);
|
||||||
|
authorInfo = contactManager.getAuthorInfo(txn, remote.getId());
|
||||||
|
assertEquals(VERIFIED, authorInfo.getStatus());
|
||||||
|
assertEquals(alias, contact.getAlias());
|
||||||
|
|
||||||
|
// check ourselves
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(identityManager).getLocalAuthor(txn);
|
||||||
|
will(returnValue(localAuthor));
|
||||||
|
never(db).getContactsByAuthorId(txn, remote.getId());
|
||||||
|
}});
|
||||||
|
authorInfo = contactManager.getAuthorInfo(txn, localAuthor.getId());
|
||||||
|
assertEquals(OURSELVES, authorInfo.getStatus());
|
||||||
|
assertNull(authorInfo.getAlias());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkAuthorStatusContext(Transaction txn, AuthorId authorId,
|
||||||
|
Collection<Contact> contacts) throws DbException {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(identityManager).getLocalAuthor(txn);
|
||||||
|
will(returnValue(localAuthor));
|
||||||
|
oneOf(db).getContactsByAuthorId(txn, authorId);
|
||||||
|
will(returnValue(contacts));
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ import static org.briarproject.bramble.test.TestUtils.getMessage;
|
|||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
@@ -99,6 +100,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
private final Group group;
|
private final Group group;
|
||||||
private final Author author;
|
private final Author author;
|
||||||
private final LocalAuthor localAuthor;
|
private final LocalAuthor localAuthor;
|
||||||
|
private final String alias;
|
||||||
private final Message message, message1;
|
private final Message message, message1;
|
||||||
private final MessageId messageId, messageId1;
|
private final MessageId messageId, messageId1;
|
||||||
private final Metadata metadata;
|
private final Metadata metadata;
|
||||||
@@ -115,6 +117,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
groupId = group.getId();
|
groupId = group.getId();
|
||||||
author = getAuthor();
|
author = getAuthor();
|
||||||
localAuthor = getLocalAuthor();
|
localAuthor = getLocalAuthor();
|
||||||
|
alias = getRandomString(5);
|
||||||
message = getMessage(groupId);
|
message = getMessage(groupId);
|
||||||
message1 = getMessage(groupId);
|
message1 = getMessage(groupId);
|
||||||
messageId = message.getId();
|
messageId = message.getId();
|
||||||
@@ -124,7 +127,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
transportId = getTransportId();
|
transportId = getTransportId();
|
||||||
maxLatency = Integer.MAX_VALUE;
|
maxLatency = Integer.MAX_VALUE;
|
||||||
contactId = new ContactId(234);
|
contactId = new ContactId(234);
|
||||||
contact = new Contact(contactId, author, localAuthor.getId(),
|
contact = new Contact(contactId, author, localAuthor.getId(), alias,
|
||||||
true, true);
|
true, true);
|
||||||
keySetId = new KeySetId(345);
|
keySetId = new KeySetId(345);
|
||||||
}
|
}
|
||||||
@@ -288,11 +291,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Check whether the contact is in the DB (which it's not)
|
// Check whether the contact is in the DB (which it's not)
|
||||||
exactly(16).of(database).startTransaction();
|
exactly(17).of(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
exactly(16).of(database).containsContact(txn, contactId);
|
exactly(17).of(database).containsContact(txn, contactId);
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
exactly(16).of(database).abortTransaction(txn);
|
exactly(17).of(database).abortTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
shutdown);
|
||||||
@@ -450,6 +453,16 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
db.endTransaction(transaction);
|
db.endTransaction(transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transaction = db.startTransaction(false);
|
||||||
|
try {
|
||||||
|
db.setContactAlias(transaction, contactId, alias);
|
||||||
|
fail();
|
||||||
|
} catch (NoSuchContactException expected) {
|
||||||
|
// Expected
|
||||||
|
} finally {
|
||||||
|
db.endTransaction(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
transaction = db.startTransaction(false);
|
transaction = db.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
db.setGroupVisibility(transaction, contactId, groupId, SHARED);
|
db.setGroupVisibility(transaction, contactId, groupId, SHARED);
|
||||||
|
|||||||
@@ -26,11 +26,10 @@ import org.briarproject.bramble.api.transport.KeySetId;
|
|||||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||||
import org.briarproject.bramble.system.SystemClock;
|
import org.briarproject.bramble.system.SystemClock;
|
||||||
import org.briarproject.bramble.test.ArrayClock;
|
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
|
import org.briarproject.bramble.test.SettableClock;
|
||||||
import org.briarproject.bramble.test.TestDatabaseConfig;
|
import org.briarproject.bramble.test.TestDatabaseConfig;
|
||||||
import org.briarproject.bramble.test.TestMessageFactory;
|
import org.briarproject.bramble.test.TestMessageFactory;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -46,6 +45,7 @@ import java.util.Map.Entry;
|
|||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.emptyMap;
|
import static java.util.Collections.emptyMap;
|
||||||
@@ -53,6 +53,7 @@ import static java.util.Collections.singletonList;
|
|||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
||||||
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
||||||
@@ -61,6 +62,9 @@ import static org.briarproject.bramble.api.sync.ValidationManager.State.DELIVERE
|
|||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State.INVALID;
|
import static org.briarproject.bramble.api.sync.ValidationManager.State.INVALID;
|
||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
|
import static org.briarproject.bramble.api.sync.ValidationManager.State.PENDING;
|
||||||
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
|
import static org.briarproject.bramble.api.sync.ValidationManager.State.UNKNOWN;
|
||||||
|
import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPACE;
|
||||||
|
import static org.briarproject.bramble.db.DatabaseConstants.LAST_COMPACTED_KEY;
|
||||||
|
import static org.briarproject.bramble.db.DatabaseConstants.MAX_COMPACTION_INTERVAL_MS;
|
||||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getClientId;
|
import static org.briarproject.bramble.test.TestUtils.getClientId;
|
||||||
@@ -71,6 +75,7 @@ import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
|||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@@ -1710,6 +1715,39 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetContactAlias() throws Exception {
|
||||||
|
Database<Connection> db = open(false);
|
||||||
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
|
// Add a contact
|
||||||
|
db.addLocalAuthor(txn, localAuthor);
|
||||||
|
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
||||||
|
true, true));
|
||||||
|
|
||||||
|
// The contact should have no alias
|
||||||
|
Contact contact = db.getContact(txn, contactId);
|
||||||
|
assertNull(contact.getAlias());
|
||||||
|
|
||||||
|
// Set a contact alias
|
||||||
|
String alias = getRandomString(MAX_AUTHOR_NAME_LENGTH);
|
||||||
|
db.setContactAlias(txn, contactId, alias);
|
||||||
|
|
||||||
|
// The contact should have an alias
|
||||||
|
contact = db.getContact(txn, contactId);
|
||||||
|
assertEquals(alias, contact.getAlias());
|
||||||
|
|
||||||
|
// Set the contact alias null
|
||||||
|
db.setContactAlias(txn, contactId, null);
|
||||||
|
|
||||||
|
// The contact should have no alias
|
||||||
|
contact = db.getContact(txn, contactId);
|
||||||
|
assertNull(contact.getAlias());
|
||||||
|
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetMessageState() throws Exception {
|
public void testSetMessageState() throws Exception {
|
||||||
Database<Connection> db = open(false);
|
Database<Connection> db = open(false);
|
||||||
@@ -1818,10 +1856,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testMessageRetransmission() throws Exception {
|
public void testMessageRetransmission() throws Exception {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
long steps[] = {now, now, now + MAX_LATENCY * 2 - 1,
|
AtomicLong time = new AtomicLong(now);
|
||||||
now + MAX_LATENCY * 2};
|
|
||||||
Database<Connection> db =
|
Database<Connection> db =
|
||||||
open(false, new TestMessageFactory(), new ArrayClock(steps));
|
open(false, new TestMessageFactory(), new SettableClock(time));
|
||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact, a shared group and a shared message
|
// Add a contact, a shared group and a shared message
|
||||||
@@ -1847,11 +1884,13 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
|
|
||||||
// Time: now + MAX_LATENCY * 2 - 1
|
// Time: now + MAX_LATENCY * 2 - 1
|
||||||
// The message should not yet be sendable
|
// The message should not yet be sendable
|
||||||
|
time.set(now + MAX_LATENCY * 2 - 1);
|
||||||
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY);
|
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY);
|
||||||
assertTrue(ids.isEmpty());
|
assertTrue(ids.isEmpty());
|
||||||
|
|
||||||
// Time: now + MAX_LATENCY * 2
|
// Time: now + MAX_LATENCY * 2
|
||||||
// The message should have expired and should now be sendable
|
// The message should have expired and should now be sendable
|
||||||
|
time.set(now + MAX_LATENCY * 2);
|
||||||
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY);
|
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY);
|
||||||
assertEquals(singletonList(messageId), ids);
|
assertEquals(singletonList(messageId), ids);
|
||||||
|
|
||||||
@@ -1859,13 +1898,12 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFasterMessageRetransmission() throws Exception {
|
public void testFasterMessageRetransmission() throws Exception {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
long steps[] = {now, now, now, now, now + 1};
|
AtomicLong time = new AtomicLong(now);
|
||||||
Database<Connection> db =
|
Database<Connection> db =
|
||||||
open(false, new TestMessageFactory(), new ArrayClock(steps));
|
open(false, new TestMessageFactory(), new SettableClock(time));
|
||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact, a shared group and a shared message
|
// Add a contact, a shared group and a shared message
|
||||||
@@ -1903,6 +1941,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
// Time: now + 1
|
// Time: now + 1
|
||||||
// The message should no longer be sendable via the faster transport,
|
// The message should no longer be sendable via the faster transport,
|
||||||
// as the ETA is now equal
|
// as the ETA is now equal
|
||||||
|
time.set(now + 1);
|
||||||
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE,
|
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE,
|
||||||
MAX_LATENCY - 1);
|
MAX_LATENCY - 1);
|
||||||
assertTrue(ids.isEmpty());
|
assertTrue(ids.isEmpty());
|
||||||
@@ -1911,6 +1950,45 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCompactionTime() throws Exception {
|
||||||
|
MessageFactory messageFactory = new TestMessageFactory();
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
AtomicLong time = new AtomicLong(now);
|
||||||
|
Clock clock = new SettableClock(time);
|
||||||
|
|
||||||
|
// Time: now
|
||||||
|
// The last compaction time should be initialised to the current time
|
||||||
|
Database<Connection> db = open(false, messageFactory, clock);
|
||||||
|
Connection txn = db.startTransaction();
|
||||||
|
Settings s = db.getSettings(txn, DB_SETTINGS_NAMESPACE);
|
||||||
|
assertEquals(now, s.getLong(LAST_COMPACTED_KEY, 0));
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
db.close();
|
||||||
|
|
||||||
|
// Time: now + MAX_COMPACTION_INTERVAL_MS
|
||||||
|
// The DB should not be compacted, so the last compaction time should
|
||||||
|
// not be updated
|
||||||
|
time.set(now + MAX_COMPACTION_INTERVAL_MS);
|
||||||
|
db = open(true, messageFactory, clock);
|
||||||
|
txn = db.startTransaction();
|
||||||
|
s = db.getSettings(txn, DB_SETTINGS_NAMESPACE);
|
||||||
|
assertEquals(now, s.getLong(LAST_COMPACTED_KEY, 0));
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
db.close();
|
||||||
|
|
||||||
|
// Time: now + MAX_COMPACTION_INTERVAL_MS + 1
|
||||||
|
// The DB should be compacted, so the last compaction time should be
|
||||||
|
// updated
|
||||||
|
time.set(now + MAX_COMPACTION_INTERVAL_MS + 1);
|
||||||
|
db = open(true, messageFactory, clock);
|
||||||
|
txn = db.startTransaction();
|
||||||
|
s = db.getSettings(txn, DB_SETTINGS_NAMESPACE);
|
||||||
|
assertEquals(now + MAX_COMPACTION_INTERVAL_MS + 1,
|
||||||
|
s.getLong(LAST_COMPACTED_KEY, 0));
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
private Database<Connection> open(boolean resume) throws Exception {
|
private Database<Connection> open(boolean resume) throws Exception {
|
||||||
return open(resume, new TestMessageFactory(), new SystemClock());
|
return open(resume, new TestMessageFactory(), new SystemClock());
|
||||||
@@ -1921,7 +1999,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Database<Connection> db =
|
Database<Connection> db =
|
||||||
createDatabase(new TestDatabaseConfig(testDir, MAX_SIZE),
|
createDatabase(new TestDatabaseConfig(testDir, MAX_SIZE),
|
||||||
messageFactory, clock);
|
messageFactory, clock);
|
||||||
if (!resume) TestUtils.deleteTestDirectory(testDir);
|
if (!resume) deleteTestDirectory(testDir);
|
||||||
db.open(key, null);
|
db.open(key, null);
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package org.briarproject.bramble.identity;
|
package org.briarproject.bramble.identity;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
@@ -9,9 +7,7 @@ import org.briarproject.bramble.api.crypto.PublicKey;
|
|||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
|
||||||
import org.briarproject.bramble.api.identity.AuthorFactory;
|
import org.briarproject.bramble.api.identity.AuthorFactory;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
@@ -19,15 +15,9 @@ import org.jmock.Expectations;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
|
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.UNKNOWN;
|
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.UNVERIFIED;
|
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.VERIFIED;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
|
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
@@ -107,60 +97,4 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
|
|||||||
assertEquals(localAuthor, identityManager.getLocalAuthor());
|
assertEquals(localAuthor, identityManager.getLocalAuthor());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetAuthorStatus() throws DbException {
|
|
||||||
Author author = getAuthor();
|
|
||||||
AuthorId authorId = author.getId();
|
|
||||||
Collection<Contact> contacts = new ArrayList<>();
|
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(db).startTransaction(true);
|
|
||||||
will(returnValue(txn));
|
|
||||||
oneOf(db).getLocalAuthors(txn);
|
|
||||||
will(returnValue(localAuthors));
|
|
||||||
oneOf(db).getContactsByAuthorId(txn, authorId);
|
|
||||||
will(returnValue(contacts));
|
|
||||||
oneOf(db).endTransaction(txn);
|
|
||||||
}});
|
|
||||||
assertEquals(UNKNOWN, identityManager.getAuthorStatus(authorId));
|
|
||||||
|
|
||||||
// add one unverified contact
|
|
||||||
Contact contact = new Contact(new ContactId(1), author,
|
|
||||||
localAuthor.getId(), false, true);
|
|
||||||
contacts.add(contact);
|
|
||||||
|
|
||||||
checkAuthorStatusContext(authorId, contacts);
|
|
||||||
assertEquals(UNVERIFIED, identityManager.getAuthorStatus(authorId));
|
|
||||||
|
|
||||||
// add one verified contact
|
|
||||||
Contact contact2 = new Contact(new ContactId(1), author,
|
|
||||||
localAuthor.getId(), true, true);
|
|
||||||
contacts.add(contact2);
|
|
||||||
|
|
||||||
checkAuthorStatusContext(authorId, contacts);
|
|
||||||
assertEquals(VERIFIED, identityManager.getAuthorStatus(authorId));
|
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(db).startTransaction(true);
|
|
||||||
will(returnValue(txn));
|
|
||||||
never(db).getLocalAuthors(txn);
|
|
||||||
never(db).getContactsByAuthorId(txn, authorId);
|
|
||||||
oneOf(db).endTransaction(txn);
|
|
||||||
}});
|
|
||||||
assertEquals(OURSELVES,
|
|
||||||
identityManager.getAuthorStatus(localAuthor.getId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkAuthorStatusContext(AuthorId authorId,
|
|
||||||
Collection<Contact> contacts) throws DbException {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(db).startTransaction(true);
|
|
||||||
will(returnValue(txn));
|
|
||||||
never(db).getLocalAuthors(txn);
|
|
||||||
oneOf(db).getContactsByAuthorId(txn, authorId);
|
|
||||||
will(returnValue(contacts));
|
|
||||||
oneOf(db).endTransaction(txn);
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import static org.briarproject.bramble.test.TestUtils.getGroup;
|
|||||||
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
|
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
||||||
@@ -612,7 +613,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
private Contact getContact(boolean active) {
|
private Contact getContact(boolean active) {
|
||||||
ContactId c = new ContactId(nextContactId++);
|
ContactId c = new ContactId(nextContactId++);
|
||||||
return new Contact(c, getAuthor(), localAuthor.getId(),
|
return new Contact(c, getAuthor(), localAuthor.getId(),
|
||||||
true, active);
|
getRandomString(5), true, active);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expectGetLocalProperties(Transaction txn) throws Exception {
|
private void expectGetLocalProperties(Transaction txn) throws Exception {
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DbCallable;
|
||||||
|
import org.briarproject.bramble.api.db.DbRunnable;
|
||||||
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
|
||||||
|
public class DbExpectations extends Expectations {
|
||||||
|
|
||||||
|
protected <E extends Exception> DbRunnable<E> withDbRunnable(
|
||||||
|
Transaction txn) {
|
||||||
|
addParameterMatcher(any(DbRunnable.class));
|
||||||
|
currentBuilder().setAction(new RunTransactionAction(txn));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <R, E extends Exception> DbCallable<R, E> withDbCallable(
|
||||||
|
Transaction txn) {
|
||||||
|
addParameterMatcher(any(DbCallable.class));
|
||||||
|
currentBuilder().setAction(new RunTransactionWithResultAction(txn));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DbRunnable;
|
||||||
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
|
import org.hamcrest.Description;
|
||||||
|
import org.jmock.api.Action;
|
||||||
|
import org.jmock.api.Invocation;
|
||||||
|
|
||||||
|
public class RunTransactionAction implements Action {
|
||||||
|
|
||||||
|
private final Transaction txn;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
public RunTransactionAction(Transaction txn) {
|
||||||
|
this.txn = txn;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(Invocation invocation) throws Throwable {
|
||||||
|
DbRunnable task = (DbRunnable) invocation.getParameter(1);
|
||||||
|
task.run(txn);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void describeTo(Description description) {
|
||||||
|
description.appendText("runs a task inside a database transaction");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DbCallable;
|
||||||
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
|
import org.hamcrest.Description;
|
||||||
|
import org.jmock.api.Action;
|
||||||
|
import org.jmock.api.Invocation;
|
||||||
|
|
||||||
|
public class RunTransactionWithResultAction implements Action {
|
||||||
|
|
||||||
|
private final Transaction txn;
|
||||||
|
|
||||||
|
public RunTransactionWithResultAction(Transaction txn) {
|
||||||
|
this.txn = txn;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(Invocation invocation) throws Throwable {
|
||||||
|
DbCallable task = (DbCallable) invocation.getParameter(1);
|
||||||
|
return task.call(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void describeTo(Description description) {
|
||||||
|
description.appendText("runs a task inside a database transaction");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,7 @@ import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
|||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
public class KeyManagerImplTest extends BrambleMockTestCase {
|
public class KeyManagerImplTest extends BrambleMockTestCase {
|
||||||
@@ -66,10 +67,10 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
|
|||||||
Author remoteAuthor = getAuthor();
|
Author remoteAuthor = getAuthor();
|
||||||
AuthorId localAuthorId = new AuthorId(getRandomId());
|
AuthorId localAuthorId = new AuthorId(getRandomId());
|
||||||
Collection<Contact> contacts = new ArrayList<>();
|
Collection<Contact> contacts = new ArrayList<>();
|
||||||
contacts.add(new Contact(contactId, remoteAuthor, localAuthorId, true,
|
contacts.add(new Contact(contactId, remoteAuthor, localAuthorId,
|
||||||
true));
|
getRandomString(5), true, true));
|
||||||
contacts.add(new Contact(inactiveContactId, remoteAuthor, localAuthorId,
|
contacts.add(new Contact(inactiveContactId, remoteAuthor, localAuthorId,
|
||||||
true, false));
|
getRandomString(5), true, false));
|
||||||
SimplexPluginFactory pluginFactory =
|
SimplexPluginFactory pluginFactory =
|
||||||
context.mock(SimplexPluginFactory.class);
|
context.mock(SimplexPluginFactory.class);
|
||||||
Collection<SimplexPluginFactory> factories =
|
Collection<SimplexPluginFactory> factories =
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import static org.briarproject.bramble.test.TestUtils.getGroup;
|
|||||||
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
|
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.briarproject.bramble.versioning.ClientVersioningConstants.GROUP_KEY_CONTACT_ID;
|
import static org.briarproject.bramble.versioning.ClientVersioningConstants.GROUP_KEY_CONTACT_ID;
|
||||||
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL;
|
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL;
|
||||||
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
|
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
|
||||||
@@ -56,7 +57,8 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
|
|||||||
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
private final Contact contact = new Contact(new ContactId(123),
|
private final Contact contact = new Contact(new ContactId(123),
|
||||||
getAuthor(), getLocalAuthor().getId(), true, true);
|
getAuthor(), getLocalAuthor().getId(), getRandomString(5), true,
|
||||||
|
true);
|
||||||
private final ClientId clientId = getClientId();
|
private final ClientId clientId = getClientId();
|
||||||
private final long now = System.currentTimeMillis();
|
private final long now = System.currentTimeMillis();
|
||||||
private final Transaction txn = new Transaction(null, false);
|
private final Transaction txn = new Transaction(null, false);
|
||||||
|
|||||||
@@ -28,6 +28,6 @@ dependencyVerification {
|
|||||||
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
||||||
'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d',
|
'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d',
|
||||||
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
||||||
'org.whispersystems:curve25519-java:0.4.1:curve25519-java-0.4.1.jar:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e',
|
'org.whispersystems:curve25519-java:0.5.0:curve25519-java-0.5.0.jar:0aadd43cf01d11e9b58f867b3c4f25c3194e8b0623d1953d32dfbfbee009e38d',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ configurations {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation project(path: ':bramble-core', configuration: 'default')
|
implementation project(path: ':bramble-core', configuration: 'default')
|
||||||
implementation fileTree(dir: 'libs', include: '*.jar')
|
implementation fileTree(dir: 'libs', include: '*.jar')
|
||||||
implementation 'net.java.dev.jna:jna:4.4.0'
|
implementation 'net.java.dev.jna:jna:4.5.2'
|
||||||
implementation 'net.java.dev.jna:jna-platform:4.4.0'
|
implementation 'net.java.dev.jna:jna-platform:4.5.2'
|
||||||
tor 'org.briarproject:tor:0.2.9.16@zip'
|
tor 'org.briarproject:tor:0.3.4.8@zip'
|
||||||
|
|
||||||
apt 'com.google.dagger:dagger-compiler:2.0.2'
|
apt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,12 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
|
|||||||
return wrapSocket((StreamConnection) Connector.open(url));
|
return wrapSocket((StreamConnection) Connector.open(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
DuplexTransportConnection discoverAndConnect(String uuid) {
|
||||||
|
return null; // TODO
|
||||||
|
}
|
||||||
|
|
||||||
private String makeUrl(String address, String uuid) {
|
private String makeUrl(String address, String uuid) {
|
||||||
return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
|
return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ dependencyVerification {
|
|||||||
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
||||||
'net.java.dev.jna:jna-platform:4.4.0:jna-platform-4.4.0.jar:e9dda9e884fc107eb6367710540789a12dfa8ad28be9326b22ca6e352e325499',
|
'net.java.dev.jna:jna-platform:4.5.2:jna-platform-4.5.2.jar:f1d00c167d8921c6e23c626ef9f1c3ae0be473c95c68ffa012bc7ae55a87e2d6',
|
||||||
'net.java.dev.jna:jna:4.4.0:jna-4.4.0.jar:c4dadeeecaa90c8847902082aee5eb107fcf59c5d0e63a17fcaf273c0e2d2bd1',
|
'net.java.dev.jna:jna:4.5.2:jna-4.5.2.jar:0c8eb7acf67261656d79005191debaba3b6bf5dd60a43735a245429381dbecff',
|
||||||
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
||||||
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||||
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
||||||
'org.briarproject:tor:0.2.9.16:tor-0.2.9.16.zip:f33091ba414d6a952263981d9059b3d0a9093fd277ae887b3cdd02e8f1936558',
|
'org.briarproject:tor:0.3.4.8:tor-0.3.4.8.zip:bc0158c34002f471a4fe14a6a481816c918eb520a220bb027f64be902beb757f',
|
||||||
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||||
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
||||||
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
|
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ def getStdout = { command, defaultValue ->
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 28
|
compileSdkVersion 28
|
||||||
buildToolsVersion '28.0.2'
|
buildToolsVersion '28.0.3'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 26
|
targetSdkVersion 26
|
||||||
versionCode 10102
|
versionCode 10103
|
||||||
versionName "1.1.2"
|
versionName "1.1.3"
|
||||||
applicationId "org.briarproject.briar.android"
|
applicationId "org.briarproject.briar.android"
|
||||||
buildConfigField "String", "GitHash",
|
buildConfigField "String", "GitHash",
|
||||||
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
|
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
|
||||||
@@ -90,7 +90,7 @@ dependencies {
|
|||||||
implementation project(path: ':bramble-core', configuration: 'default')
|
implementation project(path: ':bramble-core', configuration: 'default')
|
||||||
implementation project(':bramble-android')
|
implementation project(':bramble-android')
|
||||||
|
|
||||||
def supportVersion = '27.1.1'
|
def supportVersion = '28.0.0'
|
||||||
implementation "com.android.support:support-v4:$supportVersion"
|
implementation "com.android.support:support-v4:$supportVersion"
|
||||||
implementation("com.android.support:appcompat-v7:$supportVersion") {
|
implementation("com.android.support:appcompat-v7:$supportVersion") {
|
||||||
exclude module: 'support-v4'
|
exclude module: 'support-v4'
|
||||||
@@ -105,16 +105,17 @@ dependencies {
|
|||||||
implementation "com.android.support:cardview-v7:$supportVersion"
|
implementation "com.android.support:cardview-v7:$supportVersion"
|
||||||
implementation "com.android.support:support-annotations:$supportVersion"
|
implementation "com.android.support:support-annotations:$supportVersion"
|
||||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||||
|
implementation "android.arch.lifecycle:extensions:1.1.1"
|
||||||
|
|
||||||
implementation('ch.acra:acra:4.9.1') {
|
implementation('ch.acra:acra:4.11') {
|
||||||
exclude module: 'support-v4'
|
exclude module: 'support-v4'
|
||||||
exclude module: 'support-annotations'
|
exclude module: 'support-annotations'
|
||||||
}
|
}
|
||||||
implementation 'info.guardianproject.panic:panic:0.5'
|
implementation 'info.guardianproject.panic:panic:0.5'
|
||||||
implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
|
implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
|
||||||
implementation 'de.hdodenhof:circleimageview:2.2.0'
|
implementation 'de.hdodenhof:circleimageview:2.2.0'
|
||||||
implementation 'com.google.zxing:core:3.3.0'
|
implementation 'com.google.zxing:core:3.3.3'
|
||||||
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.8.0'
|
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.12.4'
|
||||||
implementation 'com.vanniktech:emoji-google:0.5.1'
|
implementation 'com.vanniktech:emoji-google:0.5.1'
|
||||||
|
|
||||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
-dontnote com.android.org.conscrypt.SSLParametersImpl
|
-dontnote com.android.org.conscrypt.SSLParametersImpl
|
||||||
-dontnote org.apache.harmony.xnet.provider.jsse.SSLParametersImpl
|
-dontnote org.apache.harmony.xnet.provider.jsse.SSLParametersImpl
|
||||||
-dontnote sun.security.ssl.SSLContextImpl
|
-dontnote sun.security.ssl.SSLContextImpl
|
||||||
|
-dontwarn org.conscrypt.OpenSSLProvider
|
||||||
|
-dontwarn org.conscrypt.Conscrypt
|
||||||
|
|
||||||
# HTML sanitiser
|
# HTML sanitiser
|
||||||
-keep class org.jsoup.safety.Whitelist
|
-keep class org.jsoup.safety.Whitelist
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
@@ -387,7 +388,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name="org.briarproject.briar.android.panic.PanicResponderActivity"
|
android:name="org.briarproject.briar.android.panic.PanicResponderActivity"
|
||||||
android:noHistory="true"
|
android:noHistory="true"
|
||||||
android:theme="@android:style/Theme.NoDisplay">
|
android:theme="@style/Theme.AppCompat.NoActionBar">
|
||||||
<!-- this can never have launchMode singleTask or singleInstance! -->
|
<!-- this can never have launchMode singleTask or singleInstance! -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="info.guardianproject.panic.action.TRIGGER"/>
|
<action android:name="info.guardianproject.panic.action.TRIGGER"/>
|
||||||
@@ -397,12 +398,12 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.briarproject.briar.android.logout.ExitActivity"
|
android:name="org.briarproject.briar.android.logout.ExitActivity"
|
||||||
android:theme="@android:style/Theme.NoDisplay">
|
android:theme="@style/Theme.AppCompat.NoActionBar">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.logout.HideUiActivity"
|
android:name=".android.logout.HideUiActivity"
|
||||||
android:theme="@android:style/Theme.NoDisplay">
|
android:theme="@style/Theme.AppCompat.NoActionBar">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.briarproject.bramble.api.system.LocationUtils;
|
|||||||
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
|
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
|
||||||
import org.briarproject.briar.BriarCoreEagerSingletons;
|
import org.briarproject.briar.BriarCoreEagerSingletons;
|
||||||
import org.briarproject.briar.BriarCoreModule;
|
import org.briarproject.briar.BriarCoreModule;
|
||||||
|
import org.briarproject.briar.android.contact.ConversationViewModel;
|
||||||
import org.briarproject.briar.android.login.SignInReminderReceiver;
|
import org.briarproject.briar.android.login.SignInReminderReceiver;
|
||||||
import org.briarproject.briar.android.reporting.BriarReportSender;
|
import org.briarproject.briar.android.reporting.BriarReportSender;
|
||||||
import org.briarproject.briar.android.view.TextInputView;
|
import org.briarproject.briar.android.view.TextInputView;
|
||||||
@@ -164,6 +165,8 @@ public interface AndroidComponent
|
|||||||
|
|
||||||
void inject(TextInputView textInputView);
|
void inject(TextInputView textInputView);
|
||||||
|
|
||||||
|
void inject(ConversationViewModel conversationViewModel);
|
||||||
|
|
||||||
// Eager singleton load
|
// Eager singleton load
|
||||||
void inject(AppModule.EagerSingletons init);
|
void inject(AppModule.EagerSingletons init);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ public class AppModule {
|
|||||||
Context appContext = app.getApplicationContext();
|
Context appContext = app.getApplicationContext();
|
||||||
DuplexPluginFactory bluetooth =
|
DuplexPluginFactory bluetooth =
|
||||||
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
|
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
|
||||||
appContext, random, eventBus, backoffFactory);
|
appContext, random, eventBus, clock, backoffFactory);
|
||||||
DuplexPluginFactory tor = new AndroidTorPluginFactory(ioExecutor,
|
DuplexPluginFactory tor = new AndroidTorPluginFactory(ioExecutor,
|
||||||
scheduler, appContext, networkManager, locationUtils, eventBus,
|
scheduler, appContext, networkManager, locationUtils, eventBus,
|
||||||
torSocketFactory, backoffFactory, resourceProvider,
|
torSocketFactory, backoffFactory, resourceProvider,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import org.briarproject.briar.android.controller.ActivityLifecycleController;
|
|||||||
import org.briarproject.briar.android.forum.ForumModule;
|
import org.briarproject.briar.android.forum.ForumModule;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
import org.briarproject.briar.android.fragment.ScreenFilterDialogFragment;
|
import org.briarproject.briar.android.fragment.ScreenFilterDialogFragment;
|
||||||
|
import org.briarproject.briar.android.reporting.DevReportActivity;
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
import org.briarproject.briar.android.util.UiUtils;
|
||||||
import org.briarproject.briar.android.widget.TapSafeFrameLayout;
|
import org.briarproject.briar.android.widget.TapSafeFrameLayout;
|
||||||
import org.briarproject.briar.android.widget.TapSafeFrameLayout.OnTapFilteredListener;
|
import org.briarproject.briar.android.widget.TapSafeFrameLayout.OnTapFilteredListener;
|
||||||
@@ -33,18 +34,28 @@ import org.briarproject.briar.api.android.ScreenFilterMonitor.AppDetails;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.arch.lifecycle.Lifecycle.State.STARTED;
|
||||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
||||||
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
|
import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warning: Some activities don't extend {@link BaseActivity}.
|
||||||
|
* E.g. {@link DevReportActivity}
|
||||||
|
*/
|
||||||
public abstract class BaseActivity extends AppCompatActivity
|
public abstract class BaseActivity extends AppCompatActivity
|
||||||
implements DestroyableContext, OnTapFilteredListener {
|
implements DestroyableContext, OnTapFilteredListener {
|
||||||
|
|
||||||
|
private final static Logger LOG = getLogger(BaseActivity.class.getName());
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected ScreenFilterMonitor screenFilterMonitor;
|
protected ScreenFilterMonitor screenFilterMonitor;
|
||||||
|
|
||||||
@@ -113,6 +124,8 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
@Override
|
@Override
|
||||||
protected void onStart() {
|
protected void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Starting " + this.getClass().getSimpleName());
|
||||||
for (ActivityLifecycleController alc : lifecycleControllers) {
|
for (ActivityLifecycleController alc : lifecycleControllers) {
|
||||||
alc.onActivityStart();
|
alc.onActivityStart();
|
||||||
}
|
}
|
||||||
@@ -131,6 +144,8 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Stopping " + this.getClass().getSimpleName());
|
||||||
for (ActivityLifecycleController alc : lifecycleControllers) {
|
for (ActivityLifecycleController alc : lifecycleControllers) {
|
||||||
alc.onActivityStop();
|
alc.onActivityStop();
|
||||||
}
|
}
|
||||||
@@ -143,6 +158,7 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void showNextFragment(BaseFragment f) {
|
public void showNextFragment(BaseFragment f) {
|
||||||
|
if (!getLifecycle().getCurrentState().isAtLeast(STARTED)) return;
|
||||||
getSupportFragmentManager().beginTransaction()
|
getSupportFragmentManager().beginTransaction()
|
||||||
.setCustomAnimations(R.anim.step_next_in,
|
.setCustomAnimations(R.anim.step_next_in,
|
||||||
R.anim.step_previous_out, R.anim.step_previous_in,
|
R.anim.step_previous_out, R.anim.step_previous_in,
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ public interface RequestCodes {
|
|||||||
int REQUEST_WRITE_BLOG_POST = 5;
|
int REQUEST_WRITE_BLOG_POST = 5;
|
||||||
int REQUEST_SHARE_BLOG = 6;
|
int REQUEST_SHARE_BLOG = 6;
|
||||||
int REQUEST_RINGTONE = 7;
|
int REQUEST_RINGTONE = 7;
|
||||||
int REQUEST_PERMISSION_CAMERA = 8;
|
int REQUEST_PERMISSION_CAMERA_LOCATION = 8;
|
||||||
int REQUEST_DOZE_WHITELISTING = 9;
|
int REQUEST_DOZE_WHITELISTING = 9;
|
||||||
int REQUEST_ENABLE_BLUETOOTH = 10;
|
int REQUEST_BLUETOOTH_DISCOVERABLE = 10;
|
||||||
int REQUEST_UNLOCK = 11;
|
int REQUEST_UNLOCK = 11;
|
||||||
int REQUEST_KEYGUARD_UNLOCK = 12;
|
int REQUEST_KEYGUARD_UNLOCK = 12;
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ abstract class BaseControllerImpl extends DbControllerImpl
|
|||||||
protected final IdentityManager identityManager;
|
protected final IdentityManager identityManager;
|
||||||
protected final BlogManager blogManager;
|
protected final BlogManager blogManager;
|
||||||
|
|
||||||
private final Map<MessageId, String> bodyCache = new ConcurrentHashMap<>();
|
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>();
|
||||||
private final Map<MessageId, BlogPostHeader> headerCache =
|
private final Map<MessageId, BlogPostHeader> headerCache =
|
||||||
new ConcurrentHashMap<>();
|
new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@@ -129,17 +129,17 @@ abstract class BaseControllerImpl extends DbControllerImpl
|
|||||||
public void loadBlogPost(BlogPostHeader header,
|
public void loadBlogPost(BlogPostHeader header,
|
||||||
ResultExceptionHandler<BlogPostItem, DbException> handler) {
|
ResultExceptionHandler<BlogPostItem, DbException> handler) {
|
||||||
|
|
||||||
String body = bodyCache.get(header.getId());
|
String text = textCache.get(header.getId());
|
||||||
if (body != null) {
|
if (text != null) {
|
||||||
LOG.info("Loaded body from cache");
|
LOG.info("Loaded text from cache");
|
||||||
handler.onResult(new BlogPostItem(header, body));
|
handler.onResult(new BlogPostItem(header, text));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
long start = now();
|
long start = now();
|
||||||
BlogPostItem item = getItem(header);
|
BlogPostItem item = getItem(header);
|
||||||
logDuration(LOG, "Loading body", start);
|
logDuration(LOG, "Loading text", start);
|
||||||
handler.onResult(item);
|
handler.onResult(item);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
@@ -200,28 +200,28 @@ abstract class BaseControllerImpl extends DbControllerImpl
|
|||||||
|
|
||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
private BlogPostItem getItem(BlogPostHeader h) throws DbException {
|
private BlogPostItem getItem(BlogPostHeader h) throws DbException {
|
||||||
String body;
|
String text;
|
||||||
if (h instanceof BlogCommentHeader) {
|
if (h instanceof BlogCommentHeader) {
|
||||||
BlogCommentHeader c = (BlogCommentHeader) h;
|
BlogCommentHeader c = (BlogCommentHeader) h;
|
||||||
BlogCommentItem item = new BlogCommentItem(c);
|
BlogCommentItem item = new BlogCommentItem(c);
|
||||||
body = getPostBody(item.getPostHeader().getId());
|
text = getPostText(item.getPostHeader().getId());
|
||||||
item.setBody(body);
|
item.setText(text);
|
||||||
return item;
|
return item;
|
||||||
} else {
|
} else {
|
||||||
body = getPostBody(h.getId());
|
text = getPostText(h.getId());
|
||||||
return new BlogPostItem(h, body);
|
return new BlogPostItem(h, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
private String getPostBody(MessageId m) throws DbException {
|
private String getPostText(MessageId m) throws DbException {
|
||||||
String body = bodyCache.get(m);
|
String text = textCache.get(m);
|
||||||
if (body == null) {
|
if (text == null) {
|
||||||
body = HtmlUtils.clean(blogManager.getPostBody(m), ARTICLE);
|
text = HtmlUtils.clean(blogManager.getPostText(m), ARTICLE);
|
||||||
bodyCache.put(m, body);
|
textCache.put(m, text);
|
||||||
}
|
}
|
||||||
//noinspection ConstantConditions
|
//noinspection ConstantConditions
|
||||||
return body;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ class BlogCommentItem extends BlogPostItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBody(String body) {
|
public void setText(String text) {
|
||||||
this.body = body;
|
this.text = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package org.briarproject.briar.android.blog;
|
|||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.Author.Status;
|
import org.briarproject.bramble.api.identity.AuthorInfo;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.api.blog.BlogPostHeader;
|
import org.briarproject.briar.api.blog.BlogPostHeader;
|
||||||
@@ -15,12 +15,13 @@ import javax.annotation.concurrent.NotThreadSafe;
|
|||||||
public class BlogPostItem implements Comparable<BlogPostItem> {
|
public class BlogPostItem implements Comparable<BlogPostItem> {
|
||||||
|
|
||||||
private final BlogPostHeader header;
|
private final BlogPostHeader header;
|
||||||
protected String body;
|
@Nullable
|
||||||
|
protected String text;
|
||||||
private boolean read;
|
private boolean read;
|
||||||
|
|
||||||
BlogPostItem(BlogPostHeader header, @Nullable String body) {
|
BlogPostItem(BlogPostHeader header, @Nullable String text) {
|
||||||
this.header = header;
|
this.header = header;
|
||||||
this.body = body;
|
this.text = text;
|
||||||
this.read = header.isRead();
|
this.read = header.isRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,12 +41,13 @@ public class BlogPostItem implements Comparable<BlogPostItem> {
|
|||||||
return header.getAuthor();
|
return header.getAuthor();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status getAuthorStatus() {
|
AuthorInfo getAuthorInfo() {
|
||||||
return header.getAuthorStatus();
|
return header.getAuthorInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getBody() {
|
@Nullable
|
||||||
return body;
|
public String getText() {
|
||||||
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRssFeed() {
|
public boolean isRssFeed() {
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.view.AuthorView;
|
import org.briarproject.briar.android.view.AuthorView;
|
||||||
@@ -41,7 +40,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
|||||||
private final AuthorView reblogger;
|
private final AuthorView reblogger;
|
||||||
private final AuthorView author;
|
private final AuthorView author;
|
||||||
private final ImageButton reblogButton;
|
private final ImageButton reblogButton;
|
||||||
private final TextView body;
|
private final TextView text;
|
||||||
private final ViewGroup commentContainer;
|
private final ViewGroup commentContainer;
|
||||||
private final boolean fullText;
|
private final boolean fullText;
|
||||||
|
|
||||||
@@ -63,7 +62,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
|||||||
reblogger = v.findViewById(R.id.rebloggerView);
|
reblogger = v.findViewById(R.id.rebloggerView);
|
||||||
author = v.findViewById(R.id.authorView);
|
author = v.findViewById(R.id.authorView);
|
||||||
reblogButton = v.findViewById(R.id.commentView);
|
reblogButton = v.findViewById(R.id.commentView);
|
||||||
body = v.findViewById(R.id.bodyView);
|
text = v.findViewById(R.id.textView);
|
||||||
commentContainer = v.findViewById(R.id.commentContainer);
|
commentContainer = v.findViewById(R.id.commentContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,9 +97,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
|||||||
|
|
||||||
// author and date
|
// author and date
|
||||||
BlogPostHeader post = item.getPostHeader();
|
BlogPostHeader post = item.getPostHeader();
|
||||||
Author a = post.getAuthor();
|
author.setAuthor(post.getAuthor(), post.getAuthorInfo());
|
||||||
author.setAuthor(a);
|
|
||||||
author.setAuthorStatus(post.getAuthorStatus());
|
|
||||||
author.setDate(post.getTimestamp());
|
author.setDate(post.getTimestamp());
|
||||||
author.setPersona(
|
author.setPersona(
|
||||||
item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL);
|
item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL);
|
||||||
@@ -111,17 +108,17 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
|||||||
author.setAuthorNotClickable();
|
author.setAuthorNotClickable();
|
||||||
}
|
}
|
||||||
|
|
||||||
// post body
|
// post text
|
||||||
Spanned bodyText = getSpanned(item.getBody());
|
Spanned postText = getSpanned(item.getText());
|
||||||
if (fullText) {
|
if (fullText) {
|
||||||
body.setText(bodyText);
|
text.setText(postText);
|
||||||
body.setTextIsSelectable(true);
|
text.setTextIsSelectable(true);
|
||||||
makeLinksClickable(body, fragmentManager);
|
makeLinksClickable(text, fragmentManager);
|
||||||
} else {
|
} else {
|
||||||
body.setTextIsSelectable(false);
|
text.setTextIsSelectable(false);
|
||||||
if (bodyText.length() > TEASER_LENGTH)
|
if (postText.length() > TEASER_LENGTH)
|
||||||
bodyText = getTeaser(ctx, bodyText);
|
postText = getTeaser(ctx, postText);
|
||||||
body.setText(bodyText);
|
text.setText(postText);
|
||||||
}
|
}
|
||||||
|
|
||||||
// reblog button
|
// reblog button
|
||||||
@@ -143,8 +140,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
|||||||
|
|
||||||
private void onBindComment(BlogCommentItem item) {
|
private void onBindComment(BlogCommentItem item) {
|
||||||
// reblogger
|
// reblogger
|
||||||
reblogger.setAuthor(item.getAuthor());
|
reblogger.setAuthor(item.getAuthor(), item.getAuthorInfo());
|
||||||
reblogger.setAuthorStatus(item.getAuthorStatus());
|
|
||||||
reblogger.setDate(item.getTimestamp());
|
reblogger.setDate(item.getTimestamp());
|
||||||
if (!fullText) {
|
if (!fullText) {
|
||||||
reblogger.setAuthorClickable(v -> listener.onAuthorClick(item));
|
reblogger.setAuthorClickable(v -> listener.onAuthorClick(item));
|
||||||
@@ -163,15 +159,14 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
|||||||
commentContainer, false);
|
commentContainer, false);
|
||||||
|
|
||||||
AuthorView author = v.findViewById(R.id.authorView);
|
AuthorView author = v.findViewById(R.id.authorView);
|
||||||
TextView body = v.findViewById(R.id.bodyView);
|
TextView text = v.findViewById(R.id.textView);
|
||||||
|
|
||||||
author.setAuthor(c.getAuthor());
|
author.setAuthor(c.getAuthor(), c.getAuthorInfo());
|
||||||
author.setAuthorStatus(c.getAuthorStatus());
|
|
||||||
author.setDate(c.getTimestamp());
|
author.setDate(c.getTimestamp());
|
||||||
// TODO make author clickable #624
|
// TODO make author clickable #624
|
||||||
|
|
||||||
body.setText(c.getComment());
|
text.setText(c.getComment());
|
||||||
if (fullText) body.setTextIsSelectable(true);
|
if (fullText) text.setTextIsSelectable(true);
|
||||||
|
|
||||||
commentContainer.addView(v);
|
commentContainer.addView(v);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import static android.view.View.GONE;
|
|||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
|
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_TEXT_LENGTH;
|
||||||
|
|
||||||
public class WriteBlogPostActivity extends BriarActivity
|
public class WriteBlogPostActivity extends BriarActivity
|
||||||
implements OnEditorActionListener, TextInputListener {
|
implements OnEditorActionListener, TextInputListener {
|
||||||
@@ -70,7 +70,7 @@ public class WriteBlogPostActivity extends BriarActivity
|
|||||||
|
|
||||||
setContentView(R.layout.activity_write_blog_post);
|
setContentView(R.layout.activity_write_blog_post);
|
||||||
|
|
||||||
input = findViewById(R.id.bodyInput);
|
input = findViewById(R.id.textInput);
|
||||||
input.setSendButtonEnabled(false);
|
input.setSendButtonEnabled(false);
|
||||||
input.addTextChangedListener(new TextWatcher() {
|
input.addTextChangedListener(new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
@@ -132,23 +132,23 @@ public class WriteBlogPostActivity extends BriarActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSendClick(String body) {
|
public void onSendClick(String text) {
|
||||||
// hide publish button, show progress bar
|
// hide publish button, show progress bar
|
||||||
input.hideSoftKeyboard();
|
input.hideSoftKeyboard();
|
||||||
input.setVisibility(GONE);
|
input.setVisibility(GONE);
|
||||||
progressBar.setVisibility(VISIBLE);
|
progressBar.setVisibility(VISIBLE);
|
||||||
|
|
||||||
body = StringUtils.truncateUtf8(body, MAX_BLOG_POST_BODY_LENGTH);
|
text = StringUtils.truncateUtf8(text, MAX_BLOG_POST_TEXT_LENGTH);
|
||||||
storePost(body);
|
storePost(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storePost(String body) {
|
private void storePost(String text) {
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
long timestamp = System.currentTimeMillis();
|
long timestamp = System.currentTimeMillis();
|
||||||
try {
|
try {
|
||||||
LocalAuthor author = identityManager.getLocalAuthor();
|
LocalAuthor author = identityManager.getLocalAuthor();
|
||||||
BlogPost p = blogPostFactory
|
BlogPost p = blogPostFactory
|
||||||
.createBlogPost(groupId, timestamp, null, author, body);
|
.createBlogPost(groupId, timestamp, null, author, text);
|
||||||
blogManager.addLocalPost(p);
|
blogManager.addLocalPost(p);
|
||||||
postPublished();
|
postPublished();
|
||||||
} catch (DbException | GeneralSecurityException
|
} catch (DbException | GeneralSecurityException
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package org.briarproject.briar.android.contact;
|
||||||
|
|
||||||
|
import android.arch.lifecycle.ViewModelProviders;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v7.app.AppCompatDialogFragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
|
||||||
|
public class AliasDialogFragment extends AppCompatDialogFragment {
|
||||||
|
|
||||||
|
final static String TAG = AliasDialogFragment.class.getName();
|
||||||
|
|
||||||
|
private ConversationViewModel viewModel;
|
||||||
|
private ContactId contactId;
|
||||||
|
private EditText aliasEditText;
|
||||||
|
|
||||||
|
public static AliasDialogFragment newInstance(ContactId id) {
|
||||||
|
AliasDialogFragment f = new AliasDialogFragment();
|
||||||
|
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putInt("contactId", id.getInt());
|
||||||
|
f.setArguments(args);
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
if (getArguments() == null) throw new IllegalArgumentException();
|
||||||
|
int contactIdInt = getArguments().getInt("contactId", -1);
|
||||||
|
if (contactIdInt == -1) throw new IllegalArgumentException();
|
||||||
|
contactId = new ContactId(contactIdInt);
|
||||||
|
|
||||||
|
setStyle(STYLE_NO_TITLE, R.style.BriarDialogTheme);
|
||||||
|
|
||||||
|
viewModel =
|
||||||
|
ViewModelProviders.of(getActivity()).get(ConversationViewModel.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
|
ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
|
||||||
|
View v = inflater.inflate(R.layout.fragment_alias_dialog, container,
|
||||||
|
false);
|
||||||
|
|
||||||
|
aliasEditText = v.findViewById(R.id.aliasEditText);
|
||||||
|
Contact contact = viewModel.getContact().getValue();
|
||||||
|
String alias = contact == null ? null : contact.getAlias();
|
||||||
|
aliasEditText.setText(alias);
|
||||||
|
if (alias != null) aliasEditText.setSelection(alias.length());
|
||||||
|
|
||||||
|
Button setButton = v.findViewById(R.id.setButton);
|
||||||
|
setButton.setOnClickListener(v1 -> {
|
||||||
|
viewModel.setContactAlias(contactId,
|
||||||
|
aliasEditText.getText().toString());
|
||||||
|
getDialog().dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
Button cancelButton = v.findViewById(R.id.cancelButton);
|
||||||
|
cancelButton.setOnClickListener(v1 -> getDialog().cancel());
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.briar.android.contact;
|
package org.briarproject.briar.android.contact;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
@@ -9,6 +10,7 @@ import org.briarproject.briar.android.util.BriarAdapter;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static android.support.v7.util.SortedList.INVALID_POSITION;
|
import static android.support.v7.util.SortedList.INVALID_POSITION;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
|
||||||
|
|
||||||
public abstract class BaseContactListAdapter<I extends ContactItem, VH extends ContactItemViewHolder<I>>
|
public abstract class BaseContactListAdapter<I extends ContactItem, VH extends ContactItemViewHolder<I>>
|
||||||
extends BriarAdapter<I, VH> {
|
extends BriarAdapter<I, VH> {
|
||||||
@@ -23,15 +25,15 @@ public abstract class BaseContactListAdapter<I extends ContactItem, VH extends C
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(VH ui, int position) {
|
public void onBindViewHolder(@NonNull VH ui, int position) {
|
||||||
I item = items.get(position);
|
I item = items.get(position);
|
||||||
ui.bind(item, listener);
|
ui.bind(item, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compare(I c1, I c2) {
|
public int compare(I c1, I c2) {
|
||||||
return c1.getContact().getAuthor().getName()
|
return getContactDisplayName(c1.getContact())
|
||||||
.compareTo(c2.getContact().getAuthor().getName());
|
.compareTo(getContactDisplayName(c2.getContact()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import javax.annotation.Nullable;
|
|||||||
|
|
||||||
import im.delight.android.identicons.IdenticonDrawable;
|
import im.delight.android.identicons.IdenticonDrawable;
|
||||||
|
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class ContactItemViewHolder<I extends ContactItem>
|
public class ContactItemViewHolder<I extends ContactItem>
|
||||||
@@ -41,8 +43,7 @@ public class ContactItemViewHolder<I extends ContactItem>
|
|||||||
Author author = item.getContact().getAuthor();
|
Author author = item.getContact().getAuthor();
|
||||||
avatar.setImageDrawable(
|
avatar.setImageDrawable(
|
||||||
new IdenticonDrawable(author.getId().getBytes()));
|
new IdenticonDrawable(author.getId().getBytes()));
|
||||||
String contactName = author.getName();
|
name.setText(getContactDisplayName(item.getContact()));
|
||||||
name.setText(contactName);
|
|
||||||
|
|
||||||
if (bulb != null) {
|
if (bulb != null) {
|
||||||
// online/offline
|
// online/offline
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import org.briarproject.briar.R;
|
|||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
import org.briarproject.briar.android.util.UiUtils;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static android.support.v4.view.ViewCompat.setTransitionName;
|
import static android.support.v4.view.ViewCompat.setTransitionName;
|
||||||
@@ -36,7 +38,7 @@ class ContactListItemViewHolder extends ContactItemViewHolder<ContactListItem> {
|
|||||||
// unread count
|
// unread count
|
||||||
int unreadCount = item.getUnreadCount();
|
int unreadCount = item.getUnreadCount();
|
||||||
if (unreadCount > 0) {
|
if (unreadCount > 0) {
|
||||||
unread.setText(String.valueOf(unreadCount));
|
unread.setText(String.format(Locale.getDefault(), "%d", unreadCount));
|
||||||
unread.setVisibility(View.VISIBLE);
|
unread.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
unread.setVisibility(View.INVISIBLE);
|
unread.setVisibility(View.INVISIBLE);
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package org.briarproject.briar.android.contact;
|
package org.briarproject.briar.android.contact;
|
||||||
|
|
||||||
import android.arch.lifecycle.MutableLiveData;
|
import android.arch.lifecycle.LiveData;
|
||||||
import android.arch.lifecycle.Observer;
|
import android.arch.lifecycle.Observer;
|
||||||
|
import android.arch.lifecycle.ViewModelProviders;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -23,7 +24,6 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||||
@@ -34,7 +34,6 @@ import org.briarproject.bramble.api.db.NoSuchContactException;
|
|||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
@@ -53,7 +52,7 @@ import org.briarproject.briar.android.activity.ActivityComponent;
|
|||||||
import org.briarproject.briar.android.activity.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
import org.briarproject.briar.android.blog.BlogActivity;
|
import org.briarproject.briar.android.blog.BlogActivity;
|
||||||
import org.briarproject.briar.android.contact.ConversationAdapter.ConversationListener;
|
import org.briarproject.briar.android.contact.ConversationAdapter.ConversationListener;
|
||||||
import org.briarproject.briar.android.contact.ConversationVisitor.BodyCache;
|
import org.briarproject.briar.android.contact.ConversationVisitor.TextCache;
|
||||||
import org.briarproject.briar.android.forum.ForumActivity;
|
import org.briarproject.briar.android.forum.ForumActivity;
|
||||||
import org.briarproject.briar.android.introduction.IntroductionActivity;
|
import org.briarproject.briar.android.introduction.IntroductionActivity;
|
||||||
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
|
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
|
||||||
@@ -105,7 +104,7 @@ import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_INTRO
|
|||||||
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
|
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName;
|
import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName;
|
||||||
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_BODY_LENGTH;
|
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH;
|
||||||
import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.STATE_DISMISSED;
|
import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.STATE_DISMISSED;
|
||||||
import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.STATE_FINISHED;
|
import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.STATE_FINISHED;
|
||||||
|
|
||||||
@@ -113,7 +112,7 @@ import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.S
|
|||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class ConversationActivity extends BriarActivity
|
public class ConversationActivity extends BriarActivity
|
||||||
implements EventListener, ConversationListener, TextInputListener,
|
implements EventListener, ConversationListener, TextInputListener,
|
||||||
BodyCache {
|
TextCache {
|
||||||
|
|
||||||
public static final String CONTACT_ID = "briar.CONTACT_ID";
|
public static final String CONTACT_ID = "briar.CONTACT_ID";
|
||||||
|
|
||||||
@@ -130,9 +129,9 @@ public class ConversationActivity extends BriarActivity
|
|||||||
@CryptoExecutor
|
@CryptoExecutor
|
||||||
Executor cryptoExecutor;
|
Executor cryptoExecutor;
|
||||||
|
|
||||||
private final Map<MessageId, String> bodyCache = new ConcurrentHashMap<>();
|
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>();
|
||||||
private final MutableLiveData<String> contactName = new MutableLiveData<>();
|
|
||||||
|
|
||||||
|
private ConversationViewModel viewModel;
|
||||||
private ConversationVisitor visitor;
|
private ConversationVisitor visitor;
|
||||||
private ConversationAdapter adapter;
|
private ConversationAdapter adapter;
|
||||||
private Toolbar toolbar;
|
private Toolbar toolbar;
|
||||||
@@ -166,8 +165,6 @@ public class ConversationActivity extends BriarActivity
|
|||||||
|
|
||||||
private volatile ContactId contactId;
|
private volatile ContactId contactId;
|
||||||
@Nullable
|
@Nullable
|
||||||
private volatile AuthorId contactAuthorId;
|
|
||||||
@Nullable
|
|
||||||
private volatile GroupId messagingGroupId;
|
private volatile GroupId messagingGroupId;
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
@@ -176,6 +173,9 @@ public class ConversationActivity extends BriarActivity
|
|||||||
setSceneTransitionAnimation();
|
setSceneTransitionAnimation();
|
||||||
super.onCreate(state);
|
super.onCreate(state);
|
||||||
|
|
||||||
|
viewModel =
|
||||||
|
ViewModelProviders.of(this).get(ConversationViewModel.class);
|
||||||
|
|
||||||
Intent i = getIntent();
|
Intent i = getIntent();
|
||||||
int id = i.getIntExtra(CONTACT_ID, -1);
|
int id = i.getIntExtra(CONTACT_ID, -1);
|
||||||
if (id == -1) throw new IllegalStateException();
|
if (id == -1) throw new IllegalStateException();
|
||||||
@@ -185,16 +185,29 @@ public class ConversationActivity extends BriarActivity
|
|||||||
|
|
||||||
// Custom Toolbar
|
// Custom Toolbar
|
||||||
toolbar = setUpCustomToolbar(true);
|
toolbar = setUpCustomToolbar(true);
|
||||||
if (toolbar != null) {
|
toolbarAvatar = toolbar.findViewById(R.id.contactAvatar);
|
||||||
toolbarAvatar = toolbar.findViewById(R.id.contactAvatar);
|
toolbarStatus = toolbar.findViewById(R.id.contactStatus);
|
||||||
toolbarStatus = toolbar.findViewById(R.id.contactStatus);
|
toolbarTitle = toolbar.findViewById(R.id.contactName);
|
||||||
toolbarTitle = toolbar.findViewById(R.id.contactName);
|
|
||||||
}
|
viewModel.getContactAuthorId().observe(this, authorId -> {
|
||||||
|
toolbarAvatar.setImageDrawable(
|
||||||
|
new IdenticonDrawable(authorId.getBytes()));
|
||||||
|
// we only need this once
|
||||||
|
viewModel.getContactAuthorId().removeObservers(this);
|
||||||
|
});
|
||||||
|
viewModel.getContactDisplayName().observe(this, contactName -> {
|
||||||
|
toolbarTitle.setText(contactName);
|
||||||
|
});
|
||||||
|
viewModel.isContactDeleted().observe(this, deleted -> {
|
||||||
|
if (deleted != null && deleted) finish();
|
||||||
|
});
|
||||||
|
viewModel.loadContact(contactId);
|
||||||
|
|
||||||
setTransitionName(toolbarAvatar, getAvatarTransitionName(contactId));
|
setTransitionName(toolbarAvatar, getAvatarTransitionName(contactId));
|
||||||
setTransitionName(toolbarStatus, getBulbTransitionName(contactId));
|
setTransitionName(toolbarStatus, getBulbTransitionName(contactId));
|
||||||
|
|
||||||
visitor = new ConversationVisitor(this, this, contactName);
|
visitor = new ConversationVisitor(this, this,
|
||||||
|
viewModel.getContactDisplayName());
|
||||||
adapter = new ConversationAdapter(this, this);
|
adapter = new ConversationAdapter(this, this);
|
||||||
list = findViewById(R.id.conversationView);
|
list = findViewById(R.id.conversationView);
|
||||||
list.setLayoutManager(new LinearLayoutManager(this));
|
list.setLayoutManager(new LinearLayoutManager(this));
|
||||||
@@ -229,7 +242,19 @@ public class ConversationActivity extends BriarActivity
|
|||||||
notificationManager.blockContactNotification(contactId);
|
notificationManager.blockContactNotification(contactId);
|
||||||
notificationManager.clearContactNotification(contactId);
|
notificationManager.clearContactNotification(contactId);
|
||||||
displayContactOnlineStatus();
|
displayContactOnlineStatus();
|
||||||
loadContactDetailsAndMessages();
|
LiveData<String> contactName = viewModel.getContactDisplayName();
|
||||||
|
if (contactName.getValue() == null) {
|
||||||
|
// wait for contact name to be initialized
|
||||||
|
contactName.observe(this, new Observer<String>() {
|
||||||
|
@Override
|
||||||
|
public void onChanged(@Nullable String cName) {
|
||||||
|
if (cName != null) {
|
||||||
|
loadMessages();
|
||||||
|
contactName.removeObserver(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else loadMessages();
|
||||||
list.startPeriodicUpdate();
|
list.startPeriodicUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,6 +291,10 @@ public class ConversationActivity extends BriarActivity
|
|||||||
intent.putExtra(CONTACT_ID, contactId.getInt());
|
intent.putExtra(CONTACT_ID, contactId.getInt());
|
||||||
startActivityForResult(intent, REQUEST_INTRODUCTION);
|
startActivityForResult(intent, REQUEST_INTRODUCTION);
|
||||||
return true;
|
return true;
|
||||||
|
case R.id.action_set_alias:
|
||||||
|
AliasDialogFragment.newInstance(contactId).show(
|
||||||
|
getSupportFragmentManager(), AliasDialogFragment.TAG);
|
||||||
|
return true;
|
||||||
case R.id.action_social_remove_person:
|
case R.id.action_social_remove_person:
|
||||||
askToRemoveContact();
|
askToRemoveContact();
|
||||||
return true;
|
return true;
|
||||||
@@ -274,36 +303,6 @@ public class ConversationActivity extends BriarActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadContactDetailsAndMessages() {
|
|
||||||
runOnDbThread(() -> {
|
|
||||||
try {
|
|
||||||
long start = now();
|
|
||||||
if (contactAuthorId == null) {
|
|
||||||
Contact contact = contactManager.getContact(contactId);
|
|
||||||
contactName.postValue(contact.getAuthor().getName());
|
|
||||||
contactAuthorId = contact.getAuthor().getId();
|
|
||||||
}
|
|
||||||
logDuration(LOG, "Loading contact", start);
|
|
||||||
loadMessages();
|
|
||||||
displayContactDetails();
|
|
||||||
} catch (NoSuchContactException e) {
|
|
||||||
finishOnUiThread();
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// contactAuthorId and contactName are expected to be set
|
|
||||||
private void displayContactDetails() {
|
|
||||||
runOnUiThreadUnlessDestroyed(() -> {
|
|
||||||
//noinspection ConstantConditions
|
|
||||||
toolbarAvatar.setImageDrawable(
|
|
||||||
new IdenticonDrawable(contactAuthorId.getBytes()));
|
|
||||||
toolbarTitle.setText(contactName.getValue());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayContactOnlineStatus() {
|
private void displayContactOnlineStatus() {
|
||||||
runOnUiThreadUnlessDestroyed(() -> {
|
runOnUiThreadUnlessDestroyed(() -> {
|
||||||
if (connectionRegistry.isConnected(contactId)) {
|
if (connectionRegistry.isConnected(contactId)) {
|
||||||
@@ -370,28 +369,28 @@ public class ConversationActivity extends BriarActivity
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadMessageBody(MessageId m) {
|
private void loadMessageText(MessageId m) {
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
long start = now();
|
long start = now();
|
||||||
String body = messagingManager.getMessageBody(m);
|
String text = messagingManager.getMessageText(m);
|
||||||
logDuration(LOG, "Loading body", start);
|
logDuration(LOG, "Loading text", start);
|
||||||
displayMessageBody(m, body);
|
displayMessageText(m, text);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayMessageBody(MessageId m, String body) {
|
private void displayMessageText(MessageId m, String text) {
|
||||||
runOnUiThreadUnlessDestroyed(() -> {
|
runOnUiThreadUnlessDestroyed(() -> {
|
||||||
bodyCache.put(m, body);
|
textCache.put(m, text);
|
||||||
SparseArray<ConversationItem> messages =
|
SparseArray<ConversationItem> messages =
|
||||||
adapter.getPrivateMessages();
|
adapter.getPrivateMessages();
|
||||||
for (int i = 0; i < messages.size(); i++) {
|
for (int i = 0; i < messages.size(); i++) {
|
||||||
ConversationItem item = messages.valueAt(i);
|
ConversationItem item = messages.valueAt(i);
|
||||||
if (item.getId().equals(m)) {
|
if (item.getId().equals(m)) {
|
||||||
item.setBody(body);
|
item.setText(text);
|
||||||
adapter.notifyItemChanged(messages.keyAt(i));
|
adapter.notifyItemChanged(messages.keyAt(i));
|
||||||
list.scrollToPosition(adapter.getItemCount() - 1);
|
list.scrollToPosition(adapter.getItemCount() - 1);
|
||||||
return;
|
return;
|
||||||
@@ -453,15 +452,17 @@ public class ConversationActivity extends BriarActivity
|
|||||||
private void onNewPrivateMessage(PrivateMessageHeader h) {
|
private void onNewPrivateMessage(PrivateMessageHeader h) {
|
||||||
runOnUiThreadUnlessDestroyed(() -> {
|
runOnUiThreadUnlessDestroyed(() -> {
|
||||||
if (h instanceof PrivateRequest || h instanceof PrivateResponse) {
|
if (h instanceof PrivateRequest || h instanceof PrivateResponse) {
|
||||||
String cName = contactName.getValue();
|
String cName = viewModel.getContactDisplayName().getValue();
|
||||||
if (cName == null) {
|
if (cName == null) {
|
||||||
// Wait for the contact name to be loaded
|
// Wait for the contact name to be loaded
|
||||||
contactName.observe(this, new Observer<String>() {
|
viewModel.getContactDisplayName()
|
||||||
|
.observe(this, new Observer<String>() {
|
||||||
@Override
|
@Override
|
||||||
public void onChanged(@Nullable String cName) {
|
public void onChanged(@Nullable String cName) {
|
||||||
if (cName != null) {
|
if (cName != null) {
|
||||||
addConversationItem(h.accept(visitor));
|
addConversationItem(h.accept(visitor));
|
||||||
contactName.removeObserver(this);
|
viewModel.getContactDisplayName()
|
||||||
|
.removeObserver(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -470,7 +471,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addConversationItem(h.accept(visitor));
|
addConversationItem(h.accept(visitor));
|
||||||
loadMessageBody(h.getId());
|
loadMessageText(h.getId());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -495,8 +496,8 @@ public class ConversationActivity extends BriarActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSendClick(String text) {
|
public void onSendClick(String text) {
|
||||||
if (text.equals("")) return;
|
if (text.isEmpty()) return;
|
||||||
text = StringUtils.truncateUtf8(text, MAX_PRIVATE_MESSAGE_BODY_LENGTH);
|
text = StringUtils.truncateUtf8(text, MAX_PRIVATE_MESSAGE_TEXT_LENGTH);
|
||||||
long timestamp = System.currentTimeMillis();
|
long timestamp = System.currentTimeMillis();
|
||||||
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
|
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
|
||||||
if (messagingGroupId == null) loadGroupId(text, timestamp);
|
if (messagingGroupId == null) loadGroupId(text, timestamp);
|
||||||
@@ -510,12 +511,12 @@ public class ConversationActivity extends BriarActivity
|
|||||||
return item == null ? 0 : item.getTime() + 1;
|
return item == null ? 0 : item.getTime() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadGroupId(String body, long timestamp) {
|
private void loadGroupId(String text, long timestamp) {
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
messagingGroupId =
|
messagingGroupId =
|
||||||
messagingManager.getConversationId(contactId);
|
messagingManager.getConversationId(contactId);
|
||||||
createMessage(body, timestamp);
|
createMessage(text, timestamp);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
}
|
}
|
||||||
@@ -523,19 +524,19 @@ public class ConversationActivity extends BriarActivity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createMessage(String body, long timestamp) {
|
private void createMessage(String text, long timestamp) {
|
||||||
cryptoExecutor.execute(() -> {
|
cryptoExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
//noinspection ConstantConditions init in loadGroupId()
|
//noinspection ConstantConditions init in loadGroupId()
|
||||||
storeMessage(privateMessageFactory.createPrivateMessage(
|
storeMessage(privateMessageFactory.createPrivateMessage(
|
||||||
messagingGroupId, timestamp, body), body);
|
messagingGroupId, timestamp, text), text);
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storeMessage(PrivateMessage m, String body) {
|
private void storeMessage(PrivateMessage m, String text) {
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
long start = now();
|
long start = now();
|
||||||
@@ -545,7 +546,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
PrivateMessageHeader h = new PrivateMessageHeader(
|
PrivateMessageHeader h = new PrivateMessageHeader(
|
||||||
message.getId(), message.getGroupId(),
|
message.getId(), message.getGroupId(),
|
||||||
message.getTimestamp(), true, false, false, false);
|
message.getTimestamp(), true, false, false, false);
|
||||||
bodyCache.put(message.getId(), body);
|
textCache.put(message.getId(), text);
|
||||||
addConversationItem(h.accept(visitor));
|
addConversationItem(h.accept(visitor));
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
@@ -761,9 +762,9 @@ public class ConversationActivity extends BriarActivity
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public String getBody(MessageId m) {
|
public String getText(MessageId m) {
|
||||||
String body = bodyCache.get(m);
|
String text = textCache.get(m);
|
||||||
if (body == null) loadMessageBody(m);
|
if (text == null) loadMessageText(m);
|
||||||
return body;
|
return text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,17 +14,17 @@ import javax.annotation.concurrent.NotThreadSafe;
|
|||||||
abstract class ConversationItem {
|
abstract class ConversationItem {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
protected String body;
|
protected String text;
|
||||||
private final MessageId id;
|
private final MessageId id;
|
||||||
private final GroupId groupId;
|
private final GroupId groupId;
|
||||||
private final long time;
|
private final long time;
|
||||||
private boolean read;
|
private boolean read;
|
||||||
|
|
||||||
ConversationItem(MessageId id, GroupId groupId, @Nullable String body,
|
ConversationItem(MessageId id, GroupId groupId, @Nullable String text,
|
||||||
long time, boolean read) {
|
long time, boolean read) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
this.body = body;
|
this.text = text;
|
||||||
this.time = time;
|
this.time = time;
|
||||||
this.read = read;
|
this.read = read;
|
||||||
}
|
}
|
||||||
@@ -37,13 +37,13 @@ abstract class ConversationItem {
|
|||||||
return groupId;
|
return groupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBody(String body) {
|
void setText(String text) {
|
||||||
this.body = body;
|
this.text = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getBody() {
|
public String getText() {
|
||||||
return body;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
long getTime() {
|
long getTime() {
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ class ConversationItemViewHolder extends ViewHolder {
|
|||||||
|
|
||||||
@CallSuper
|
@CallSuper
|
||||||
void bind(ConversationItem item) {
|
void bind(ConversationItem item) {
|
||||||
if (item.getBody() == null) {
|
if (item.getText() == null) {
|
||||||
text.setText("\u2026");
|
text.setText("\u2026");
|
||||||
} else {
|
} else {
|
||||||
text.setText(StringUtils.trim(item.getBody()));
|
text.setText(StringUtils.trim(item.getText()));
|
||||||
}
|
}
|
||||||
|
|
||||||
long timestamp = item.getTime();
|
long timestamp = item.getTime();
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ class ConversationNoticeInViewHolder extends ConversationItemViewHolder {
|
|||||||
ConversationNoticeInItem item =
|
ConversationNoticeInItem item =
|
||||||
(ConversationNoticeInItem) conversationItem;
|
(ConversationNoticeInItem) conversationItem;
|
||||||
|
|
||||||
String message = item.getMsgText();
|
String text = item.getMsgText();
|
||||||
if (StringUtils.isNullOrEmpty(message)) {
|
if (StringUtils.isNullOrEmpty(text)) {
|
||||||
msgText.setVisibility(GONE);
|
msgText.setVisibility(GONE);
|
||||||
layout.setBackgroundResource(R.drawable.notice_in);
|
layout.setBackgroundResource(R.drawable.notice_in);
|
||||||
} else {
|
} else {
|
||||||
msgText.setVisibility(VISIBLE);
|
msgText.setVisibility(VISIBLE);
|
||||||
msgText.setText(StringUtils.trim(message));
|
msgText.setText(StringUtils.trim(text));
|
||||||
layout.setBackgroundResource(R.drawable.notice_in_bottom);
|
layout.setBackgroundResource(R.drawable.notice_in_bottom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class ConversationNoticeOutItem extends ConversationOutItem {
|
|||||||
ConversationNoticeOutItem(String text, PrivateRequest r) {
|
ConversationNoticeOutItem(String text, PrivateRequest r) {
|
||||||
super(r.getId(), r.getGroupId(), text, r.getTimestamp(), r.isSent(),
|
super(r.getId(), r.getGroupId(), text, r.getTimestamp(), r.isSent(),
|
||||||
r.isSeen());
|
r.isSeen());
|
||||||
this.msgText = r.getMessage();
|
this.msgText = r.getText();
|
||||||
}
|
}
|
||||||
|
|
||||||
ConversationNoticeOutItem(String text, PrivateResponse r) {
|
ConversationNoticeOutItem(String text, PrivateResponse r) {
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ class ConversationNoticeOutViewHolder extends ConversationOutItemViewHolder {
|
|||||||
ConversationNoticeOutItem item =
|
ConversationNoticeOutItem item =
|
||||||
(ConversationNoticeOutItem) conversationItem;
|
(ConversationNoticeOutItem) conversationItem;
|
||||||
|
|
||||||
String message = item.getMsgText();
|
String text = item.getMsgText();
|
||||||
if (StringUtils.isNullOrEmpty(message)) {
|
if (StringUtils.isNullOrEmpty(text)) {
|
||||||
msgText.setVisibility(GONE);
|
msgText.setVisibility(GONE);
|
||||||
layout.setBackgroundResource(R.drawable.notice_out);
|
layout.setBackgroundResource(R.drawable.notice_out);
|
||||||
} else {
|
} else {
|
||||||
msgText.setVisibility(VISIBLE);
|
msgText.setVisibility(VISIBLE);
|
||||||
msgText.setText(StringUtils.trim(message));
|
msgText.setText(StringUtils.trim(text));
|
||||||
layout.setBackgroundResource(R.drawable.notice_out_bottom);
|
layout.setBackgroundResource(R.drawable.notice_out_bottom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class ConversationRequestItem extends ConversationNoticeInItem {
|
|||||||
private boolean answered;
|
private boolean answered;
|
||||||
|
|
||||||
ConversationRequestItem(String text, RequestType type, PrivateRequest r) {
|
ConversationRequestItem(String text, RequestType type, PrivateRequest r) {
|
||||||
super(r.getId(), r.getGroupId(), text, r.getMessage(),
|
super(r.getId(), r.getGroupId(), text, r.getText(),
|
||||||
r.getTimestamp(), r.isRead());
|
r.getTimestamp(), r.isRead());
|
||||||
this.requestType = type;
|
this.requestType = type;
|
||||||
this.sessionId = r.getSessionId();
|
this.sessionId = r.getSessionId();
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package org.briarproject.briar.android.contact;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.arch.lifecycle.AndroidViewModel;
|
||||||
|
import android.arch.lifecycle.LiveData;
|
||||||
|
import android.arch.lifecycle.MutableLiveData;
|
||||||
|
import android.arch.lifecycle.Transformations;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||||
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
|
import org.briarproject.briar.android.AndroidComponent;
|
||||||
|
import org.briarproject.briar.android.BriarApplication;
|
||||||
|
import org.briarproject.briar.android.util.UiUtils;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
|
|
||||||
|
public class ConversationViewModel extends AndroidViewModel {
|
||||||
|
|
||||||
|
private static Logger LOG =
|
||||||
|
Logger.getLogger(ConversationViewModel.class.getName());
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@DatabaseExecutor
|
||||||
|
Executor dbExecutor;
|
||||||
|
@Inject
|
||||||
|
ContactManager contactManager;
|
||||||
|
|
||||||
|
private final MutableLiveData<Contact> contact = new MutableLiveData<>();
|
||||||
|
private final LiveData<AuthorId> contactAuthorId =
|
||||||
|
Transformations.map(contact, c -> c.getAuthor().getId());
|
||||||
|
private final LiveData<String> contactName =
|
||||||
|
Transformations.map(contact, UiUtils::getContactDisplayName);
|
||||||
|
private final MutableLiveData<Boolean> contactDeleted =
|
||||||
|
new MutableLiveData<>();
|
||||||
|
|
||||||
|
public ConversationViewModel(@NonNull Application application) {
|
||||||
|
super(application);
|
||||||
|
AndroidComponent component =
|
||||||
|
((BriarApplication) application).getApplicationComponent();
|
||||||
|
component.inject(this);
|
||||||
|
contactDeleted.setValue(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadContact(ContactId contactId) {
|
||||||
|
dbExecutor.execute(() -> {
|
||||||
|
try {
|
||||||
|
long start = now();
|
||||||
|
contact.postValue(contactManager.getContact(contactId));
|
||||||
|
logDuration(LOG, "Loading contact", start);
|
||||||
|
} catch (NoSuchContactException e) {
|
||||||
|
contactDeleted.postValue(true);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void setContactAlias(ContactId contactId, String alias) {
|
||||||
|
dbExecutor.execute(() -> {
|
||||||
|
try {
|
||||||
|
contactManager.setContactAlias(contactId,
|
||||||
|
alias.isEmpty() ? null : alias);
|
||||||
|
loadContact(contactId);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveData<Contact> getContact() {
|
||||||
|
return contact;
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveData<AuthorId> getContactAuthorId() {
|
||||||
|
return contactAuthorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveData<String> getContactDisplayName() {
|
||||||
|
return contactName;
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveData<Boolean> isContactDeleted() {
|
||||||
|
return contactDeleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -24,19 +24,20 @@ import static org.briarproject.briar.android.contact.ConversationRequestItem.Req
|
|||||||
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.FORUM;
|
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.FORUM;
|
||||||
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.GROUP;
|
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.GROUP;
|
||||||
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.INTRODUCTION;
|
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.INTRODUCTION;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class ConversationVisitor implements PrivateMessageVisitor<ConversationItem> {
|
class ConversationVisitor implements PrivateMessageVisitor<ConversationItem> {
|
||||||
|
|
||||||
private final Context ctx;
|
private final Context ctx;
|
||||||
private final BodyCache bodyCache;
|
private final TextCache textCache;
|
||||||
private final LiveData<String> contactName;
|
private final LiveData<String> contactName;
|
||||||
|
|
||||||
ConversationVisitor(Context ctx, BodyCache bodyCache,
|
ConversationVisitor(Context ctx, TextCache textCache,
|
||||||
LiveData<String> contactName) {
|
LiveData<String> contactName) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.bodyCache = bodyCache;
|
this.textCache = textCache;
|
||||||
this.contactName = contactName;
|
this.contactName = contactName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,8 +46,8 @@ class ConversationVisitor implements PrivateMessageVisitor<ConversationItem> {
|
|||||||
ConversationItem item;
|
ConversationItem item;
|
||||||
if (h.isLocal()) item = new ConversationMessageOutItem(h);
|
if (h.isLocal()) item = new ConversationMessageOutItem(h);
|
||||||
else item = new ConversationMessageInItem(h);
|
else item = new ConversationMessageInItem(h);
|
||||||
String body = bodyCache.getBody(h.getId());
|
String text = textCache.getText(h.getId());
|
||||||
if (body != null) item.setBody(body);
|
if (text != null) item.setText(text);
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,33 +189,36 @@ class ConversationVisitor implements PrivateMessageVisitor<ConversationItem> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConversationItem visitIntroductionRequest(IntroductionRequest r) {
|
public ConversationItem visitIntroductionRequest(IntroductionRequest r) {
|
||||||
|
String name = getContactDisplayName(r.getNameable(), r.getAlias());
|
||||||
if (r.isLocal()) {
|
if (r.isLocal()) {
|
||||||
String text = ctx.getString(R.string.introduction_request_sent,
|
String text = ctx.getString(R.string.introduction_request_sent,
|
||||||
contactName.getValue(), r.getName());
|
contactName.getValue(), name);
|
||||||
return new ConversationNoticeOutItem(text, r);
|
return new ConversationNoticeOutItem(text, r);
|
||||||
} else {
|
} else {
|
||||||
String text = ctx.getString(R.string.introduction_request_received,
|
String text = ctx.getString(R.string.introduction_request_received,
|
||||||
contactName.getValue(), r.getName());
|
contactName.getValue(), name);
|
||||||
return new ConversationRequestItem(text, INTRODUCTION, r);
|
return new ConversationRequestItem(text, INTRODUCTION, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConversationItem visitIntroductionResponse(IntroductionResponse r) {
|
public ConversationItem visitIntroductionResponse(IntroductionResponse r) {
|
||||||
|
String introducedAuthor =
|
||||||
|
getContactDisplayName(r.getIntroducedAuthor(),
|
||||||
|
r.getIntroducedAuthorInfo().getAlias());
|
||||||
if (r.isLocal()) {
|
if (r.isLocal()) {
|
||||||
String text;
|
String text;
|
||||||
if (r.wasAccepted()) {
|
if (r.wasAccepted()) {
|
||||||
String introducee = r.getIntroducedAuthor().getName();
|
|
||||||
text = ctx.getString(
|
text = ctx.getString(
|
||||||
R.string.introduction_response_accepted_sent,
|
R.string.introduction_response_accepted_sent,
|
||||||
introducee)
|
introducedAuthor)
|
||||||
+ "\n\n" + ctx.getString(
|
+ "\n\n" + ctx.getString(
|
||||||
R.string.introduction_response_accepted_sent_info,
|
R.string.introduction_response_accepted_sent_info,
|
||||||
introducee);
|
introducedAuthor);
|
||||||
} else {
|
} else {
|
||||||
text = ctx.getString(
|
text = ctx.getString(
|
||||||
R.string.introduction_response_declined_sent,
|
R.string.introduction_response_declined_sent,
|
||||||
r.getIntroducedAuthor().getName());
|
introducedAuthor);
|
||||||
}
|
}
|
||||||
return new ConversationNoticeOutItem(text, r);
|
return new ConversationNoticeOutItem(text, r);
|
||||||
} else {
|
} else {
|
||||||
@@ -223,24 +227,24 @@ class ConversationVisitor implements PrivateMessageVisitor<ConversationItem> {
|
|||||||
text = ctx.getString(
|
text = ctx.getString(
|
||||||
R.string.introduction_response_accepted_received,
|
R.string.introduction_response_accepted_received,
|
||||||
contactName.getValue(),
|
contactName.getValue(),
|
||||||
r.getIntroducedAuthor().getName());
|
introducedAuthor);
|
||||||
} else if (r.isIntroducer()) {
|
} else if (r.isIntroducer()) {
|
||||||
text = ctx.getString(
|
text = ctx.getString(
|
||||||
R.string.introduction_response_declined_received,
|
R.string.introduction_response_declined_received,
|
||||||
contactName.getValue(),
|
contactName.getValue(),
|
||||||
r.getIntroducedAuthor().getName());
|
introducedAuthor);
|
||||||
} else {
|
} else {
|
||||||
text = ctx.getString(
|
text = ctx.getString(
|
||||||
R.string.introduction_response_declined_received_by_introducee,
|
R.string.introduction_response_declined_received_by_introducee,
|
||||||
contactName.getValue(),
|
contactName.getValue(),
|
||||||
r.getIntroducedAuthor().getName());
|
introducedAuthor);
|
||||||
}
|
}
|
||||||
return new ConversationNoticeInItem(text, r);
|
return new ConversationNoticeInItem(text, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BodyCache {
|
interface TextCache {
|
||||||
@Nullable
|
@Nullable
|
||||||
String getBody(MessageId m);
|
String getText(MessageId m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import javax.inject.Inject;
|
|||||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
import static android.widget.Toast.LENGTH_SHORT;
|
import static android.widget.Toast.LENGTH_SHORT;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_SHARE_FORUM;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_SHARE_FORUM;
|
||||||
import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
|
import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_POST_TEXT_LENGTH;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -130,8 +130,8 @@ public class ForumActivity extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getMaxBodyLength() {
|
protected int getMaxTextLength() {
|
||||||
return MAX_FORUM_POST_BODY_LENGTH;
|
return MAX_FORUM_POST_TEXT_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class ForumControllerImpl extends
|
|||||||
ForumPostReceivedEvent f = (ForumPostReceivedEvent) e;
|
ForumPostReceivedEvent f = (ForumPostReceivedEvent) e;
|
||||||
if (f.getGroupId().equals(getGroupId())) {
|
if (f.getGroupId().equals(getGroupId())) {
|
||||||
LOG.info("Forum post received, adding...");
|
LOG.info("Forum post received, adding...");
|
||||||
onForumPostReceived(f.getHeader(), f.getBody());
|
onForumPostReceived(f.getHeader(), f.getText());
|
||||||
}
|
}
|
||||||
} else if (e instanceof ForumInvitationResponseReceivedEvent) {
|
} else if (e instanceof ForumInvitationResponseReceivedEvent) {
|
||||||
ForumInvitationResponseReceivedEvent f =
|
ForumInvitationResponseReceivedEvent f =
|
||||||
@@ -109,8 +109,8 @@ class ForumControllerImpl extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String loadMessageBody(ForumPostHeader h) throws DbException {
|
protected String loadMessageText(ForumPostHeader h) throws DbException {
|
||||||
return forumManager.getPostBody(h.getId());
|
return forumManager.getPostText(h.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -137,7 +137,7 @@ class ForumControllerImpl extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createAndStoreMessage(String body,
|
public void createAndStoreMessage(String text,
|
||||||
@Nullable ForumItem parentItem,
|
@Nullable ForumItem parentItem,
|
||||||
ResultExceptionHandler<ForumItem, DbException> handler) {
|
ResultExceptionHandler<ForumItem, DbException> handler) {
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
@@ -148,7 +148,7 @@ class ForumControllerImpl extends
|
|||||||
clock.currentTimeMillis());
|
clock.currentTimeMillis());
|
||||||
MessageId parentId = parentItem != null ?
|
MessageId parentId = parentItem != null ?
|
||||||
parentItem.getId() : null;
|
parentItem.getId() : null;
|
||||||
createMessage(body, timestamp, parentId, author, handler);
|
createMessage(text, timestamp, parentId, author, handler);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
handler.onException(e);
|
handler.onException(e);
|
||||||
@@ -156,14 +156,14 @@ class ForumControllerImpl extends
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createMessage(String body, long timestamp,
|
private void createMessage(String text, long timestamp,
|
||||||
@Nullable MessageId parentId, LocalAuthor author,
|
@Nullable MessageId parentId, LocalAuthor author,
|
||||||
ResultExceptionHandler<ForumItem, DbException> handler) {
|
ResultExceptionHandler<ForumItem, DbException> handler) {
|
||||||
cryptoExecutor.execute(() -> {
|
cryptoExecutor.execute(() -> {
|
||||||
LOG.info("Creating forum post...");
|
LOG.info("Creating forum post...");
|
||||||
ForumPost msg = forumManager.createLocalPost(getGroupId(), body,
|
ForumPost msg = forumManager.createLocalPost(getGroupId(), text,
|
||||||
timestamp, parentId, author);
|
timestamp, parentId, author);
|
||||||
storePost(msg, body, handler);
|
storePost(msg, text, handler);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,12 +179,12 @@ class ForumControllerImpl extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ForumItem buildItem(ForumPostHeader header, String body) {
|
protected ForumItem buildItem(ForumPostHeader header, String text) {
|
||||||
return new ForumItem(header, body);
|
return new ForumItem(header, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onForumPostReceived(ForumPostHeader h, String body) {
|
private void onForumPostReceived(ForumPostHeader h, String text) {
|
||||||
ForumItem item = buildItem(h, body);
|
ForumItem item = buildItem(h, text);
|
||||||
listener.runOnUiThreadUnlessDestroyed(
|
listener.runOnUiThreadUnlessDestroyed(
|
||||||
() -> listener.onItemReceived(item));
|
() -> listener.onItemReceived(item));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package org.briarproject.briar.android.forum;
|
package org.briarproject.briar.android.forum;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.Author.Status;
|
import org.briarproject.bramble.api.identity.AuthorInfo;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.android.threaded.ThreadItem;
|
import org.briarproject.briar.android.threaded.ThreadItem;
|
||||||
import org.briarproject.briar.api.forum.ForumPostHeader;
|
import org.briarproject.briar.api.forum.ForumPostHeader;
|
||||||
@@ -12,13 +12,13 @@ import javax.annotation.concurrent.NotThreadSafe;
|
|||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
class ForumItem extends ThreadItem {
|
class ForumItem extends ThreadItem {
|
||||||
|
|
||||||
ForumItem(ForumPostHeader h, String body) {
|
ForumItem(ForumPostHeader h, String text) {
|
||||||
super(h.getId(), h.getParentId(), body, h.getTimestamp(), h.getAuthor(),
|
super(h.getId(), h.getParentId(), text, h.getTimestamp(), h.getAuthor(),
|
||||||
h.getAuthorStatus(), h.isRead());
|
h.getAuthorInfo(), h.isRead());
|
||||||
}
|
}
|
||||||
|
|
||||||
ForumItem(MessageId messageId, @Nullable MessageId parentId, String text,
|
ForumItem(MessageId messageId, @Nullable MessageId parentId, String text,
|
||||||
long timestamp, Author author, Status status) {
|
long timestamp, Author author, AuthorInfo status) {
|
||||||
super(messageId, parentId, text, timestamp, author, status, true);
|
super(messageId, parentId, text, timestamp, author, status, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ import static android.view.View.VISIBLE;
|
|||||||
import static android.widget.Toast.LENGTH_SHORT;
|
import static android.widget.Toast.LENGTH_SHORT;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_REQUEST_MESSAGE_LENGTH;
|
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
|
||||||
|
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_TEXT_LENGTH;
|
||||||
|
|
||||||
public class IntroductionMessageFragment extends BaseFragment
|
public class IntroductionMessageFragment extends BaseFragment
|
||||||
implements TextInputListener {
|
implements TextInputListener {
|
||||||
@@ -148,8 +149,8 @@ public class IntroductionMessageFragment extends BaseFragment
|
|||||||
c2.getAuthor().getId().getBytes()));
|
c2.getAuthor().getId().getBytes()));
|
||||||
|
|
||||||
// set contact names
|
// set contact names
|
||||||
ui.contactName1.setText(c1.getAuthor().getName());
|
ui.contactName1.setText(getContactDisplayName(c1));
|
||||||
ui.contactName2.setText(c2.getAuthor().getName());
|
ui.contactName2.setText(getContactDisplayName(c2));
|
||||||
|
|
||||||
// hide progress bar
|
// hide progress bar
|
||||||
ui.progressBar.setVisibility(GONE);
|
ui.progressBar.setVisibility(GONE);
|
||||||
@@ -187,10 +188,10 @@ public class IntroductionMessageFragment extends BaseFragment
|
|||||||
// disable button to prevent accidental double invitations
|
// disable button to prevent accidental double invitations
|
||||||
ui.message.setSendButtonEnabled(false);
|
ui.message.setSendButtonEnabled(false);
|
||||||
|
|
||||||
String msg = ui.message.getText().toString();
|
String txt = ui.message.getText().toString();
|
||||||
if (msg.equals("")) msg = null;
|
if (txt.isEmpty()) txt = null;
|
||||||
else msg = StringUtils.truncateUtf8(msg, MAX_REQUEST_MESSAGE_LENGTH);
|
else txt = StringUtils.truncateUtf8(txt, MAX_INTRODUCTION_TEXT_LENGTH);
|
||||||
makeIntroduction(contact1, contact2, msg);
|
makeIntroduction(contact1, contact2, txt);
|
||||||
|
|
||||||
// don't wait for the introduction to be made before finishing activity
|
// don't wait for the introduction to be made before finishing activity
|
||||||
introductionActivity.hideSoftKeyboard(ui.message);
|
introductionActivity.hideSoftKeyboard(ui.message);
|
||||||
@@ -199,12 +200,12 @@ public class IntroductionMessageFragment extends BaseFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void makeIntroduction(Contact c1, Contact c2,
|
private void makeIntroduction(Contact c1, Contact c2,
|
||||||
@Nullable String msg) {
|
@Nullable String text) {
|
||||||
introductionActivity.runOnDbThread(() -> {
|
introductionActivity.runOnDbThread(() -> {
|
||||||
// actually make the introduction
|
// actually make the introduction
|
||||||
try {
|
try {
|
||||||
long timestamp = System.currentTimeMillis();
|
long timestamp = System.currentTimeMillis();
|
||||||
introductionManager.makeIntroduction(c1, c2, msg, timestamp);
|
introductionManager.makeIntroduction(c1, c2, text, timestamp);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
introductionError();
|
introductionError();
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.briar.android.keyagreement;
|
|||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface.OnClickListener;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -11,19 +10,15 @@ import android.support.annotation.StringRes;
|
|||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.v4.app.ActivityCompat;
|
import android.support.v4.app.ActivityCompat;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v4.content.ContextCompat;
|
|
||||||
import android.support.v7.app.AlertDialog.Builder;
|
import android.support.v7.app.AlertDialog.Builder;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent;
|
import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.R.string;
|
|
||||||
import org.briarproject.briar.R.style;
|
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
@@ -37,16 +32,19 @@ import java.util.logging.Logger;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
|
||||||
import static android.Manifest.permission.CAMERA;
|
import static android.Manifest.permission.CAMERA;
|
||||||
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_ENABLE;
|
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
|
||||||
|
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
|
||||||
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
|
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
|
||||||
|
import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
|
||||||
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
|
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
|
||||||
|
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
|
||||||
|
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
|
||||||
import static android.bluetooth.BluetoothAdapter.STATE_ON;
|
import static android.bluetooth.BluetoothAdapter.STATE_ON;
|
||||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE;
|
||||||
import static android.widget.Toast.LENGTH_LONG;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_ENABLE_BLUETOOTH;
|
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA;
|
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -55,7 +53,11 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
KeyAgreementEventListener {
|
KeyAgreementEventListener {
|
||||||
|
|
||||||
private enum BluetoothState {
|
private enum BluetoothState {
|
||||||
UNKNOWN, NO_ADAPTER, WAITING, REFUSED, ENABLED
|
UNKNOWN, NO_ADAPTER, WAITING, REFUSED, ENABLED, DISCOVERABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum Permission {
|
||||||
|
UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
@@ -64,8 +66,27 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
@Inject
|
@Inject
|
||||||
EventBus eventBus;
|
EventBus eventBus;
|
||||||
|
|
||||||
private boolean isResumed = false, enableWasRequested = false;
|
/**
|
||||||
private boolean continueClicked, gotCameraPermission;
|
* Set to true in onPostResume() and false in onPause(). This prevents the
|
||||||
|
* QR code fragment from being shown if onRequestPermissionsResult() is
|
||||||
|
* called while the activity is paused, which could cause a crash due to
|
||||||
|
* https://issuetracker.google.com/issues/37067655.
|
||||||
|
*/
|
||||||
|
private boolean isResumed = false;
|
||||||
|
/**
|
||||||
|
* Set to true when the continue button is clicked, and false when the QR
|
||||||
|
* code fragment is shown. This prevents the QR code fragment from being
|
||||||
|
* shown automatically before the continue button has been clicked.
|
||||||
|
*/
|
||||||
|
private boolean continueClicked = false;
|
||||||
|
/**
|
||||||
|
* Records whether the Bluetooth adapter was already enabled before we
|
||||||
|
* asked for Bluetooth discoverability, so we know whether to broadcast a
|
||||||
|
* {@link BluetoothEnabledEvent}.
|
||||||
|
*/
|
||||||
|
private boolean wasAdapterEnabled = false;
|
||||||
|
private Permission cameraPermission = Permission.UNKNOWN;
|
||||||
|
private Permission locationPermission = Permission.UNKNOWN;
|
||||||
private BluetoothState bluetoothState = BluetoothState.UNKNOWN;
|
private BluetoothState bluetoothState = BluetoothState.UNKNOWN;
|
||||||
private BroadcastReceiver bluetoothReceiver = null;
|
private BroadcastReceiver bluetoothReceiver = null;
|
||||||
|
|
||||||
@@ -85,7 +106,9 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
if (state == null) {
|
if (state == null) {
|
||||||
showInitialFragment(IntroFragment.newInstance());
|
showInitialFragment(IntroFragment.newInstance());
|
||||||
}
|
}
|
||||||
IntentFilter filter = new IntentFilter(ACTION_STATE_CHANGED);
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(ACTION_STATE_CHANGED);
|
||||||
|
filter.addAction(ACTION_SCAN_MODE_CHANGED);
|
||||||
bluetoothReceiver = new BluetoothStateReceiver();
|
bluetoothReceiver = new BluetoothStateReceiver();
|
||||||
registerReceiver(bluetoothReceiver, filter);
|
registerReceiver(bluetoothReceiver, filter);
|
||||||
}
|
}
|
||||||
@@ -107,20 +130,40 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
// Permissions may have been granted manually while we were stopped
|
||||||
|
cameraPermission = Permission.UNKNOWN;
|
||||||
|
locationPermission = Permission.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostResume() {
|
protected void onPostResume() {
|
||||||
super.onPostResume();
|
super.onPostResume();
|
||||||
isResumed = true;
|
isResumed = true;
|
||||||
// Workaround for
|
// Workaround for
|
||||||
// https://code.google.com/p/android/issues/detail?id=190966
|
// https://code.google.com/p/android/issues/detail?id=190966
|
||||||
if (canShowQrCodeFragment()) showQrCodeFragment();
|
showQrCodeFragmentIfAllowed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canShowQrCodeFragment() {
|
private void showQrCodeFragmentIfAllowed() {
|
||||||
return isResumed && continueClicked
|
if (isResumed && continueClicked && areEssentialPermissionsGranted()) {
|
||||||
&& (SDK_INT < 23 || gotCameraPermission)
|
if (bluetoothState == BluetoothState.UNKNOWN ||
|
||||||
&& bluetoothState != BluetoothState.UNKNOWN
|
bluetoothState == BluetoothState.ENABLED) {
|
||||||
&& bluetoothState != BluetoothState.WAITING;
|
requestBluetoothDiscoverable();
|
||||||
|
} else if (bluetoothState != BluetoothState.WAITING) {
|
||||||
|
showQrCodeFragment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean areEssentialPermissionsGranted() {
|
||||||
|
// If the camera permission has been granted, and the location
|
||||||
|
// permission has been granted or permanently denied, we can continue
|
||||||
|
return cameraPermission == Permission.GRANTED &&
|
||||||
|
(locationPermission == Permission.GRANTED ||
|
||||||
|
locationPermission == Permission.PERMANENTLY_DENIED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -132,50 +175,54 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
@Override
|
@Override
|
||||||
public void showNextScreen() {
|
public void showNextScreen() {
|
||||||
continueClicked = true;
|
continueClicked = true;
|
||||||
if (checkPermissions()) {
|
if (checkPermissions()) showQrCodeFragmentIfAllowed();
|
||||||
if (shouldRequestEnableBluetooth()) requestEnableBluetooth();
|
|
||||||
else if (canShowQrCodeFragment()) showQrCodeFragment();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldRequestEnableBluetooth() {
|
private void requestBluetoothDiscoverable() {
|
||||||
return bluetoothState == BluetoothState.UNKNOWN
|
|
||||||
|| bluetoothState == BluetoothState.REFUSED;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void requestEnableBluetooth() {
|
|
||||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||||
if (bt == null) {
|
if (bt == null) {
|
||||||
setBluetoothState(BluetoothState.NO_ADAPTER);
|
setBluetoothState(BluetoothState.NO_ADAPTER);
|
||||||
} else if (bt.isEnabled()) {
|
|
||||||
setBluetoothState(BluetoothState.ENABLED);
|
|
||||||
} else {
|
} else {
|
||||||
enableWasRequested = true;
|
|
||||||
setBluetoothState(BluetoothState.WAITING);
|
setBluetoothState(BluetoothState.WAITING);
|
||||||
Intent i = new Intent(ACTION_REQUEST_ENABLE);
|
wasAdapterEnabled = bt.isEnabled();
|
||||||
startActivityForResult(i, REQUEST_ENABLE_BLUETOOTH);
|
Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
|
||||||
|
startActivityForResult(i, REQUEST_BLUETOOTH_DISCOVERABLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setBluetoothState(BluetoothState bluetoothState) {
|
private void setBluetoothState(BluetoothState bluetoothState) {
|
||||||
LOG.info("Setting Bluetooth state to " + bluetoothState);
|
LOG.info("Setting Bluetooth state to " + bluetoothState);
|
||||||
this.bluetoothState = bluetoothState;
|
this.bluetoothState = bluetoothState;
|
||||||
if (enableWasRequested && bluetoothState == BluetoothState.ENABLED) {
|
if (!wasAdapterEnabled && bluetoothState == BluetoothState.ENABLED) {
|
||||||
eventBus.broadcast(new BluetoothEnabledEvent());
|
eventBus.broadcast(new BluetoothEnabledEvent());
|
||||||
enableWasRequested = false;
|
wasAdapterEnabled = true;
|
||||||
}
|
}
|
||||||
if (canShowQrCodeFragment()) showQrCodeFragment();
|
showQrCodeFragmentIfAllowed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityResult(int request, int result, Intent data) {
|
public void onActivityResult(int request, int result, Intent data) {
|
||||||
// If the request was granted we'll catch the state change event
|
if (request == REQUEST_BLUETOOTH_DISCOVERABLE) {
|
||||||
if (request == REQUEST_ENABLE_BLUETOOTH && result == RESULT_CANCELED)
|
if (result == RESULT_CANCELED) {
|
||||||
setBluetoothState(BluetoothState.REFUSED);
|
setBluetoothState(BluetoothState.REFUSED);
|
||||||
|
} else {
|
||||||
|
// If Bluetooth is already discoverable, show the QR code -
|
||||||
|
// otherwise wait for the state or scan mode to change
|
||||||
|
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
if (bt == null) throw new AssertionError();
|
||||||
|
if (bt.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE)
|
||||||
|
setBluetoothState(BluetoothState.DISCOVERABLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showQrCodeFragment() {
|
private void showQrCodeFragment() {
|
||||||
|
// If we return to the intro fragment, the continue button needs to be
|
||||||
|
// clicked again before showing the QR code fragment
|
||||||
continueClicked = false;
|
continueClicked = false;
|
||||||
|
// If we return to the intro fragment, ask for Bluetooth
|
||||||
|
// discoverability again before showing the QR code fragment
|
||||||
|
bluetoothState = BluetoothState.UNKNOWN;
|
||||||
// FIXME #824
|
// FIXME #824
|
||||||
FragmentManager fm = getSupportFragmentManager();
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
if (fm.findFragmentByTag(KeyAgreementFragment.TAG) == null) {
|
if (fm.findFragmentByTag(KeyAgreementFragment.TAG) == null) {
|
||||||
@@ -194,74 +241,113 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkPermissions() {
|
private boolean checkPermissions() {
|
||||||
if (ContextCompat.checkSelfPermission(this, CAMERA) !=
|
if (areEssentialPermissionsGranted()) return true;
|
||||||
PERMISSION_GRANTED) {
|
// If the camera permission has been permanently denied, ask the
|
||||||
// Should we show an explanation?
|
// user to change the setting
|
||||||
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
|
if (cameraPermission == Permission.PERMANENTLY_DENIED) {
|
||||||
CAMERA)) {
|
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
||||||
OnClickListener continueListener =
|
builder.setTitle(R.string.permission_camera_title);
|
||||||
(dialog, which) -> requestPermission();
|
builder.setMessage(R.string.permission_camera_denied_body);
|
||||||
Builder builder = new Builder(this, style.BriarDialogTheme);
|
builder.setPositiveButton(R.string.ok,
|
||||||
builder.setTitle(string.permission_camera_title);
|
UiUtils.getGoToSettingsListener(this));
|
||||||
builder.setMessage(string.permission_camera_request_body);
|
builder.setNegativeButton(R.string.cancel,
|
||||||
builder.setNeutralButton(string.continue_button,
|
(dialog, which) -> supportFinishAfterTransition());
|
||||||
continueListener);
|
builder.show();
|
||||||
builder.show();
|
|
||||||
} else {
|
|
||||||
requestPermission();
|
|
||||||
}
|
|
||||||
gotCameraPermission = false;
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
gotCameraPermission = true;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
// Should we show the rationale for one or both permissions?
|
||||||
|
if (cameraPermission == Permission.SHOW_RATIONALE &&
|
||||||
|
locationPermission == Permission.SHOW_RATIONALE) {
|
||||||
|
showRationale(R.string.permission_camera_location_title,
|
||||||
|
R.string.permission_camera_location_request_body);
|
||||||
|
} else if (cameraPermission == Permission.SHOW_RATIONALE) {
|
||||||
|
showRationale(R.string.permission_camera_title,
|
||||||
|
R.string.permission_camera_request_body);
|
||||||
|
} else if (locationPermission == Permission.SHOW_RATIONALE) {
|
||||||
|
showRationale(R.string.permission_location_title,
|
||||||
|
R.string.permission_location_request_body);
|
||||||
|
} else {
|
||||||
|
requestPermissions();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestPermission() {
|
private void showRationale(@StringRes int title, @StringRes int body) {
|
||||||
ActivityCompat.requestPermissions(this, new String[] {CAMERA},
|
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
||||||
REQUEST_PERMISSION_CAMERA);
|
builder.setTitle(title);
|
||||||
|
builder.setMessage(body);
|
||||||
|
builder.setNeutralButton(R.string.continue_button,
|
||||||
|
(dialog, which) -> requestPermissions());
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requestPermissions() {
|
||||||
|
ActivityCompat.requestPermissions(this,
|
||||||
|
new String[] {CAMERA, ACCESS_COARSE_LOCATION},
|
||||||
|
REQUEST_PERMISSION_CAMERA_LOCATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@UiThread
|
@UiThread
|
||||||
public void onRequestPermissionsResult(int requestCode,
|
public void onRequestPermissionsResult(int requestCode,
|
||||||
String permissions[], int[] grantResults) {
|
String[] permissions, int[] grantResults) {
|
||||||
if (requestCode == REQUEST_PERMISSION_CAMERA) {
|
if (requestCode != REQUEST_PERMISSION_CAMERA_LOCATION)
|
||||||
// If request is cancelled, the result arrays are empty.
|
throw new AssertionError();
|
||||||
if (grantResults.length > 0 &&
|
if (gotPermission(CAMERA, permissions, grantResults)) {
|
||||||
grantResults[0] == PERMISSION_GRANTED) {
|
cameraPermission = Permission.GRANTED;
|
||||||
gotCameraPermission = true;
|
} else if (shouldShowRationale(CAMERA)) {
|
||||||
showNextScreen();
|
cameraPermission = Permission.SHOW_RATIONALE;
|
||||||
} else {
|
} else {
|
||||||
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
|
cameraPermission = Permission.PERMANENTLY_DENIED;
|
||||||
CAMERA)) {
|
|
||||||
// The user has permanently denied the request
|
|
||||||
OnClickListener cancelListener =
|
|
||||||
(dialog, which) -> supportFinishAfterTransition();
|
|
||||||
Builder builder = new Builder(this, style.BriarDialogTheme);
|
|
||||||
builder.setTitle(string.permission_camera_title);
|
|
||||||
builder.setMessage(string.permission_camera_denied_body);
|
|
||||||
builder.setPositiveButton(string.ok,
|
|
||||||
UiUtils.getGoToSettingsListener(this));
|
|
||||||
builder.setNegativeButton(string.cancel, cancelListener);
|
|
||||||
builder.show();
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, string.permission_camera_denied_toast,
|
|
||||||
LENGTH_LONG).show();
|
|
||||||
supportFinishAfterTransition();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (gotPermission(ACCESS_COARSE_LOCATION, permissions, grantResults)) {
|
||||||
|
locationPermission = Permission.GRANTED;
|
||||||
|
} else if (shouldShowRationale(ACCESS_COARSE_LOCATION)) {
|
||||||
|
locationPermission = Permission.SHOW_RATIONALE;
|
||||||
|
} else {
|
||||||
|
locationPermission = Permission.PERMANENTLY_DENIED;
|
||||||
|
}
|
||||||
|
// If a permission dialog has been shown, showing the QR code fragment
|
||||||
|
// on this call path would cause a crash due to
|
||||||
|
// https://code.google.com/p/android/issues/detail?id=190966.
|
||||||
|
// In that case the isResumed flag prevents the fragment from being
|
||||||
|
// shown here, and showQrCodeFragmentIfAllowed() will be called again
|
||||||
|
// from onPostResume().
|
||||||
|
if (checkPermissions()) showQrCodeFragmentIfAllowed();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean gotPermission(String permission, String[] permissions,
|
||||||
|
int[] grantResults) {
|
||||||
|
for (int i = 0; i < permissions.length; i++) {
|
||||||
|
if (permission.equals(permissions[i]))
|
||||||
|
return grantResults[i] == PERMISSION_GRANTED;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldShowRationale(String permission) {
|
||||||
|
return ActivityCompat.shouldShowRequestPermissionRationale(this,
|
||||||
|
permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BluetoothStateReceiver extends BroadcastReceiver {
|
private class BluetoothStateReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
int state = intent.getIntExtra(EXTRA_STATE, 0);
|
String action = intent.getAction();
|
||||||
if (state == STATE_ON) setBluetoothState(BluetoothState.ENABLED);
|
if (ACTION_STATE_CHANGED.equals(action)) {
|
||||||
else setBluetoothState(BluetoothState.UNKNOWN);
|
int state = intent.getIntExtra(EXTRA_STATE, 0);
|
||||||
|
if (state == STATE_ON)
|
||||||
|
setBluetoothState(BluetoothState.ENABLED);
|
||||||
|
else setBluetoothState(BluetoothState.UNKNOWN);
|
||||||
|
} else if (ACTION_SCAN_MODE_CHANGED.equals(action)) {
|
||||||
|
int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0);
|
||||||
|
if (scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE)
|
||||||
|
setBluetoothState(BluetoothState.DISCOVERABLE);
|
||||||
|
else if (scanMode == SCAN_MODE_CONNECTABLE)
|
||||||
|
setBluetoothState(BluetoothState.ENABLED);
|
||||||
|
else setBluetoothState(BluetoothState.UNKNOWN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
|||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
import javax.annotation.ParametersAreNonnullByDefault;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.COMPACTING_DATABASE;
|
||||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.MIGRATING_DATABASE;
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.MIGRATING_DATABASE;
|
||||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING_SERVICES;
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING_SERVICES;
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ public class OpenDatabaseActivity extends BriarActivity
|
|||||||
|
|
||||||
private TextView textView;
|
private TextView textView;
|
||||||
private ImageView imageView;
|
private ImageView imageView;
|
||||||
private boolean showingMigration = false;
|
private boolean showingMigration = false, showingCompaction = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle state) {
|
public void onCreate(@Nullable Bundle state) {
|
||||||
@@ -57,6 +58,7 @@ public class OpenDatabaseActivity extends BriarActivity
|
|||||||
finishAndStartApp();
|
finishAndStartApp();
|
||||||
} else {
|
} else {
|
||||||
if (state == MIGRATING_DATABASE) showMigration();
|
if (state == MIGRATING_DATABASE) showMigration();
|
||||||
|
else if (state == COMPACTING_DATABASE) showCompaction();
|
||||||
eventBus.addListener(this);
|
eventBus.addListener(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,6 +77,8 @@ public class OpenDatabaseActivity extends BriarActivity
|
|||||||
runOnUiThreadUnlessDestroyed(this::finishAndStartApp);
|
runOnUiThreadUnlessDestroyed(this::finishAndStartApp);
|
||||||
else if (state == MIGRATING_DATABASE)
|
else if (state == MIGRATING_DATABASE)
|
||||||
runOnUiThreadUnlessDestroyed(this::showMigration);
|
runOnUiThreadUnlessDestroyed(this::showMigration);
|
||||||
|
else if (state == COMPACTING_DATABASE)
|
||||||
|
runOnUiThreadUnlessDestroyed(this::showCompaction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +89,13 @@ public class OpenDatabaseActivity extends BriarActivity
|
|||||||
showingMigration = true;
|
showingMigration = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showCompaction() {
|
||||||
|
if (showingCompaction) return;
|
||||||
|
textView.setText(R.string.startup_compact_database);
|
||||||
|
imageView.setImageResource(R.drawable.startup_migration);
|
||||||
|
showingCompaction = true;
|
||||||
|
}
|
||||||
|
|
||||||
private void finishAndStartApp() {
|
private void finishAndStartApp() {
|
||||||
startActivity(new Intent(this, NavDrawerActivity.class));
|
startActivity(new Intent(this, NavDrawerActivity.class));
|
||||||
supportFinishAfterTransition();
|
supportFinishAfterTransition();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.briar.android.panic;
|
package org.briarproject.briar.android.panic;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -22,9 +21,14 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import info.guardianproject.panic.Panic;
|
|
||||||
import info.guardianproject.panic.PanicResponder;
|
import info.guardianproject.panic.PanicResponder;
|
||||||
|
|
||||||
|
import static android.app.Activity.RESULT_CANCELED;
|
||||||
|
import static android.app.Activity.RESULT_OK;
|
||||||
|
import static android.content.Intent.ACTION_VIEW;
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||||
|
import static info.guardianproject.panic.Panic.PACKAGE_NAME_NONE;
|
||||||
|
|
||||||
public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
||||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
@@ -42,7 +46,9 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
|||||||
@Override
|
@Override
|
||||||
public void onCreatePreferences(Bundle bundle, String s) {
|
public void onCreatePreferences(Bundle bundle, String s) {
|
||||||
addPreferencesFromResource(R.xml.panic_preferences);
|
addPreferencesFromResource(R.xml.panic_preferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePreferences() {
|
||||||
pm = getActivity().getPackageManager();
|
pm = getActivity().getPackageManager();
|
||||||
|
|
||||||
lockPref = (SwitchPreference) findPreference(KEY_LOCK);
|
lockPref = (SwitchPreference) findPreference(KEY_LOCK);
|
||||||
@@ -74,7 +80,7 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
|||||||
ArrayList<CharSequence> entries = new ArrayList<>();
|
ArrayList<CharSequence> entries = new ArrayList<>();
|
||||||
ArrayList<CharSequence> entryValues = new ArrayList<>();
|
ArrayList<CharSequence> entryValues = new ArrayList<>();
|
||||||
entries.add(0, getString(R.string.panic_app_setting_none));
|
entries.add(0, getString(R.string.panic_app_setting_none));
|
||||||
entryValues.add(0, Panic.PACKAGE_NAME_NONE);
|
entryValues.add(0, PACKAGE_NAME_NONE);
|
||||||
|
|
||||||
for (ResolveInfo resolveInfo : PanicResponder.resolveTriggerApps(pm)) {
|
for (ResolveInfo resolveInfo : PanicResponder.resolveTriggerApps(pm)) {
|
||||||
if (resolveInfo.activityInfo == null)
|
if (resolveInfo.activityInfo == null)
|
||||||
@@ -83,21 +89,19 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
|||||||
entryValues.add(resolveInfo.activityInfo.packageName);
|
entryValues.add(resolveInfo.activityInfo.packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
panicAppPref.setEntries(
|
panicAppPref.setEntries(entries.toArray(new CharSequence[0]));
|
||||||
entries.toArray(new CharSequence[entries.size()]));
|
panicAppPref.setEntryValues(entryValues.toArray(new CharSequence[0]));
|
||||||
panicAppPref.setEntryValues(
|
panicAppPref.setDefaultValue(PACKAGE_NAME_NONE);
|
||||||
entryValues.toArray(new CharSequence[entryValues.size()]));
|
|
||||||
panicAppPref.setDefaultValue(Panic.PACKAGE_NAME_NONE);
|
|
||||||
|
|
||||||
panicAppPref.setOnPreferenceChangeListener((preference, newValue) -> {
|
panicAppPref.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||||
String packageName = (String) newValue;
|
String packageName = (String) newValue;
|
||||||
PanicResponder.setTriggerPackageName(getActivity(), packageName);
|
PanicResponder.setTriggerPackageName(getActivity(), packageName);
|
||||||
showPanicApp(packageName);
|
showPanicApp(packageName);
|
||||||
|
|
||||||
if (packageName.equals(Panic.PACKAGE_NAME_NONE)) {
|
if (packageName.equals(PACKAGE_NAME_NONE)) {
|
||||||
purgePref.setChecked(false);
|
purgePref.setChecked(false);
|
||||||
purgePref.setEnabled(false);
|
purgePref.setEnabled(false);
|
||||||
getActivity().setResult(Activity.RESULT_CANCELED);
|
getActivity().setResult(RESULT_CANCELED);
|
||||||
} else {
|
} else {
|
||||||
purgePref.setEnabled(true);
|
purgePref.setEnabled(true);
|
||||||
}
|
}
|
||||||
@@ -107,16 +111,18 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
|||||||
|
|
||||||
if (entries.size() <= 1) {
|
if (entries.size() <= 1) {
|
||||||
panicAppPref.setOnPreferenceClickListener(preference -> {
|
panicAppPref.setOnPreferenceClickListener(preference -> {
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
Intent intent = new Intent(ACTION_VIEW);
|
||||||
intent.setData(Uri.parse(
|
intent.setData(Uri.parse(
|
||||||
"market://details?id=info.guardianproject.ripple"));
|
"market://details?id=info.guardianproject.ripple"));
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||||
if (intent.resolveActivity(getActivity().getPackageManager())
|
if (intent.resolveActivity(getActivity().getPackageManager())
|
||||||
!= null) {
|
!= null) {
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
panicAppPref.setOnPreferenceClickListener(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +131,7 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
|||||||
super.onStart();
|
super.onStart();
|
||||||
getPreferenceScreen().getSharedPreferences()
|
getPreferenceScreen().getSharedPreferences()
|
||||||
.registerOnSharedPreferenceChangeListener(this);
|
.registerOnSharedPreferenceChangeListener(this);
|
||||||
|
updatePreferences();
|
||||||
showPanicApp(PanicResponder.getTriggerPackageName(getActivity()));
|
showPanicApp(PanicResponder.getTriggerPackageName(getActivity()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,9 +159,9 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
|||||||
|
|
||||||
private void showPanicApp(String triggerPackageName) {
|
private void showPanicApp(String triggerPackageName) {
|
||||||
if (TextUtils.isEmpty(triggerPackageName)
|
if (TextUtils.isEmpty(triggerPackageName)
|
||||||
|| triggerPackageName.equals(Panic.PACKAGE_NAME_NONE)) {
|
|| triggerPackageName.equals(PACKAGE_NAME_NONE)) {
|
||||||
// no panic app set
|
// no panic app set
|
||||||
panicAppPref.setValue(Panic.PACKAGE_NAME_NONE);
|
panicAppPref.setValue(PACKAGE_NAME_NONE);
|
||||||
panicAppPref
|
panicAppPref
|
||||||
.setSummary(getString(R.string.panic_app_setting_summary));
|
.setSummary(getString(R.string.panic_app_setting_summary));
|
||||||
panicAppPref.setIcon(
|
panicAppPref.setIcon(
|
||||||
@@ -176,8 +183,8 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
|||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
// revert back to no app, just to be safe
|
// revert back to no app, just to be safe
|
||||||
PanicResponder.setTriggerPackageName(getActivity(),
|
PanicResponder.setTriggerPackageName(getActivity(),
|
||||||
Panic.PACKAGE_NAME_NONE);
|
PACKAGE_NAME_NONE);
|
||||||
showPanicApp(Panic.PACKAGE_NAME_NONE);
|
showPanicApp(PACKAGE_NAME_NONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,10 +193,10 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
|||||||
DialogInterface.OnClickListener okListener = (dialog, which) -> {
|
DialogInterface.OnClickListener okListener = (dialog, which) -> {
|
||||||
PanicResponder.setTriggerPackageName(getActivity());
|
PanicResponder.setTriggerPackageName(getActivity());
|
||||||
showPanicApp(PanicResponder.getTriggerPackageName(getActivity()));
|
showPanicApp(PanicResponder.getTriggerPackageName(getActivity()));
|
||||||
getActivity().setResult(Activity.RESULT_OK);
|
getActivity().setResult(RESULT_OK);
|
||||||
};
|
};
|
||||||
DialogInterface.OnClickListener cancelListener = (dialog, which) -> {
|
DialogInterface.OnClickListener cancelListener = (dialog, which) -> {
|
||||||
getActivity().setResult(Activity.RESULT_CANCELED);
|
getActivity().setResult(RESULT_CANCELED);
|
||||||
getActivity().finish();
|
getActivity().finish();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import javax.inject.Inject;
|
|||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_GROUP_INVITE;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_GROUP_INVITE;
|
||||||
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_POST_BODY_LENGTH;
|
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_POST_TEXT_LENGTH;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -179,8 +179,8 @@ public class GroupActivity extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int getMaxBodyLength() {
|
protected int getMaxTextLength() {
|
||||||
return MAX_GROUP_POST_BODY_LENGTH;
|
return MAX_GROUP_POST_TEXT_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ class GroupControllerImpl extends
|
|||||||
GroupMessageAddedEvent g = (GroupMessageAddedEvent) e;
|
GroupMessageAddedEvent g = (GroupMessageAddedEvent) e;
|
||||||
if (!g.isLocal() && g.getGroupId().equals(getGroupId())) {
|
if (!g.isLocal() && g.getGroupId().equals(getGroupId())) {
|
||||||
LOG.info("Group message received, adding...");
|
LOG.info("Group message received, adding...");
|
||||||
GroupMessageItem item = buildItem(g.getHeader(), g.getBody());
|
GroupMessageItem item = buildItem(g.getHeader(), g.getText());
|
||||||
listener.runOnUiThreadUnlessDestroyed(
|
listener.runOnUiThreadUnlessDestroyed(
|
||||||
() -> listener.onItemReceived(item));
|
() -> listener.onItemReceived(item));
|
||||||
}
|
}
|
||||||
@@ -124,13 +124,13 @@ class GroupControllerImpl extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String loadMessageBody(GroupMessageHeader header)
|
protected String loadMessageText(GroupMessageHeader header)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
if (header instanceof JoinMessageHeader) {
|
if (header instanceof JoinMessageHeader) {
|
||||||
// will be looked up later
|
// will be looked up later
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return privateGroupManager.getMessageBody(header.getId());
|
return privateGroupManager.getMessageText(header.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -159,7 +159,7 @@ class GroupControllerImpl extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createAndStoreMessage(String body,
|
public void createAndStoreMessage(String text,
|
||||||
@Nullable GroupMessageItem parentItem,
|
@Nullable GroupMessageItem parentItem,
|
||||||
ResultExceptionHandler<GroupMessageItem, DbException> handler) {
|
ResultExceptionHandler<GroupMessageItem, DbException> handler) {
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
@@ -173,7 +173,7 @@ class GroupControllerImpl extends
|
|||||||
long timestamp = count.getLatestMsgTime();
|
long timestamp = count.getLatestMsgTime();
|
||||||
if (parentItem != null) parentId = parentItem.getId();
|
if (parentItem != null) parentId = parentItem.getId();
|
||||||
timestamp = max(clock.currentTimeMillis(), timestamp + 1);
|
timestamp = max(clock.currentTimeMillis(), timestamp + 1);
|
||||||
createMessage(body, timestamp, parentId, author, previousMsgId,
|
createMessage(text, timestamp, parentId, author, previousMsgId,
|
||||||
handler);
|
handler);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
@@ -182,7 +182,7 @@ class GroupControllerImpl extends
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createMessage(String body, long timestamp,
|
private void createMessage(String text, long timestamp,
|
||||||
@Nullable MessageId parentId, LocalAuthor author,
|
@Nullable MessageId parentId, LocalAuthor author,
|
||||||
MessageId previousMsgId,
|
MessageId previousMsgId,
|
||||||
ResultExceptionHandler<GroupMessageItem, DbException> handler) {
|
ResultExceptionHandler<GroupMessageItem, DbException> handler) {
|
||||||
@@ -190,8 +190,8 @@ class GroupControllerImpl extends
|
|||||||
LOG.info("Creating group message...");
|
LOG.info("Creating group message...");
|
||||||
GroupMessage msg = groupMessageFactory
|
GroupMessage msg = groupMessageFactory
|
||||||
.createGroupMessage(getGroupId(), timestamp,
|
.createGroupMessage(getGroupId(), timestamp,
|
||||||
parentId, author, body, previousMsgId);
|
parentId, author, text, previousMsgId);
|
||||||
storePost(msg, body, handler);
|
storePost(msg, text, handler);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,11 +208,11 @@ class GroupControllerImpl extends
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected GroupMessageItem buildItem(GroupMessageHeader header,
|
protected GroupMessageItem buildItem(GroupMessageHeader header,
|
||||||
String body) {
|
String text) {
|
||||||
if (header instanceof JoinMessageHeader) {
|
if (header instanceof JoinMessageHeader) {
|
||||||
return new JoinMessageItem((JoinMessageHeader) header, body);
|
return new JoinMessageItem((JoinMessageHeader) header, text);
|
||||||
}
|
}
|
||||||
return new GroupMessageItem(header, body);
|
return new GroupMessageItem(header, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import android.support.annotation.LayoutRes;
|
|||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.Author.Status;
|
import org.briarproject.bramble.api.identity.AuthorInfo;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
@@ -22,14 +22,14 @@ class GroupMessageItem extends ThreadItem {
|
|||||||
|
|
||||||
private GroupMessageItem(MessageId messageId, GroupId groupId,
|
private GroupMessageItem(MessageId messageId, GroupId groupId,
|
||||||
@Nullable MessageId parentId, String text, long timestamp,
|
@Nullable MessageId parentId, String text, long timestamp,
|
||||||
Author author, Status status, boolean isRead) {
|
Author author, AuthorInfo status, boolean isRead) {
|
||||||
super(messageId, parentId, text, timestamp, author, status, isRead);
|
super(messageId, parentId, text, timestamp, author, status, isRead);
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupMessageItem(GroupMessageHeader h, String text) {
|
GroupMessageItem(GroupMessageHeader h, String text) {
|
||||||
this(h.getId(), h.getGroupId(), h.getParentId(), text, h.getTimestamp(),
|
this(h.getId(), h.getGroupId(), h.getParentId(), text, h.getTimestamp(),
|
||||||
h.getAuthor(), h.getAuthorStatus(), h.isRead());
|
h.getAuthor(), h.getAuthorInfo(), h.isRead());
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupId getGroupId() {
|
public GroupId getGroupId() {
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import org.briarproject.briar.R;
|
|||||||
import org.briarproject.briar.android.threaded.BaseThreadItemViewHolder;
|
import org.briarproject.briar.android.threaded.BaseThreadItemViewHolder;
|
||||||
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
|
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -36,24 +37,27 @@ class JoinMessageItemViewHolder
|
|||||||
if (item.isInitial()) {
|
if (item.isInitial()) {
|
||||||
textView.setText(R.string.groups_member_created_you);
|
textView.setText(R.string.groups_member_created_you);
|
||||||
} else {
|
} else {
|
||||||
textView.setText(
|
String name = getContactDisplayName(item.getAuthor(),
|
||||||
getContext().getString(R.string.groups_member_joined,
|
item.getAuthorInfo().getAlias());
|
||||||
item.getAuthor().getName()));
|
textView.setText(getContext()
|
||||||
|
.getString(R.string.groups_member_joined, name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bind(JoinMessageItem item) {
|
private void bind(JoinMessageItem item) {
|
||||||
Context ctx = getContext();
|
Context ctx = getContext();
|
||||||
|
String name = getContactDisplayName(item.getAuthor(),
|
||||||
|
item.getAuthorInfo().getAlias());
|
||||||
|
|
||||||
if (item.isInitial()) {
|
if (item.isInitial()) {
|
||||||
textView.setText(ctx.getString(R.string.groups_member_created,
|
textView.setText(
|
||||||
item.getAuthor().getName()));
|
ctx.getString(R.string.groups_member_created, name));
|
||||||
} else {
|
} else {
|
||||||
if (item.getStatus() == OURSELVES) {
|
if (item.getAuthorInfo().getStatus() == OURSELVES) {
|
||||||
textView.setText(R.string.groups_member_joined_you);
|
textView.setText(R.string.groups_member_joined_you);
|
||||||
} else {
|
} else {
|
||||||
textView.setText(ctx.getString(R.string.groups_member_joined,
|
textView.setText(
|
||||||
item.getAuthor().getName()));
|
ctx.getString(R.string.groups_member_joined, name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ public interface CreateGroupController
|
|||||||
ResultExceptionHandler<GroupId, DbException> result);
|
ResultExceptionHandler<GroupId, DbException> result);
|
||||||
|
|
||||||
void sendInvitation(GroupId g, Collection<ContactId> contacts,
|
void sendInvitation(GroupId g, Collection<ContactId> contacts,
|
||||||
String message, ResultExceptionHandler<Void, DbException> result);
|
String text, ResultExceptionHandler<Void, DbException> result);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user