mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Compare commits
123 Commits
1233-remot
...
tor-probe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f840d25d9 | ||
|
|
389b2b5b8e | ||
|
|
78abfa3698 | ||
|
|
e47d41596a | ||
|
|
8cf54bcedb | ||
|
|
89d5145665 | ||
|
|
0706498b03 | ||
|
|
b296500e7a | ||
|
|
60a8b03344 | ||
|
|
ae16a93522 | ||
|
|
c9a2ff71ae | ||
|
|
16f4c60a56 | ||
|
|
76121eb871 | ||
|
|
47c91a96ae | ||
|
|
14befb957b | ||
|
|
b464fe1653 | ||
|
|
09c6f09805 | ||
|
|
a93093182d | ||
|
|
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 | ||
|
|
39478a7914 | ||
|
|
112e71a9cb | ||
|
|
5650bef310 | ||
|
|
2a87171c49 | ||
|
|
071d961ed1 | ||
|
|
cb9efc5fec | ||
|
|
f9e292f734 | ||
|
|
15cb5409e7 | ||
|
|
fd07dc006d | ||
|
|
cc87c4e37d | ||
|
|
4a10e876f6 | ||
|
|
fad0057c4a | ||
|
|
5aabfcea9a | ||
|
|
f7d928c774 | ||
|
|
bd983d9796 | ||
|
|
de8d1b7d96 | ||
|
|
9155f62d0b | ||
|
|
c2b7f85b8e | ||
|
|
ae81eb3737 | ||
|
|
60d949c342 | ||
|
|
1c90e64894 | ||
|
|
f0e2d5281f |
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_EXCEPTION_COMMENTS" value="false" />
|
||||
</JavaCodeStyleSettings>
|
||||
<JetCodeStyleSettings>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<Objective-C-extensions>
|
||||
<file>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
||||
@@ -257,5 +260,11 @@
|
||||
</rules>
|
||||
</arrangement>
|
||||
</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>
|
||||
</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-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 in briar-headless" run_configuration_type="AndroidJUnit" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
</component>
|
||||
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'
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion '27.0.3'
|
||||
compileSdkVersion 28
|
||||
buildToolsVersion '28.0.3'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 26
|
||||
versionCode 10101
|
||||
versionName "1.1.1"
|
||||
versionCode 10103
|
||||
versionName "1.1.3"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
@@ -28,7 +28,7 @@ configurations {
|
||||
|
||||
dependencies {
|
||||
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'
|
||||
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package org.briarproject.bramble.plugin.tor;
|
||||
|
||||
import org.spongycastle.crypto.tls.Certificate;
|
||||
import org.spongycastle.crypto.tls.CipherSuite;
|
||||
import org.spongycastle.crypto.tls.DefaultTlsClient;
|
||||
import org.spongycastle.crypto.tls.ServerOnlyTlsAuthentication;
|
||||
import org.spongycastle.crypto.tls.TlsAuthentication;
|
||||
import org.spongycastle.crypto.tls.TlsClientProtocol;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
|
||||
public class TorProbe {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(TorProbe.class.getName());
|
||||
|
||||
private static final int READ_TIMEOUT = 10 * 1000;
|
||||
|
||||
// https://trac.torproject.org/projects/tor/wiki/org/projects/Tor/TLSHistory
|
||||
private static final int SSL3_RSA_FIPS_WITH_3DES_EDE_CBC_SHA = 0xfeff;
|
||||
|
||||
// https://gitweb.torproject.org/torspec.git/tree/tor-spec.txt#n347
|
||||
private static final int[] TOR_CIPHER_SUITES = new int[] {
|
||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
|
||||
CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
|
||||
CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
|
||||
CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA,
|
||||
CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
|
||||
CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
|
||||
CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
CipherSuite.TLS_RSA_WITH_RC4_128_MD5,
|
||||
CipherSuite.TLS_RSA_WITH_RC4_128_SHA,
|
||||
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
|
||||
CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
|
||||
CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
|
||||
SSL3_RSA_FIPS_WITH_3DES_EDE_CBC_SHA,
|
||||
CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA
|
||||
};
|
||||
|
||||
// https://gitweb.torproject.org/torspec.git/tree/tor-spec.txt#n412
|
||||
private static final byte[] VERSIONS_CELL = new byte[] {
|
||||
0x00, 0x00, // Circuit ID: 0
|
||||
0x07, // Command: Versions
|
||||
0x00, 0x06, // Payload length: 6 bytes
|
||||
0x00, 0x03, 0x00, 0x04, 0x00, 0x05 // Supported versions: 3, 4, 5
|
||||
};
|
||||
|
||||
public List<Integer> probe(String address, int port) throws IOException {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Connecting to " + address + ":" + port);
|
||||
Socket socket = new Socket(address, port);
|
||||
LOG.info("Connected");
|
||||
TlsClientProtocol client = new TlsClientProtocol(
|
||||
socket.getInputStream(), socket.getOutputStream(),
|
||||
new SecureRandom());
|
||||
client.connect(new TorTlsClient());
|
||||
LOG.info("TLS handshake succeeded");
|
||||
socket.setSoTimeout(READ_TIMEOUT);
|
||||
try {
|
||||
// Send a versions cell
|
||||
OutputStream out = client.getOutputStream();
|
||||
out.write(VERSIONS_CELL);
|
||||
out.flush();
|
||||
LOG.info("Sent versions cell");
|
||||
|
||||
// Expect a versions cell in response
|
||||
List<Integer> versions = new ArrayList<>();
|
||||
DataInputStream in = new DataInputStream(client.getInputStream());
|
||||
int circuitId = in.readUnsignedShort();
|
||||
if (circuitId != 0)
|
||||
throw new IOException("Unexpected circuit ID: " + circuitId);
|
||||
int command = in.readUnsignedByte();
|
||||
if (command != 7)
|
||||
throw new IOException("Unexpected command: " + command);
|
||||
int payloadLength = in.readUnsignedShort();
|
||||
if (payloadLength == 0 || payloadLength % 2 != 0) {
|
||||
throw new IOException("Invalid payload length: "
|
||||
+ payloadLength);
|
||||
}
|
||||
for (int i = 0; i < payloadLength / 2; i++) {
|
||||
int version = in.readUnsignedShort();
|
||||
versions.add(version);
|
||||
}
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Supported versions: " + versions);
|
||||
return versions;
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
if (args.length != 2) {
|
||||
System.err.println("Usage: TorProbe <address> <port>");
|
||||
System.exit(1);
|
||||
}
|
||||
String address = args[0];
|
||||
int port = Integer.parseInt(args[1]);
|
||||
new TorProbe().probe(address, port);
|
||||
}
|
||||
|
||||
private static class TorTlsClient extends DefaultTlsClient {
|
||||
|
||||
@Override
|
||||
public TlsAuthentication getAuthentication() {
|
||||
return new ServerOnlyTlsAuthentication() {
|
||||
@Override
|
||||
public void notifyServerCertificate(Certificate cert)
|
||||
throws IOException {
|
||||
LOG.info("Received server certificate");
|
||||
org.spongycastle.asn1.x509.Certificate[] chain =
|
||||
cert.getCertificateList();
|
||||
if (chain.length != 1)
|
||||
throw new IOException("Certificate is not self-signed");
|
||||
for (org.spongycastle.asn1.x509.Certificate c : chain) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Subject: " + c.getSubject());
|
||||
LOG.info("Issuer: " + c.getIssuer());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getCipherSuites() {
|
||||
return TOR_CIPHER_SUITES;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,42 @@
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'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:shared:26.1.3:shared-26.1.3.jar:7110706c7ada96c8b6f5ca80c478291bc7899d46277de2c48527e045442401a3',
|
||||
'com.android.tools.analytics-library:tracker:26.1.3:tracker-26.1.3.jar:4155424bf2ce4872da83332579a1707252bc66cbd77c5144fdc4483d0f2e1418',
|
||||
'com.android.tools.build:apksig:3.1.3:apksig-3.1.3.jar:7e1f8e675a6e768e5b56405e41d6c3cc05befe62e601b04177de1029902c9c89',
|
||||
'com.android.tools.build:builder-model:3.1.3:builder-model-3.1.3.jar:06ad1c422d679fc698451479cb40ba863849d67bfd1de23f6d2c16d78b024b0b',
|
||||
'com.android.tools.build:builder-test-api:3.1.3:builder-test-api-3.1.3.jar:4d989f780436794f0f8b2f50e9e079b786571eac90f26c208ab2ae6d4012f389',
|
||||
'com.android.tools.build:builder:3.1.3:builder-3.1.3.jar:8a1092012c89d0ec1ee2eff09c5708c71ef4482a6862df8d3a44a67fccace01c',
|
||||
'com.android.tools.build:gradle-api:3.1.3:gradle-api-3.1.3.jar:01e4df521456aef66514336f1d492346730dd1fb8f6433a89f62da834941ed72',
|
||||
'com.android.tools.build:manifest-merger:26.1.3:manifest-merger-26.1.3.jar:1e4fc7e932adb4607082409800e5e6fccb42e6c5360ae5990094bf522f3ada55',
|
||||
'com.android.tools.ddms:ddmlib:26.1.3:ddmlib-26.1.3.jar:c54931cd68df5d1ea2923b3b320eae47cd2307a5a916bb8674c0acf93cd1d3cd',
|
||||
'com.android.tools.external.com-intellij:intellij-core:26.1.3:intellij-core-26.1.3.jar:af67f5535fef2e1a28b1007a4acb8c5deb6a1e33b8afe7b11d012c9e778ebcec',
|
||||
'com.android.tools.external.com-intellij:kotlin-compiler:26.1.3:kotlin-compiler-26.1.3.jar:c746d2859dc11cc05c84b692b3498d3a621e0929511f8440ee009c6557838fd4',
|
||||
'com.android.tools.external.org-jetbrains:uast:26.1.3:uast-26.1.3.jar:3f3f6651d0c7685a77ecb22e9c82d6b49fdf24322c17360768dc530678f43265',
|
||||
'com.android.tools.layoutlib:layoutlib-api:26.1.3:layoutlib-api-26.1.3.jar:10bc73ce706c45629872d6a999dbe12116df64e24f47ff93b7b13121ff57b4b0',
|
||||
'com.android.tools.lint:lint-api:26.1.3:lint-api-26.1.3.jar:6f97323f9af8deda86278717885b5c927f3766757db89709f52d11d42b6fb751',
|
||||
'com.android.tools.lint:lint-checks:26.1.3:lint-checks-26.1.3.jar:73c3d53784c9ce3e6d5968506581918e0179645d20809927ca4a001dd766b001',
|
||||
'com.android.tools.lint:lint-gradle-api:26.1.3:lint-gradle-api-26.1.3.jar:7ca3c4866ec21dc21d53a9d86f752b77ace6f6c610a0c9dc877313856c733d9d',
|
||||
'com.android.tools.lint:lint-gradle:26.1.3:lint-gradle-26.1.3.jar:db0c354b8f4b6f6637e31f91c564785a59ff896325331fcbc3de7458e0b6c067',
|
||||
'com.android.tools.lint:lint-kotlin:26.1.3:lint-kotlin-26.1.3.jar:94e2b0f4565a241561cfb8fc1222bb3f132a3b98d2a90421dbb72ee8358e7d68',
|
||||
'com.android.tools.lint:lint:26.1.3:lint-26.1.3.jar:8d5f32c989c6d191d712e90ad3ca2d1c409313599551d04d834caa44d26c78df',
|
||||
'com.android.tools:annotations:26.1.3:annotations-26.1.3.jar:c950430b24ac5d58fc97e7283b8f0115f99587e76e08b4e1e2aaa780f2d77323',
|
||||
'com.android.tools:common:26.1.3:common-26.1.3.jar:7c31a90581a148ab219f615a59667f0dded7fa39b248529784474da3c2274ef2',
|
||||
'com.android.tools:dvlib:26.1.3:dvlib-26.1.3.jar:0cae87906f53d3f1088366a916ed180a7312b6d9919b90797f238875c8492855',
|
||||
'com.android.tools:repository:26.1.3:repository-26.1.3.jar:52d4539cc68db91b261e2a33b2c8206b26e05539078758dc28cfb3854adb4f59',
|
||||
'com.android.tools:sdk-common:26.1.3:sdk-common-26.1.3.jar:1948603ca9ff22c7ebb3178000bffa3a9dd2ca1cc5cb0c793cae08468b8fcfc1',
|
||||
'com.android.tools:sdklib:26.1.3:sdklib-26.1.3.jar:4adcfaad9514607098d2c51503c39811112d3050f4d1e744c01c7f08f591032b',
|
||||
'com.android.tools.analytics-library:protos:26.2.1:protos-26.2.1.jar:2f371f5b1f551e85ab08be4d6a2873471b3d44afd1ebf6aa3298f3b796bf691f',
|
||||
'com.android.tools.analytics-library:shared:26.2.1:shared-26.2.1.jar:4c1e4e705fa4d45f23aaea230557f6508155012d9c296337787c1d7b26a97f5a',
|
||||
'com.android.tools.analytics-library:tracker:26.2.1:tracker-26.2.1.jar:4a624ecc976539f755ddb0bb8dfc2dd3d08326cfec59a098dbd70f701ca7fb75',
|
||||
'com.android.tools.build:aapt2:3.2.1-4818971:aapt2-3.2.1-4818971-linux.jar:f431b6f96c91a2c155144b091a9c97d9805c589fe8efc9c930b6cd346cb60a1e',
|
||||
'com.android.tools.build:apksig:3.2.1:apksig-3.2.1.jar:2b46f2feffea66037aab29e4261b2433c190194a6ef97b958511eb157f2ccba5',
|
||||
'com.android.tools.build:apkzlib:3.2.1:apkzlib-3.2.1.jar:c39ad0313905932431fe81c8899c2cf39a4d92ad6c4edcaa4b25432f461452aa',
|
||||
'com.android.tools.build:builder-model:3.2.1:builder-model-3.2.1.jar:a9f68e6abcec122f9cb5ad352d3f05a3eb03acbcdca95e4d25c16310c2c965ff',
|
||||
'com.android.tools.build:builder-test-api:3.2.1:builder-test-api-3.2.1.jar:533ac6c2b5884bb54967a33791f2628dfdfac7981af39417a333b43d4379b6be',
|
||||
'com.android.tools.build:builder:3.2.1:builder-3.2.1.jar:aedcbfd115dbe91d09b4113e66ef50589b558d0aa3b2f133b1d867c9b87fae83',
|
||||
'com.android.tools.build:gradle-api:3.2.1:gradle-api-3.2.1.jar:57cf0ac5ac1dca8afdb3f62b94265e776e7dcfa641cc3844fb53a05193de208d',
|
||||
'com.android.tools.build:manifest-merger:26.2.1:manifest-merger-26.2.1.jar:8830573263361035d38cfdcb51e2db94029c93865b21334f5fbf8a27984281a6',
|
||||
'com.android.tools.ddms:ddmlib:26.2.1:ddmlib-26.2.1.jar:a4bf0a29a19980bf27269465cc782064656750b77c26728f82f9e148b705218b',
|
||||
'com.android.tools.external.com-intellij:intellij-core:26.2.1:intellij-core-26.2.1.jar:4925ad1892c2687cb1a63427d440ef519c8c59215fefe0dc5d541d5d411fcafe',
|
||||
'com.android.tools.external.com-intellij:kotlin-compiler:26.2.1:kotlin-compiler-26.2.1.jar:daa064fd708f340ee25fb9823c4c74104ac77f1370b76d907eb9ae6daec0a2ae',
|
||||
'com.android.tools.external.org-jetbrains:uast:26.2.1:uast-26.2.1.jar:f10f7258d2ab9189562cc0f9ad838c0378fdba439229173390a99de02ebac75b',
|
||||
'com.android.tools.layoutlib:layoutlib-api:26.2.1:layoutlib-api-26.2.1.jar:ddbf4fca123733fa011595b1cc1f4ac2937ed327b60990711fafc33c775c2ade',
|
||||
'com.android.tools.lint:lint-api:26.2.1:lint-api-26.2.1.jar:3b57e739de567b98bc9ab56c2c0ee66fc026b4adf5843e8f9804ca0666a6f66e',
|
||||
'com.android.tools.lint:lint-checks:26.2.1:lint-checks-26.2.1.jar:c86f4cc9aaee722ee4ad70062f7b5af91e9b041914af27adc09f545ab0fb3bc6',
|
||||
'com.android.tools.lint:lint-gradle-api:26.2.1:lint-gradle-api-26.2.1.jar:2283e7af32e301565f2a797e531f0fc8c648077d457afb3ffdddbee638976c2f',
|
||||
'com.android.tools.lint:lint-gradle:26.2.1:lint-gradle-26.2.1.jar:8fd90b2f3ec788cbb9801c07ab3e1ea2255aa31a6093157d7ea0ff13d0315ecb',
|
||||
'com.android.tools.lint:lint-kotlin:26.2.1:lint-kotlin-26.2.1.jar:7a6a5d2b18f69cf1b900d857c2632b4c683713c533295933b8b759f8cab4a877',
|
||||
'com.android.tools.lint:lint:26.2.1:lint-26.2.1.jar:7848b82ae988b90dee259ae7c7e86e05cbf52db6cd21c8bbd38ce7df08f3f8c5',
|
||||
'com.android.tools:annotations:26.2.1:annotations-26.2.1.jar:7391c6a1e080174b96e64ceb078dadd31ce4d8a2d2fee0ec65be202126f90f24',
|
||||
'com.android.tools:common:26.2.1:common-26.2.1.jar:a50aab2d6411ff68f4004a87c7e93d87d8e980a0ec3b352246549897ea2d78e5',
|
||||
'com.android.tools:dvlib:26.2.1:dvlib-26.2.1.jar:72a83bf2839b1df9b1fbf67ba45d1bfb9f966cd774da4320c762b2be8f1688aa',
|
||||
'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.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-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.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: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.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
|
||||
'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.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',
|
||||
'commons-codec:commons-codec:1.6:commons-codec-1.6.jar:54b34e941b8e1414bd3e40d736efd3481772dc26db3296f6aa45cec9f6203d86',
|
||||
'commons-logging:commons-logging:1.1.1:commons-logging-1.1.1.jar:ce6f913cad1f0db3aad70186d65c5bc7ffcc9a99e3fe8e0b137312819f7c362f',
|
||||
'commons-codec:commons-codec:1.9:commons-codec-1.9.jar:ad19d2601c3abf0b946b5c3a4113e226a8c1e3305e395b90013b78dd94a723ce',
|
||||
'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',
|
||||
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||
'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:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||
'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:httpcore:4.2.5:httpcore-4.2.5.jar:e5e82da4cc66c8d917bbf743e3c0752efe8522735e7fc9dbddb65bccea81cfe9',
|
||||
'org.apache.httpcomponents:httpmime:4.1:httpmime-4.1.jar:31629566148e8a47688ae43b420abc3ecd783ed15b33bebc00824bf24c9b15aa',
|
||||
'org.apache.httpcomponents:httpclient:4.5.2:httpclient-4.5.2.jar:0dffc621400d6c632f55787d996b8aeca36b30746a716e079a985f24d8074057',
|
||||
'org.apache.httpcomponents:httpcore:4.4.5:httpcore-4.4.5.jar:64d5453874cab7e40a7065cb01a9a9ca1053845a9786b478878b679e0580cec3',
|
||||
'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.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
||||
'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.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',
|
||||
@@ -70,9 +72,10 @@ dependencyVerification {
|
||||
'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.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-jre8:1.2.0:kotlin-stdlib-jre8-1.2.0.jar:633524eee6ef1941f7cb1dab7ee3927b0a221ceee9047aeb5515f4cbb990c82a',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.2.0:kotlin-stdlib-1.2.0.jar:05cfd9f5ac0b41910703a8925f7211a495909b27a2ffdd1c5106f1689aeafcd4',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71:kotlin-stdlib-common-1.2.71.jar:63999687ff2fce8a592dd180ffbbf8f1d21c26b4044c55cdc74ff3cf3b3cf328',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71:kotlin-stdlib-jdk7-1.2.71.jar:b136bd61b240e07d4d92ce00d3bd1dbf584400a7bf5f220c2f3cd22446858082',
|
||||
'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:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||
'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.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.ow2.asm:asm-analysis:5.1:asm-analysis-5.1.jar:a34658f5c5de4b573eef21131cc32cc25f7b66407944f312b28ec2e56abb1fa9',
|
||||
'org.ow2.asm:asm-commons:5.1:asm-commons-5.1.jar:97b3786e1f55e74bddf8ad102bf50e33bbcbc1f6b7fd7b36f0bbbb25cd4981be',
|
||||
'org.ow2.asm:asm-tree:5.1:asm-tree-5.1.jar:c0de2bbc4cb8297419659813ecd4ed1d077ed1dd5c1f5544cc5143e493e84c10',
|
||||
'org.ow2.asm:asm-util:5.1:asm-util-5.1.jar:ee032c39ae5e3cd099148fbba9a2124f9ed613e5cb93e03ee0fa8808ce364040',
|
||||
'org.ow2.asm:asm-analysis:6.0:asm-analysis-6.0.jar:2f1a6387219c3a6cc4856481f221b03bd9f2408a326d416af09af5d6f608c1f4',
|
||||
'org.ow2.asm:asm-commons:6.0:asm-commons-6.0.jar:f1bce5c648a96a017bdcd01fe5d59af9845297fd7b79b81c015a6fbbd9719abf',
|
||||
'org.ow2.asm:asm-tree:6.0:asm-tree-6.0.jar:887998fb69727c8759e4d253f856822801e33f9fd4caa566b3ac58ee92106215',
|
||||
'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.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) {
|
||||
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(
|
||||
"Timestamp is too far in the future");
|
||||
}
|
||||
byte[] body = m.getBody();
|
||||
if (body.length == 0) {
|
||||
throw new InvalidMessageException("Message is too short");
|
||||
}
|
||||
try {
|
||||
BdfList bodyList = clientHelper.toList(body);
|
||||
BdfList bodyList = clientHelper.toList(m.getBody());
|
||||
BdfMessageContext result = validateMessage(m, g, bodyList);
|
||||
Metadata meta = metadataEncoder.encode(result.getDictionary());
|
||||
return new MessageContext(meta, result.getDependencies());
|
||||
|
||||
@@ -76,6 +76,19 @@ public interface DatabaseComponent {
|
||||
*/
|
||||
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,
|
||||
* and returns an ID for the 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.
|
||||
* 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();
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ public interface LifecycleManager {
|
||||
*/
|
||||
enum LifecycleState {
|
||||
|
||||
STARTING, MIGRATING_DATABASE, STARTING_SERVICES, RUNNING, STOPPING;
|
||||
STARTING, MIGRATING_DATABASE, COMPACTING_DATABASE, STARTING_SERVICES,
|
||||
RUNNING, STOPPING;
|
||||
|
||||
public boolean isAfter(LifecycleState state) {
|
||||
return ordinal() > state.ordinal();
|
||||
|
||||
@@ -4,7 +4,8 @@ public interface TorConstants {
|
||||
|
||||
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 CONTROL_PORT = 59051;
|
||||
|
||||
@@ -16,6 +16,7 @@ public class Message {
|
||||
private final 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)
|
||||
throw new IllegalArgumentException();
|
||||
this.id = id;
|
||||
|
||||
@@ -153,15 +153,4 @@ public class StringUtils {
|
||||
return new String(c);
|
||||
}
|
||||
|
||||
|
||||
public static String getRandomBase32String(int length) {
|
||||
char[] c = new char[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
int character = random.nextInt(32);
|
||||
if (character < 26) c[i] = (char) ('A' + character);
|
||||
else c[i] = (char) ('2' + (character - 26));
|
||||
}
|
||||
return new String(c);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 'org.bitlet:weupnp:0.1.4'
|
||||
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'
|
||||
|
||||
apt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
|
||||
@@ -159,7 +159,8 @@ class AccountManagerImpl implements AccountManager {
|
||||
@Override
|
||||
public boolean createAccount(String name, String password) {
|
||||
synchronized (stateChangeLock) {
|
||||
// TODO don't allow creating another account if one already exists
|
||||
if (hasDatabaseKey())
|
||||
throw new AssertionError("Already have a database key");
|
||||
LocalAuthor localAuthor = identityManager.createLocalAuthor(name);
|
||||
identityManager.registerLocalAuthor(localAuthor);
|
||||
SecretKey key = crypto.generateSecretKey();
|
||||
@@ -182,6 +183,7 @@ class AccountManagerImpl implements AccountManager {
|
||||
LOG.info("Deleting account");
|
||||
IoUtils.deleteFileOrDir(databaseConfig.getDatabaseKeyDirectory());
|
||||
IoUtils.deleteFileOrDir(databaseConfig.getDatabaseDirectory());
|
||||
databaseKey = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.ContactExistsException;
|
||||
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.DbRunnable;
|
||||
import org.briarproject.bramble.api.db.Metadata;
|
||||
import org.briarproject.bramble.api.db.MigrationListener;
|
||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||
@@ -166,6 +168,31 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
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) {
|
||||
if (transaction.isCommitted()) throw new IllegalStateException();
|
||||
return txnClass.cast(transaction.unbox());
|
||||
|
||||
@@ -2,6 +2,8 @@ package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
|
||||
interface DatabaseConstants {
|
||||
|
||||
/**
|
||||
@@ -23,4 +25,16 @@ interface DatabaseConstants {
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -106,4 +107,22 @@ class H2Database extends JdbcDatabase {
|
||||
String getUrl() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,14 +61,18 @@ class HyperSqlDatabase extends JdbcDatabase {
|
||||
|
||||
@Override
|
||||
public void close() throws DbException {
|
||||
Connection c = null;
|
||||
Statement s = null;
|
||||
try {
|
||||
super.closeAllConnections();
|
||||
Connection c = createConnection();
|
||||
Statement s = c.createStatement();
|
||||
c = createConnection();
|
||||
s = c.createStatement();
|
||||
s.executeQuery("SHUTDOWN");
|
||||
s.close();
|
||||
c.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(s);
|
||||
tryToClose(c);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
@@ -104,4 +108,22 @@ class HyperSqlDatabase extends JdbcDatabase {
|
||||
String hex = StringUtils.toHexString(key.getBytes());
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,9 +67,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.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.db.DatabaseConstants.SCHEMA_VERSION_KEY;
|
||||
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.now;
|
||||
|
||||
/**
|
||||
* A generic database implementation that can be used with any JDBC-compatible
|
||||
@@ -317,9 +321,10 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
private int openConnections = 0; // Locking: connectionsLock
|
||||
private boolean closed = false; // Locking: connectionsLock
|
||||
|
||||
@Nullable
|
||||
protected abstract Connection createConnection() throws SQLException;
|
||||
|
||||
protected abstract void compactAndClose() throws DbException;
|
||||
|
||||
private final Lock connectionsLock = new ReentrantLock();
|
||||
private final Condition connectionsChanged = connectionsLock.newCondition();
|
||||
|
||||
@@ -344,13 +349,16 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
throw new DbException(e);
|
||||
}
|
||||
// Open the database and create the tables and indexes if necessary
|
||||
boolean compact;
|
||||
Connection txn = startTransaction();
|
||||
try {
|
||||
if (reopen) {
|
||||
checkSchemaVersion(txn, listener);
|
||||
Settings s = getSettings(txn, DB_SETTINGS_NAMESPACE);
|
||||
compact = migrateSchema(txn, s, listener) || isCompactionDue(s);
|
||||
} else {
|
||||
createTables(txn);
|
||||
storeSchemaVersion(txn, CODE_SCHEMA_VERSION);
|
||||
initialiseSettings(txn);
|
||||
compact = false;
|
||||
}
|
||||
createIndexes(txn);
|
||||
commitTransaction(txn);
|
||||
@@ -358,6 +366,25 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
abortTransaction(txn);
|
||||
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 +392,18 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
* version used by the current code and applies any suitable migrations to
|
||||
* 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
|
||||
* current code
|
||||
* @throws DataTooOldException if the data uses an older schema than the
|
||||
* current code and cannot be migrated
|
||||
*/
|
||||
private void checkSchemaVersion(Connection txn,
|
||||
private boolean migrateSchema(Connection txn, Settings s,
|
||||
@Nullable MigrationListener listener) throws DbException {
|
||||
Settings s = getSettings(txn, DB_SETTINGS_NAMESPACE);
|
||||
int dataSchemaVersion = s.getInt(SCHEMA_VERSION_KEY, -1);
|
||||
if (dataSchemaVersion == -1) throw new DbException();
|
||||
if (dataSchemaVersion == CODE_SCHEMA_VERSION) return;
|
||||
if (dataSchemaVersion == CODE_SCHEMA_VERSION) return false;
|
||||
if (CODE_SCHEMA_VERSION < dataSchemaVersion)
|
||||
throw new DataTooNewException();
|
||||
// Apply any suitable migrations in order
|
||||
@@ -384,7 +412,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
if (start == dataSchemaVersion) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Migrating from schema " + start + " to " + end);
|
||||
if (listener != null) listener.onMigrationRun();
|
||||
if (listener != null) listener.onDatabaseMigration();
|
||||
// Apply the migration
|
||||
m.migrate(txn);
|
||||
// Store the new schema version
|
||||
@@ -394,6 +422,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
if (dataSchemaVersion != CODE_SCHEMA_VERSION)
|
||||
throw new DataTooOldException();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Package access for testing
|
||||
@@ -401,6 +430,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
return Arrays.asList(new Migration38_39(), new Migration39_40());
|
||||
}
|
||||
|
||||
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)
|
||||
throws DbException {
|
||||
Settings s = new Settings();
|
||||
@@ -408,6 +445,19 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
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) {
|
||||
try {
|
||||
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 {
|
||||
if (s != null) s.close();
|
||||
} catch (SQLException e) {
|
||||
@@ -424,6 +474,14 @@ 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 {
|
||||
Statement s = null;
|
||||
try {
|
||||
@@ -489,7 +547,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
if (txn == null) {
|
||||
// Open a new connection
|
||||
txn = createConnection();
|
||||
if (txn == null) throw new DbException();
|
||||
txn.setAutoCommit(false);
|
||||
connectionsLock.lock();
|
||||
try {
|
||||
@@ -1508,7 +1565,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
rs.close();
|
||||
ps.close();
|
||||
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];
|
||||
System.arraycopy(raw, MESSAGE_HEADER_LENGTH, body, 0, body.length);
|
||||
return new Message(m, g, timestamp, body);
|
||||
|
||||
@@ -29,6 +29,7 @@ import javax.inject.Inject;
|
||||
import static java.util.logging.Level.FINE;
|
||||
import static java.util.logging.Level.INFO;
|
||||
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.RUNNING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING;
|
||||
@@ -159,11 +160,17 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMigrationRun() {
|
||||
public void onDatabaseMigration() {
|
||||
state = MIGRATING_DATABASE;
|
||||
eventBus.broadcast(new LifecycleEvent(MIGRATING_DATABASE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDatabaseCompaction() {
|
||||
state = COMPACTING_DATABASE;
|
||||
eventBus.broadcast(new LifecycleEvent(COMPACTING_DATABASE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopServices() {
|
||||
try {
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||
import org.briarproject.bramble.util.IoUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
|
||||
import java.io.Closeable;
|
||||
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_WITH_BRIDGES;
|
||||
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.PrivacyUtils.scrubOnion;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
@@ -87,7 +88,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
private static final String OWNER = "__OwningControllerProcess";
|
||||
private static final int COOKIE_TIMEOUT_MS = 3000;
|
||||
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 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
|
||||
String portString = settings.get(PREF_TOR_PORT);
|
||||
int port;
|
||||
if (StringUtils.isNullOrEmpty(portString)) port = 0;
|
||||
if (isNullOrEmpty(portString)) port = 0;
|
||||
else port = Integer.parseInt(portString);
|
||||
// Bind a server socket to receive connections from Tor
|
||||
ServerSocket ss = null;
|
||||
@@ -427,11 +429,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
return;
|
||||
}
|
||||
// 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))
|
||||
LOG.info("Hidden service " + scrubOnion(hostname));
|
||||
LOG.info("Hidden service " + scrubOnion(onion2));
|
||||
TransportProperties p = new TransportProperties();
|
||||
p.put(PROP_ONION, hostname);
|
||||
p.put(PROP_ONION_V2, onion2);
|
||||
callback.mergeLocalProperties(p);
|
||||
if (privKey == null) {
|
||||
// Save the hidden service's private key for next time
|
||||
@@ -530,26 +532,41 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
@Override
|
||||
public DuplexTransportConnection createConnection(TransportProperties p) {
|
||||
if (!isRunning()) return null;
|
||||
String onion = p.get(PROP_ONION);
|
||||
if (StringUtils.isNullOrEmpty(onion)) return null;
|
||||
if (!ONION.matcher(onion).matches()) {
|
||||
// not scrubbing this address, so we are able to find the problem
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Invalid hostname: " + onion);
|
||||
return null;
|
||||
String bestOnion = null;
|
||||
String onion2 = p.get(PROP_ONION_V2);
|
||||
String onion3 = p.get(PROP_ONION_V3);
|
||||
if (!isNullOrEmpty(onion2)) {
|
||||
if (ONION_V2.matcher(onion2).matches()) {
|
||||
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;
|
||||
try {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Connecting to " + scrubOnion(onion));
|
||||
s = torSocketFactory.createSocket(onion + ".onion", 80);
|
||||
LOG.info("Connecting to " + scrubOnion(bestOnion));
|
||||
s = torSocketFactory.createSocket(bestOnion + ".onion", 80);
|
||||
s.setSoTimeout(socketTimeout);
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Connected to " + scrubOnion(onion));
|
||||
LOG.info("Connected to " + scrubOnion(bestOnion));
|
||||
return new TorTransportConnection(this, s);
|
||||
} catch (IOException e) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Could not connect to " + scrubOnion(onion) + ": " +
|
||||
e.toString());
|
||||
LOG.info("Could not connect to " + scrubOnion(bestOnion)
|
||||
+ ": " + e.toString());
|
||||
}
|
||||
tryToClose(s);
|
||||
return null;
|
||||
@@ -627,6 +644,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
if (s.getNamespace().equals(ID.getString())) {
|
||||
LOG.info("Tor settings updated");
|
||||
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());
|
||||
}
|
||||
} 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) {
|
||||
connectionStatusExecutor.execute(() -> {
|
||||
if (!running) return;
|
||||
|
||||
@@ -36,6 +36,7 @@ class MessageFactoryImpl implements MessageFactory {
|
||||
|
||||
@Override
|
||||
public Message createMessage(GroupId g, long timestamp, byte[] body) {
|
||||
if (body.length == 0) throw new IllegalArgumentException();
|
||||
if (body.length > MAX_MESSAGE_BODY_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
MessageId id = getMessageId(g, timestamp, body);
|
||||
@@ -54,7 +55,7 @@ class MessageFactoryImpl implements MessageFactory {
|
||||
|
||||
@Override
|
||||
public Message createMessage(byte[] raw) {
|
||||
if (raw.length < MESSAGE_HEADER_LENGTH)
|
||||
if (raw.length <= MESSAGE_HEADER_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
if (raw.length > MAX_MESSAGE_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
@@ -124,7 +124,8 @@ class SyncRecordReaderImpl implements SyncRecordReader {
|
||||
if (!hasMessage()) throw new FormatException();
|
||||
if (nextRecord == null) throw new AssertionError();
|
||||
byte[] payload = nextRecord.getPayload();
|
||||
if (payload.length < MESSAGE_HEADER_LENGTH) throw new FormatException();
|
||||
if (payload.length <= MESSAGE_HEADER_LENGTH)
|
||||
throw new FormatException();
|
||||
// Validate timestamp
|
||||
long timestamp = ByteUtils.readUint64(payload, UniqueId.LENGTH);
|
||||
if (timestamp < 0) throw new FormatException();
|
||||
|
||||
@@ -79,18 +79,6 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
|
||||
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
|
||||
public void testAcceptsMinLengthMessage() throws Exception {
|
||||
Message shortMessage = getMessage(groupId, 1);
|
||||
|
||||
@@ -26,11 +26,10 @@ import org.briarproject.bramble.api.transport.KeySetId;
|
||||
import org.briarproject.bramble.api.transport.OutgoingKeys;
|
||||
import org.briarproject.bramble.api.transport.TransportKeys;
|
||||
import org.briarproject.bramble.system.SystemClock;
|
||||
import org.briarproject.bramble.test.ArrayClock;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.SettableClock;
|
||||
import org.briarproject.bramble.test.TestDatabaseConfig;
|
||||
import org.briarproject.bramble.test.TestMessageFactory;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -46,6 +45,7 @@ import java.util.Map.Entry;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptyMap;
|
||||
@@ -61,6 +61,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.PENDING;
|
||||
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.getAuthor;
|
||||
import static org.briarproject.bramble.test.TestUtils.getClientId;
|
||||
@@ -1818,10 +1821,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
@Test
|
||||
public void testMessageRetransmission() throws Exception {
|
||||
long now = System.currentTimeMillis();
|
||||
long steps[] = {now, now, now + MAX_LATENCY * 2 - 1,
|
||||
now + MAX_LATENCY * 2};
|
||||
AtomicLong time = new AtomicLong(now);
|
||||
Database<Connection> db =
|
||||
open(false, new TestMessageFactory(), new ArrayClock(steps));
|
||||
open(false, new TestMessageFactory(), new SettableClock(time));
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Add a contact, a shared group and a shared message
|
||||
@@ -1847,11 +1849,13 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
|
||||
// Time: now + MAX_LATENCY * 2 - 1
|
||||
// The message should not yet be sendable
|
||||
time.set(now + MAX_LATENCY * 2 - 1);
|
||||
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY);
|
||||
assertTrue(ids.isEmpty());
|
||||
|
||||
// Time: now + MAX_LATENCY * 2
|
||||
// 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);
|
||||
assertEquals(singletonList(messageId), ids);
|
||||
|
||||
@@ -1859,13 +1863,12 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
db.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testFasterMessageRetransmission() throws Exception {
|
||||
long now = System.currentTimeMillis();
|
||||
long steps[] = {now, now, now, now, now + 1};
|
||||
AtomicLong time = new AtomicLong(now);
|
||||
Database<Connection> db =
|
||||
open(false, new TestMessageFactory(), new ArrayClock(steps));
|
||||
open(false, new TestMessageFactory(), new SettableClock(time));
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Add a contact, a shared group and a shared message
|
||||
@@ -1903,6 +1906,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Time: now + 1
|
||||
// The message should no longer be sendable via the faster transport,
|
||||
// as the ETA is now equal
|
||||
time.set(now + 1);
|
||||
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE,
|
||||
MAX_LATENCY - 1);
|
||||
assertTrue(ids.isEmpty());
|
||||
@@ -1911,6 +1915,45 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
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 {
|
||||
return open(resume, new TestMessageFactory(), new SystemClock());
|
||||
@@ -1921,7 +1964,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
Database<Connection> db =
|
||||
createDatabase(new TestDatabaseConfig(testDir, MAX_SIZE),
|
||||
messageFactory, clock);
|
||||
if (!resume) TestUtils.deleteTestDirectory(testDir);
|
||||
if (!resume) deleteTestDirectory(testDir);
|
||||
db.open(key, null);
|
||||
return db;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,6 @@ dependencyVerification {
|
||||
'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: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 {
|
||||
implementation project(path: ':bramble-core', configuration: 'default')
|
||||
implementation fileTree(dir: 'libs', include: '*.jar')
|
||||
implementation 'net.java.dev.jna:jna:4.4.0'
|
||||
implementation 'net.java.dev.jna:jna-platform:4.4.0'
|
||||
tor 'org.briarproject:tor:0.2.9.16@zip'
|
||||
implementation 'net.java.dev.jna:jna:4.5.2'
|
||||
implementation 'net.java.dev.jna:jna-platform:4.5.2'
|
||||
tor 'org.briarproject:tor:0.3.4.8@zip'
|
||||
|
||||
apt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@ dependencyVerification {
|
||||
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'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:4.4.0:jna-4.4.0.jar:c4dadeeecaa90c8847902082aee5eb107fcf59c5d0e63a17fcaf273c0e2d2bd1',
|
||||
'net.java.dev.jna:jna-platform:4.5.2:jna-platform-4.5.2.jar:f1d00c167d8921c6e23c626ef9f1c3ae0be473c95c68ffa012bc7ae55a87e2d6',
|
||||
'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:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||
'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-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
||||
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
style="width:24px;height:24px"
|
||||
viewBox="0 0 24 24"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="ic_link_down.svg"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)">
|
||||
<metadata
|
||||
id="metadata10">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1020"
|
||||
id="namedview6"
|
||||
showgrid="false"
|
||||
inkscape:zoom="13.906433"
|
||||
inkscape:cx="5.1490538"
|
||||
inkscape:cy="22.945407"
|
||||
inkscape:window-x="1440"
|
||||
inkscape:window-y="24"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg4" />
|
||||
<path
|
||||
fill="#000000"
|
||||
d="M16,6H13V7.9H16C18.26,7.9 20.1,9.73 20.1,12A4.1,4.1 0 0,1 16,16.1H13V18H16A6,6 0 0,0 22,12C22,8.68 19.31,6 16,6M3.9,12C3.9,9.73 5.74,7.9 8,7.9H11V6H8A6,6 0 0,0 2,12A6,6 0 0,0 8,18H11V16.1H8C5.74,16.1 3.9,14.26 3.9,12M8,13H16V11H8V13Z"
|
||||
id="path2" />
|
||||
<path
|
||||
id="path884"
|
||||
d="m 21.659779,17.473157 h -2.295918 v 3.061224 H 17.51182 l 3.000001,3 2.999999,-3 h -1.852041 z"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#000000;stroke-width:0.38265303" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
style="width:24px;height:24px"
|
||||
viewBox="0 0 24 24"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="ic_link_up.svg"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)">
|
||||
<metadata
|
||||
id="metadata10">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1020"
|
||||
id="namedview6"
|
||||
showgrid="false"
|
||||
inkscape:zoom="13.906433"
|
||||
inkscape:cx="5.1490538"
|
||||
inkscape:cy="22.945407"
|
||||
inkscape:window-x="1440"
|
||||
inkscape:window-y="24"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg4" />
|
||||
<path
|
||||
fill="#000000"
|
||||
d="M16,6H13V7.9H16C18.26,7.9 20.1,9.73 20.1,12A4.1,4.1 0 0,1 16,16.1H13V18H16A6,6 0 0,0 22,12C22,8.68 19.31,6 16,6M3.9,12C3.9,9.73 5.74,7.9 8,7.9H11V6H8A6,6 0 0,0 2,12A6,6 0 0,0 8,18H11V16.1H8C5.74,16.1 3.9,14.26 3.9,12M8,13H16V11H8V13Z"
|
||||
id="path2" />
|
||||
<path
|
||||
id="path884"
|
||||
d="M 21.659779,23.534381 H 19.363861 V 20.473157 H 17.51182 l 3.000001,-3 2.999999,3 h -1.852041 z"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#000000;stroke-width:0.38265303" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -2,73 +2,6 @@ apply plugin: 'com.android.application'
|
||||
apply plugin: 'witness'
|
||||
apply from: 'witness.gradle'
|
||||
|
||||
// prototype
|
||||
repositories {
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(path: ':briar-core', configuration: 'default')
|
||||
implementation project(path: ':bramble-core', configuration: 'default')
|
||||
implementation project(':bramble-android')
|
||||
|
||||
def supportVersion = '28.0.0'
|
||||
implementation "com.android.support:support-v4:$supportVersion"
|
||||
implementation("com.android.support:appcompat-v7:$supportVersion") {
|
||||
exclude module: 'support-v4'
|
||||
}
|
||||
implementation("com.android.support:preference-v14:$supportVersion") {
|
||||
exclude module: 'support-v4'
|
||||
}
|
||||
implementation("com.android.support:design:$supportVersion") {
|
||||
exclude module: 'support-v4'
|
||||
exclude module: 'recyclerview-v7'
|
||||
}
|
||||
implementation "com.android.support:cardview-v7:$supportVersion"
|
||||
implementation "com.android.support:support-annotations:$supportVersion"
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||
|
||||
implementation('ch.acra:acra:4.9.1') {
|
||||
exclude module: 'support-v4'
|
||||
exclude module: 'support-annotations'
|
||||
}
|
||||
implementation 'info.guardianproject.panic:panic:0.5'
|
||||
implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
|
||||
implementation 'de.hdodenhof:circleimageview:2.2.0'
|
||||
implementation 'com.google.zxing:core:3.3.0'
|
||||
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.8.0'
|
||||
implementation 'com.vanniktech:emoji-google:0.5.1'
|
||||
|
||||
// prototype
|
||||
implementation "com.github.kobakei:MaterialFabSpeedDial:1.2.0"
|
||||
|
||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
|
||||
compileOnly 'javax.annotation:jsr250-api:1.0'
|
||||
|
||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
||||
testImplementation 'org.robolectric:robolectric:3.8'
|
||||
testImplementation 'org.robolectric:shadows-support-v4:3.3.2'
|
||||
testImplementation 'org.mockito:mockito-core:2.13.0'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation "org.jmock:jmock:2.8.2"
|
||||
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
||||
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
||||
|
||||
def espressoVersion = '3.0.2'
|
||||
androidTestImplementation "com.android.support.test.espresso:espresso-core:$espressoVersion"
|
||||
androidTestImplementation "com.android.support.test.espresso:espresso-contrib:$espressoVersion"
|
||||
androidTestImplementation "com.android.support.test.espresso:espresso-intents:$espressoVersion"
|
||||
androidTestImplementation "tools.fastlane:screengrab:1.1.0"
|
||||
androidTestImplementation "com.android.support.test.uiautomator:uiautomator-v18:2.1.3"
|
||||
androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:2.0.2"
|
||||
androidTestCompileOnly 'javax.annotation:jsr250-api:1.0'
|
||||
androidTestImplementation 'junit:junit:4.12'
|
||||
}
|
||||
|
||||
def getStdout = { command, defaultValue ->
|
||||
def stdout = new ByteArrayOutputStream()
|
||||
try {
|
||||
@@ -84,20 +17,20 @@ def getStdout = { command, defaultValue ->
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
buildToolsVersion '28.0.2'
|
||||
buildToolsVersion '28.0.3'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 26
|
||||
versionCode 10101
|
||||
versionName "1.1.1"
|
||||
versionCode 10103
|
||||
versionName "1.1.3"
|
||||
applicationId "org.briarproject.briar.android"
|
||||
buildConfigField "String", "GitHash",
|
||||
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
|
||||
def now = (long) (System.currentTimeMillis() / 1000)
|
||||
buildConfigField "Long", "BuildTimestamp",
|
||||
"${getStdout(['git', 'log', '-n', '1', '--format=%ct'], now)}000L"
|
||||
testInstrumentationRunner 'org.briarproject.briar.android.test.BriarTestRunner'
|
||||
testInstrumentationRunner 'org.briarproject.briar.android.BriarTestRunner'
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -152,6 +85,65 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(path: ':briar-core', configuration: 'default')
|
||||
implementation project(path: ':bramble-core', configuration: 'default')
|
||||
implementation project(':bramble-android')
|
||||
|
||||
def supportVersion = '28.0.0'
|
||||
implementation "com.android.support:support-v4:$supportVersion"
|
||||
implementation("com.android.support:appcompat-v7:$supportVersion") {
|
||||
exclude module: 'support-v4'
|
||||
}
|
||||
implementation("com.android.support:preference-v14:$supportVersion") {
|
||||
exclude module: 'support-v4'
|
||||
}
|
||||
implementation("com.android.support:design:$supportVersion") {
|
||||
exclude module: 'support-v4'
|
||||
exclude module: 'recyclerview-v7'
|
||||
}
|
||||
implementation "com.android.support:cardview-v7:$supportVersion"
|
||||
implementation "com.android.support:support-annotations:$supportVersion"
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||
|
||||
implementation('ch.acra:acra:4.11') {
|
||||
exclude module: 'support-v4'
|
||||
exclude module: 'support-annotations'
|
||||
}
|
||||
implementation 'info.guardianproject.panic:panic:0.5'
|
||||
implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
|
||||
implementation 'de.hdodenhof:circleimageview:2.2.0'
|
||||
implementation 'com.google.zxing:core:3.3.3'
|
||||
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.12.4'
|
||||
implementation 'com.vanniktech:emoji-google:0.5.1'
|
||||
|
||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
|
||||
compileOnly 'javax.annotation:jsr250-api:1.0'
|
||||
|
||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
||||
testImplementation 'org.robolectric:robolectric:3.8'
|
||||
testImplementation 'org.robolectric:shadows-support-v4:3.3.2'
|
||||
testImplementation 'org.mockito:mockito-core:2.13.0'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation "org.jmock:jmock:2.8.2"
|
||||
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
||||
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
||||
|
||||
def espressoVersion = '3.0.2'
|
||||
androidTestImplementation "com.android.support.test.espresso:espresso-core:$espressoVersion"
|
||||
androidTestImplementation "com.android.support.test.espresso:espresso-contrib:$espressoVersion"
|
||||
androidTestImplementation "com.android.support.test.espresso:espresso-intents:$espressoVersion"
|
||||
androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:2.0.2"
|
||||
androidTestCompileOnly 'javax.annotation:jsr250-api:1.0'
|
||||
androidTestImplementation 'junit:junit:4.12'
|
||||
androidTestScreenshotImplementation "tools.fastlane:screengrab:1.2.0"
|
||||
androidTestScreenshotImplementation "com.android.support.test.uiautomator:uiautomator-v18:2.1.3"
|
||||
}
|
||||
|
||||
task verifyTranslations {
|
||||
doLast {
|
||||
def file = project.file("src/main/res/values/arrays.xml")
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
app_package_name "org.briarproject.briar.android.screenshot.debug"
|
||||
locales ['en-US']
|
||||
use_tests_in_classes([
|
||||
'org.briarproject.briar.android.login.SetupActivityScreenshotTest',
|
||||
'org.briarproject.briar.android.settings.SettingsActivityScreenshotTest',
|
||||
])
|
||||
app_apk_path "build/outputs/apk/screenshot/debug/briar-android-screenshot-debug.apk"
|
||||
tests_apk_path "build/outputs/apk/androidTest/screenshot/debug/briar-android-screenshot-debug-androidTest.apk"
|
||||
test_instrumentation_runner "org.briarproject.briar.android.test.BriarTestRunner"
|
||||
test_instrumentation_runner "org.briarproject.briar.android.BriarTestRunner"
|
||||
reinstall_app = true
|
||||
exit_on_test_failure = true
|
||||
@@ -4,4 +4,8 @@ adb shell am broadcast -a com.android.systemui.demo -e command enter
|
||||
adb shell am broadcast -a com.android.systemui.demo -e command notifications -e visible false
|
||||
adb shell am broadcast -a com.android.systemui.demo -e command battery -e level 100
|
||||
adb shell am broadcast -a com.android.systemui.demo -e command network -e wifi show
|
||||
adb shell am broadcast -a com.android.systemui.demo -e command clock -e hhmm 1337
|
||||
adb shell am broadcast -a com.android.systemui.demo -e command clock -e hhmm 1337
|
||||
|
||||
# workaround for Android Pie hidden API Espresso bug
|
||||
adb shell settings put global hidden_api_policy_pre_p_apps 1
|
||||
adb shell settings put global hidden_api_policy_p_apps 1
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
-dontnote com.android.org.conscrypt.SSLParametersImpl
|
||||
-dontnote org.apache.harmony.xnet.provider.jsse.SSLParametersImpl
|
||||
-dontnote sun.security.ssl.SSLContextImpl
|
||||
-dontwarn org.conscrypt.OpenSSLProvider
|
||||
-dontwarn org.conscrypt.Conscrypt
|
||||
|
||||
# HTML sanitiser
|
||||
-keep class org.jsoup.safety.Whitelist
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
-keep class junit.** { *; }
|
||||
-dontwarn junit.**
|
||||
|
||||
-dontwarn org.briarproject.briar.android.BriarTestComponentApplication
|
||||
-dontwarn org.briarproject.briar.android.**
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package org.briarproject.briar.android.test;
|
||||
package org.briarproject.briar.android;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.support.test.runner.AndroidJUnitRunner;
|
||||
|
||||
import org.briarproject.briar.android.BriarTestComponentApplication;
|
||||
|
||||
public class BriarTestRunner extends AndroidJUnitRunner {
|
||||
|
||||
@Override
|
||||
@@ -13,8 +11,8 @@ public class BriarTestRunner extends AndroidJUnitRunner {
|
||||
Context context)
|
||||
throws InstantiationException, IllegalAccessException,
|
||||
ClassNotFoundException {
|
||||
return super.newApplication(cl, BriarTestComponentApplication.class.getName(),
|
||||
context);
|
||||
return super.newApplication(cl,
|
||||
BriarTestComponentApplication.class.getName(), context);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.briarproject.briar.android;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.support.test.espresso.intent.rule.IntentsTestRule;
|
||||
|
||||
import org.briarproject.bramble.api.account.AccountManager;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.support.test.InstrumentationRegistry.getTargetContext;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public abstract class UiTest {
|
||||
|
||||
protected final String USERNAME =
|
||||
getTargetContext().getString(R.string.screenshot_alice);
|
||||
protected static final String PASSWORD = "123456";
|
||||
|
||||
@Inject
|
||||
protected AccountManager accountManager;
|
||||
@Inject
|
||||
protected LifecycleManager lifecycleManager;
|
||||
|
||||
public UiTest() {
|
||||
BriarTestComponentApplication app =
|
||||
(BriarTestComponentApplication) getTargetContext()
|
||||
.getApplicationContext();
|
||||
inject((BriarUiTestComponent) app.getApplicationComponent());
|
||||
}
|
||||
|
||||
protected abstract void inject(BriarUiTestComponent component);
|
||||
|
||||
@NotNullByDefault
|
||||
protected class CleanAccountTestRule<A extends Activity>
|
||||
extends IntentsTestRule<A> {
|
||||
|
||||
@Nullable
|
||||
private final Runnable runnable;
|
||||
|
||||
public CleanAccountTestRule(Class<A> activityClass) {
|
||||
super(activityClass);
|
||||
this.runnable = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this if you need to run code before launching the activity.
|
||||
* Note: You need to use {@link #launchActivity(Intent)} yourself
|
||||
* to start the activity.
|
||||
*/
|
||||
public CleanAccountTestRule(Class<A> activityClass, Runnable runnable) {
|
||||
super(activityClass, false, false);
|
||||
this.runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeActivityLaunched() {
|
||||
super.beforeActivityLaunched();
|
||||
accountManager.deleteAccount();
|
||||
accountManager.createAccount(USERNAME, PASSWORD);
|
||||
if (runnable != null) {
|
||||
Intent serviceIntent =
|
||||
new Intent(getTargetContext(), BriarService.class);
|
||||
getTargetContext().startService(serviceIntent);
|
||||
try {
|
||||
lifecycleManager.waitForStartup();
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.briar.android.test;
|
||||
package org.briarproject.briar.android;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.support.test.espresso.PerformException;
|
||||
@@ -1,73 +0,0 @@
|
||||
package org.briarproject.briar.android.test;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.support.test.espresso.intent.rule.IntentsTestRule;
|
||||
import android.util.Log;
|
||||
|
||||
import org.briarproject.bramble.api.account.AccountManager;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.briar.android.BriarTestComponentApplication;
|
||||
import org.briarproject.briar.android.BriarUiTestComponent;
|
||||
import org.junit.ClassRule;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import tools.fastlane.screengrab.Screengrab;
|
||||
import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy;
|
||||
import tools.fastlane.screengrab.locale.LocaleTestRule;
|
||||
|
||||
import static android.support.test.InstrumentationRegistry.getTargetContext;
|
||||
import static tools.fastlane.screengrab.Screengrab.setDefaultScreenshotStrategy;
|
||||
|
||||
public abstract class ScreenshotTest {
|
||||
|
||||
@ClassRule
|
||||
public static final LocaleTestRule localeTestRule = new LocaleTestRule();
|
||||
|
||||
protected static final String USERNAME = "Alice";
|
||||
protected static final String PASSWORD = "123456";
|
||||
|
||||
@Inject
|
||||
protected AccountManager accountManager;
|
||||
@Inject
|
||||
protected LifecycleManager lifecycleManager;
|
||||
|
||||
public ScreenshotTest() {
|
||||
super();
|
||||
setDefaultScreenshotStrategy(new UiAutomatorScreenshotStrategy());
|
||||
BriarTestComponentApplication app =
|
||||
(BriarTestComponentApplication) getTargetContext()
|
||||
.getApplicationContext();
|
||||
inject((BriarUiTestComponent) app.getApplicationComponent());
|
||||
}
|
||||
|
||||
protected abstract void inject(BriarUiTestComponent component);
|
||||
|
||||
protected void screenshot(String name) {
|
||||
try {
|
||||
Screengrab.screenshot(name);
|
||||
} catch (RuntimeException e) {
|
||||
if (!e.getMessage().equals("Unable to capture screenshot."))
|
||||
throw e;
|
||||
// The tests should still pass when run from AndroidStudio
|
||||
// without manually granting permissions like fastlane does.
|
||||
Log.w("Screengrab", "Permission to write screenshot is missing.");
|
||||
}
|
||||
}
|
||||
|
||||
protected class CleanAccountTestRule<A extends Activity>
|
||||
extends IntentsTestRule<A> {
|
||||
|
||||
public CleanAccountTestRule(Class<A> activityClass) {
|
||||
super(activityClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeActivityLaunched() {
|
||||
super.beforeActivityLaunched();
|
||||
accountManager.deleteAccount();
|
||||
accountManager.createAccount(USERNAME, PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.briarproject.briar.android;
|
||||
|
||||
import org.briarproject.bramble.BrambleAndroidModule;
|
||||
import org.briarproject.bramble.BrambleCoreModule;
|
||||
import org.briarproject.bramble.account.BriarAccountModule;
|
||||
import org.briarproject.briar.BriarCoreModule;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivityTest;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
AppModule.class,
|
||||
BriarCoreModule.class,
|
||||
BrambleAndroidModule.class,
|
||||
BriarAccountModule.class,
|
||||
BrambleCoreModule.class
|
||||
})
|
||||
public interface BriarUiTestComponent extends AndroidComponent {
|
||||
|
||||
void inject(NavDrawerActivityTest test);
|
||||
|
||||
}
|
||||
@@ -6,8 +6,8 @@ import android.view.Gravity;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.BriarUiTestComponent;
|
||||
import org.briarproject.briar.android.UiTest;
|
||||
import org.briarproject.briar.android.settings.SettingsActivity;
|
||||
import org.briarproject.briar.android.test.ScreenshotTest;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -23,7 +23,7 @@ import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class NavDrawerActivityTest extends ScreenshotTest {
|
||||
public class NavDrawerActivityTest extends UiTest {
|
||||
|
||||
@Rule
|
||||
public CleanAccountTestRule<NavDrawerActivity> testRule =
|
||||
@@ -4,8 +4,7 @@ import org.briarproject.bramble.BrambleAndroidModule;
|
||||
import org.briarproject.bramble.BrambleCoreModule;
|
||||
import org.briarproject.bramble.account.BriarAccountModule;
|
||||
import org.briarproject.briar.BriarCoreModule;
|
||||
import org.briarproject.briar.android.login.SetupActivityScreenshotTest;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivityTest;
|
||||
import org.briarproject.briar.android.contact.ConversationActivityScreenshotTest;
|
||||
import org.briarproject.briar.android.settings.SettingsActivityScreenshotTest;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
@@ -22,8 +21,9 @@ import dagger.Component;
|
||||
})
|
||||
public interface BriarUiTestComponent extends AndroidComponent {
|
||||
|
||||
void inject(SetupActivityScreenshotTest test);
|
||||
void inject(NavDrawerActivityTest test);
|
||||
void inject(SetupDataTest test);
|
||||
|
||||
void inject(ConversationActivityScreenshotTest test);
|
||||
void inject(SettingsActivityScreenshotTest test);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.briarproject.briar.android;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.util.Log;
|
||||
|
||||
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.briar.api.test.TestDataCreator;
|
||||
import org.junit.ClassRule;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import tools.fastlane.screengrab.FalconScreenshotStrategy;
|
||||
import tools.fastlane.screengrab.Screengrab;
|
||||
import tools.fastlane.screengrab.locale.LocaleTestRule;
|
||||
|
||||
public abstract class ScreenshotTest extends UiTest {
|
||||
|
||||
@ClassRule
|
||||
public static final LocaleTestRule localeTestRule = new LocaleTestRule();
|
||||
|
||||
@Inject
|
||||
protected TestDataCreator testDataCreator;
|
||||
@Inject
|
||||
protected ConnectionRegistry connectionRegistry;
|
||||
@Inject
|
||||
protected Clock clock;
|
||||
|
||||
protected void screenshot(String name, Activity activity) {
|
||||
try {
|
||||
Screengrab.screenshot(name, new FalconScreenshotStrategy(activity));
|
||||
} catch (RuntimeException e) {
|
||||
if (!e.getMessage().equals("Unable to capture screenshot."))
|
||||
throw e;
|
||||
// The tests should still pass when run from AndroidStudio
|
||||
// without manually granting permissions like fastlane does.
|
||||
Log.w("Screengrab", "Permission to write screenshot is missing.");
|
||||
}
|
||||
}
|
||||
|
||||
protected long getMinutesAgo(int minutes) {
|
||||
return clock.currentTimeMillis() - minutes * 60 * 1000;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.briar.android.login;
|
||||
package org.briarproject.briar.android;
|
||||
|
||||
import android.support.test.espresso.intent.rule.IntentsTestRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
@@ -6,9 +6,12 @@ import android.support.test.uiautomator.UiDevice;
|
||||
import android.support.test.uiautomator.UiObject;
|
||||
import android.support.test.uiautomator.UiSelector;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.BriarUiTestComponent;
|
||||
import org.briarproject.briar.android.test.ScreenshotTest;
|
||||
import org.briarproject.briar.android.login.OpenDatabaseActivity;
|
||||
import org.briarproject.briar.android.login.SetupActivity;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -26,14 +29,14 @@ import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static android.support.test.runner.lifecycle.Stage.PAUSED;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.briarproject.briar.android.test.ViewActions.waitForActivity;
|
||||
import static org.briarproject.briar.android.test.ViewActions.waitUntilMatches;
|
||||
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
|
||||
import static org.briarproject.briar.android.ViewActions.waitForActivity;
|
||||
import static org.briarproject.briar.android.ViewActions.waitUntilMatches;
|
||||
import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SetupActivityScreenshotTest extends ScreenshotTest {
|
||||
public class SetupDataTest extends ScreenshotTest {
|
||||
|
||||
@Rule
|
||||
public IntentsTestRule<SetupActivity> testRule =
|
||||
@@ -61,7 +64,7 @@ public class SetupActivityScreenshotTest extends ScreenshotTest {
|
||||
onView(withId(R.id.nickname_entry))
|
||||
.perform(waitUntilMatches(withText(USERNAME)));
|
||||
|
||||
screenshot("manual_create_account");
|
||||
screenshot("manual_create_account", testRule.getActivity());
|
||||
|
||||
onView(withId(R.id.next))
|
||||
.check(matches(isDisplayed()))
|
||||
@@ -94,13 +97,54 @@ public class SetupActivityScreenshotTest extends ScreenshotTest {
|
||||
}
|
||||
|
||||
// wait for OpenDatabaseActivity to show up
|
||||
onView(withId(R.id.progress))
|
||||
.check(matches(isDisplayed()));
|
||||
onView(isRoot())
|
||||
.perform(waitForActivity(testRule.getActivity(), PAUSED));
|
||||
intended(hasComponent(OpenDatabaseActivity.class.getName()));
|
||||
|
||||
assertTrue(accountManager.hasDatabaseKey());
|
||||
|
||||
lifecycleManager.waitForStartup();
|
||||
createTestData();
|
||||
|
||||
// close expiry warning
|
||||
onView(withId(R.id.expiryWarning))
|
||||
.perform(waitUntilMatches(isDisplayed()));
|
||||
onView(withId(R.id.expiryWarningClose))
|
||||
.check(matches(isDisplayed()));
|
||||
onView(withId(R.id.expiryWarningClose))
|
||||
.perform(click());
|
||||
}
|
||||
|
||||
private void createTestData() {
|
||||
try {
|
||||
createTestDataExceptions();
|
||||
} catch (DbException | FormatException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void createTestDataExceptions()
|
||||
throws DbException, FormatException {
|
||||
String bobName =
|
||||
getTargetContext().getString(R.string.screenshot_bob);
|
||||
Contact bob = testDataCreator.addContact(bobName);
|
||||
|
||||
String bobHi = getTargetContext()
|
||||
.getString(R.string.screenshot_message_1);
|
||||
long bobTime = getMinutesAgo(2);
|
||||
testDataCreator.addPrivateMessage(bob, bobHi, bobTime, true);
|
||||
|
||||
String aliceHi = getTargetContext()
|
||||
.getString(R.string.screenshot_message_2);
|
||||
long aliceTime = getMinutesAgo(1);
|
||||
testDataCreator.addPrivateMessage(bob, aliceHi, aliceTime, false);
|
||||
|
||||
String bobHi2 = getTargetContext()
|
||||
.getString(R.string.screenshot_message_3);
|
||||
long bobTime2 = getMinutesAgo(0);
|
||||
testDataCreator.addPrivateMessage(bob, bobHi2, bobTime2, true);
|
||||
|
||||
connectionRegistry.registerConnection(bob.getId(), ID, true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.briarproject.briar.android.contact;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.BriarUiTestComponent;
|
||||
import org.briarproject.briar.android.ScreenshotTest;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static android.support.test.InstrumentationRegistry.getInstrumentation;
|
||||
import static android.support.test.espresso.Espresso.onView;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.briarproject.briar.android.ViewActions.waitUntilMatches;
|
||||
import static org.briarproject.briar.android.contact.ConversationActivity.CONTACT_ID;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ConversationActivityScreenshotTest extends ScreenshotTest {
|
||||
|
||||
@Rule
|
||||
public ActivityTestRule<ConversationActivity> testRule =
|
||||
new ActivityTestRule<>(ConversationActivity.class, false, false);
|
||||
|
||||
@Override
|
||||
protected void inject(BriarUiTestComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void messaging() throws Exception {
|
||||
Context targetContext = getInstrumentation().getTargetContext();
|
||||
Intent intent = new Intent(targetContext, ConversationActivity.class);
|
||||
intent.putExtra(CONTACT_ID, 1);
|
||||
testRule.launchActivity(intent);
|
||||
|
||||
onView(withId(R.id.conversationView))
|
||||
.perform(waitUntilMatches(allOf(
|
||||
withText(R.string.screenshot_message_3),
|
||||
isCompletelyDisplayed())
|
||||
));
|
||||
|
||||
screenshot("manual_messaging", testRule.getActivity());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,14 +2,15 @@ package org.briarproject.briar.android.settings;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.test.espresso.contrib.DrawerActions;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.Gravity;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.BriarUiTestComponent;
|
||||
import org.briarproject.briar.android.ScreenshotTest;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
||||
import org.briarproject.briar.android.test.ScreenshotTest;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -26,15 +27,15 @@ import static android.support.test.espresso.matcher.ViewMatchers.withChild;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static android.support.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.briarproject.briar.android.test.ViewActions.waitUntilMatches;
|
||||
import static org.briarproject.briar.android.ViewActions.waitUntilMatches;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SettingsActivityScreenshotTest extends ScreenshotTest {
|
||||
|
||||
@Rule
|
||||
public CleanAccountTestRule<SettingsActivity> testRule =
|
||||
new CleanAccountTestRule<>(SettingsActivity.class);
|
||||
public ActivityTestRule<SettingsActivity> testRule =
|
||||
new ActivityTestRule<>(SettingsActivity.class);
|
||||
|
||||
@Override
|
||||
protected void inject(BriarUiTestComponent component) {
|
||||
@@ -46,7 +47,7 @@ public class SettingsActivityScreenshotTest extends ScreenshotTest {
|
||||
onView(withText(R.string.settings_button))
|
||||
.check(matches(isDisplayed()));
|
||||
|
||||
screenshot("manual_dark_theme_settings");
|
||||
screenshot("manual_dark_theme_settings", testRule.getActivity());
|
||||
|
||||
// switch to dark theme
|
||||
onView(withText(R.string.pref_theme_title))
|
||||
@@ -56,10 +57,20 @@ public class SettingsActivityScreenshotTest extends ScreenshotTest {
|
||||
.check(matches(isDisplayed()))
|
||||
.perform(click());
|
||||
|
||||
// open nav drawer and remove expiry warning
|
||||
openNavDrawer(true);
|
||||
openNavDrawer();
|
||||
|
||||
screenshot("manual_dark_theme_nav_drawer");
|
||||
screenshot("manual_dark_theme_nav_drawer", testRule.getActivity());
|
||||
|
||||
// switch to back to light theme
|
||||
onView(withText(R.string.settings_button))
|
||||
.check(matches(isDisplayed()))
|
||||
.perform(click());
|
||||
onView(withText(R.string.pref_theme_title))
|
||||
.check(matches(isDisplayed()))
|
||||
.perform(click());
|
||||
onView(withText(R.string.pref_theme_light))
|
||||
.check(matches(isDisplayed()))
|
||||
.perform(click());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -83,12 +94,11 @@ public class SettingsActivityScreenshotTest extends ScreenshotTest {
|
||||
.check(matches(isDisplayed()))
|
||||
.check(matches(isEnabled()));
|
||||
|
||||
screenshot("manual_app_lock");
|
||||
screenshot("manual_app_lock", testRule.getActivity());
|
||||
|
||||
// no more expiry warning to remove, because sharedprefs cached?
|
||||
openNavDrawer(false);
|
||||
openNavDrawer();
|
||||
|
||||
screenshot("manual_app_lock_nav_drawer");
|
||||
screenshot("manual_app_lock_nav_drawer", testRule.getActivity());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -104,23 +114,15 @@ public class SettingsActivityScreenshotTest extends ScreenshotTest {
|
||||
.check(matches(isDisplayed()))
|
||||
.perform(waitUntilMatches(isEnabled()));
|
||||
|
||||
screenshot("manual_tor_settings");
|
||||
screenshot("manual_tor_settings", testRule.getActivity());
|
||||
}
|
||||
|
||||
private void openNavDrawer(boolean expiry) {
|
||||
private void openNavDrawer() {
|
||||
// start main activity
|
||||
Intent i =
|
||||
new Intent(testRule.getActivity(), NavDrawerActivity.class);
|
||||
testRule.getActivity().startActivity(i);
|
||||
|
||||
// close expiry warning
|
||||
if (expiry) {
|
||||
onView(withId(R.id.expiryWarningClose))
|
||||
.check(matches(isDisplayed()));
|
||||
onView(withId(R.id.expiryWarningClose))
|
||||
.perform(click());
|
||||
}
|
||||
|
||||
// open navigation drawer
|
||||
onView(withId(R.id.drawer_layout))
|
||||
.check(matches(isClosed(Gravity.START)))
|
||||
@@ -1,8 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
package="org.briarproject.briar"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||
@@ -28,8 +27,7 @@
|
||||
android:label="@string/app_name"
|
||||
android:logo="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/BriarTheme"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
android:theme="@style/BriarTheme">
|
||||
|
||||
<receiver
|
||||
android:name="org.briarproject.briar.android.login.SignInReminderReceiver"
|
||||
@@ -389,7 +387,7 @@
|
||||
<activity
|
||||
android:name="org.briarproject.briar.android.panic.PanicResponderActivity"
|
||||
android:noHistory="true"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar">
|
||||
<!-- this can never have launchMode singleTask or singleInstance! -->
|
||||
<intent-filter>
|
||||
<action android:name="info.guardianproject.panic.action.TRIGGER"/>
|
||||
@@ -399,12 +397,12 @@
|
||||
|
||||
<activity
|
||||
android:name="org.briarproject.briar.android.logout.ExitActivity"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar">
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.logout.HideUiActivity"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar">
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
@@ -413,34 +411,5 @@
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/BriarTheme.NoActionBar"/>
|
||||
|
||||
<!-- Prototype -->
|
||||
<activity
|
||||
android:name=".android.contact.ContactInviteOutputActivity"
|
||||
android:theme="@style/BriarTheme"
|
||||
android:label="@string/send_link_title"/>
|
||||
|
||||
<activity
|
||||
android:name=".android.contact.ContactInviteInputActivity"
|
||||
android:theme="@style/BriarTheme"
|
||||
android:label="@string/open_link_title"
|
||||
android:windowSoftInputMode="stateHidden|adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="briar"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.contact.PendingRequestsActivity"
|
||||
android:label="@string/pending_contact_requests"
|
||||
android:theme="@style/BriarTheme"/>
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -15,17 +15,9 @@ import org.briarproject.briar.android.blog.ReblogFragment;
|
||||
import org.briarproject.briar.android.blog.RssFeedImportActivity;
|
||||
import org.briarproject.briar.android.blog.RssFeedManageActivity;
|
||||
import org.briarproject.briar.android.blog.WriteBlogPostActivity;
|
||||
import org.briarproject.briar.android.contact.ContactAliasInputFragment;
|
||||
import org.briarproject.briar.android.contact.ContactInviteInputActivity;
|
||||
import org.briarproject.briar.android.contact.ContactInviteOutputActivity;
|
||||
import org.briarproject.briar.android.contact.ContactLinkInputFragment;
|
||||
import org.briarproject.briar.android.contact.ContactLinkOutputFragment;
|
||||
import org.briarproject.briar.android.contact.ContactListFragment;
|
||||
import org.briarproject.briar.android.contact.ContactModule;
|
||||
import org.briarproject.briar.android.contact.ContactQrCodeInputFragment;
|
||||
import org.briarproject.briar.android.contact.ContactQrCodeOutputFragment;
|
||||
import org.briarproject.briar.android.contact.ConversationActivity;
|
||||
import org.briarproject.briar.android.contact.PendingRequestsActivity;
|
||||
import org.briarproject.briar.android.forum.CreateForumActivity;
|
||||
import org.briarproject.briar.android.forum.ForumActivity;
|
||||
import org.briarproject.briar.android.forum.ForumListFragment;
|
||||
@@ -175,15 +167,6 @@ public interface ActivityComponent {
|
||||
|
||||
void inject(UnlockActivity activity);
|
||||
|
||||
void inject(ContactInviteOutputActivity activity);
|
||||
void inject(ContactInviteInputActivity activity);
|
||||
void inject(PendingRequestsActivity activity);
|
||||
void inject(ContactLinkOutputFragment activity);
|
||||
void inject(ContactQrCodeOutputFragment activity);
|
||||
void inject(ContactLinkInputFragment activity);
|
||||
void inject(ContactQrCodeInputFragment activity);
|
||||
void inject(ContactAliasInputFragment activity);
|
||||
|
||||
// Fragments
|
||||
void inject(AuthorNameFragment fragment);
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.briarproject.briar.android.controller.ActivityLifecycleController;
|
||||
import org.briarproject.briar.android.forum.ForumModule;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
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.widget.TapSafeFrameLayout;
|
||||
import org.briarproject.briar.android.widget.TapSafeFrameLayout.OnTapFilteredListener;
|
||||
@@ -37,11 +38,16 @@ import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.arch.lifecycle.Lifecycle.State.STARTED;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
||||
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
||||
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
|
||||
implements DestroyableContext, OnTapFilteredListener {
|
||||
|
||||
@@ -143,6 +149,7 @@ public abstract class BaseActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
public void showNextFragment(BaseFragment f) {
|
||||
if (!getLifecycle().getCurrentState().isAtLeast(STARTED)) return;
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.setCustomAnimations(R.anim.step_next_in,
|
||||
R.anim.step_previous_out, R.anim.step_previous_in,
|
||||
|
||||
@@ -51,7 +51,7 @@ abstract class BaseControllerImpl extends DbControllerImpl
|
||||
protected final IdentityManager identityManager;
|
||||
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 =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
@@ -129,17 +129,17 @@ abstract class BaseControllerImpl extends DbControllerImpl
|
||||
public void loadBlogPost(BlogPostHeader header,
|
||||
ResultExceptionHandler<BlogPostItem, DbException> handler) {
|
||||
|
||||
String body = bodyCache.get(header.getId());
|
||||
if (body != null) {
|
||||
LOG.info("Loaded body from cache");
|
||||
handler.onResult(new BlogPostItem(header, body));
|
||||
String text = textCache.get(header.getId());
|
||||
if (text != null) {
|
||||
LOG.info("Loaded text from cache");
|
||||
handler.onResult(new BlogPostItem(header, text));
|
||||
return;
|
||||
}
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
long start = now();
|
||||
BlogPostItem item = getItem(header);
|
||||
logDuration(LOG, "Loading body", start);
|
||||
logDuration(LOG, "Loading text", start);
|
||||
handler.onResult(item);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
@@ -200,28 +200,28 @@ abstract class BaseControllerImpl extends DbControllerImpl
|
||||
|
||||
@DatabaseExecutor
|
||||
private BlogPostItem getItem(BlogPostHeader h) throws DbException {
|
||||
String body;
|
||||
String text;
|
||||
if (h instanceof BlogCommentHeader) {
|
||||
BlogCommentHeader c = (BlogCommentHeader) h;
|
||||
BlogCommentItem item = new BlogCommentItem(c);
|
||||
body = getPostBody(item.getPostHeader().getId());
|
||||
item.setBody(body);
|
||||
text = getPostText(item.getPostHeader().getId());
|
||||
item.setText(text);
|
||||
return item;
|
||||
} else {
|
||||
body = getPostBody(h.getId());
|
||||
return new BlogPostItem(h, body);
|
||||
text = getPostText(h.getId());
|
||||
return new BlogPostItem(h, text);
|
||||
}
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
private String getPostBody(MessageId m) throws DbException {
|
||||
String body = bodyCache.get(m);
|
||||
if (body == null) {
|
||||
body = HtmlUtils.clean(blogManager.getPostBody(m), ARTICLE);
|
||||
bodyCache.put(m, body);
|
||||
private String getPostText(MessageId m) throws DbException {
|
||||
String text = textCache.get(m);
|
||||
if (text == null) {
|
||||
text = HtmlUtils.clean(blogManager.getPostText(m), ARTICLE);
|
||||
textCache.put(m, text);
|
||||
}
|
||||
//noinspection ConstantConditions
|
||||
return body;
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,8 +34,8 @@ class BlogCommentItem extends BlogPostItem {
|
||||
}
|
||||
}
|
||||
|
||||
public void setBody(String body) {
|
||||
this.body = body;
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,12 +15,12 @@ import javax.annotation.concurrent.NotThreadSafe;
|
||||
public class BlogPostItem implements Comparable<BlogPostItem> {
|
||||
|
||||
private final BlogPostHeader header;
|
||||
protected String body;
|
||||
protected String text;
|
||||
private boolean read;
|
||||
|
||||
BlogPostItem(BlogPostHeader header, @Nullable String body) {
|
||||
BlogPostItem(BlogPostHeader header, @Nullable String text) {
|
||||
this.header = header;
|
||||
this.body = body;
|
||||
this.text = text;
|
||||
this.read = header.isRead();
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ public class BlogPostItem implements Comparable<BlogPostItem> {
|
||||
return header.getAuthorStatus();
|
||||
}
|
||||
|
||||
public String getBody() {
|
||||
return body;
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public boolean isRssFeed() {
|
||||
|
||||
@@ -41,7 +41,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
||||
private final AuthorView reblogger;
|
||||
private final AuthorView author;
|
||||
private final ImageButton reblogButton;
|
||||
private final TextView body;
|
||||
private final TextView text;
|
||||
private final ViewGroup commentContainer;
|
||||
private final boolean fullText;
|
||||
|
||||
@@ -63,7 +63,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
||||
reblogger = v.findViewById(R.id.rebloggerView);
|
||||
author = v.findViewById(R.id.authorView);
|
||||
reblogButton = v.findViewById(R.id.commentView);
|
||||
body = v.findViewById(R.id.bodyView);
|
||||
text = v.findViewById(R.id.textView);
|
||||
commentContainer = v.findViewById(R.id.commentContainer);
|
||||
}
|
||||
|
||||
@@ -111,17 +111,17 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
||||
author.setAuthorNotClickable();
|
||||
}
|
||||
|
||||
// post body
|
||||
Spanned bodyText = getSpanned(item.getBody());
|
||||
// post text
|
||||
Spanned postText = getSpanned(item.getText());
|
||||
if (fullText) {
|
||||
body.setText(bodyText);
|
||||
body.setTextIsSelectable(true);
|
||||
makeLinksClickable(body, fragmentManager);
|
||||
text.setText(postText);
|
||||
text.setTextIsSelectable(true);
|
||||
makeLinksClickable(text, fragmentManager);
|
||||
} else {
|
||||
body.setTextIsSelectable(false);
|
||||
if (bodyText.length() > TEASER_LENGTH)
|
||||
bodyText = getTeaser(ctx, bodyText);
|
||||
body.setText(bodyText);
|
||||
text.setTextIsSelectable(false);
|
||||
if (postText.length() > TEASER_LENGTH)
|
||||
postText = getTeaser(ctx, postText);
|
||||
text.setText(postText);
|
||||
}
|
||||
|
||||
// reblog button
|
||||
@@ -163,15 +163,15 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
||||
commentContainer, false);
|
||||
|
||||
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.setAuthorStatus(c.getAuthorStatus());
|
||||
author.setDate(c.getTimestamp());
|
||||
// TODO make author clickable #624
|
||||
|
||||
body.setText(c.getComment());
|
||||
if (fullText) body.setTextIsSelectable(true);
|
||||
text.setText(c.getComment());
|
||||
if (fullText) text.setTextIsSelectable(true);
|
||||
|
||||
commentContainer.addView(v);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
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
|
||||
implements OnEditorActionListener, TextInputListener {
|
||||
@@ -70,7 +70,7 @@ public class WriteBlogPostActivity extends BriarActivity
|
||||
|
||||
setContentView(R.layout.activity_write_blog_post);
|
||||
|
||||
input = findViewById(R.id.bodyInput);
|
||||
input = findViewById(R.id.textInput);
|
||||
input.setSendButtonEnabled(false);
|
||||
input.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
@@ -132,23 +132,23 @@ public class WriteBlogPostActivity extends BriarActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendClick(String body) {
|
||||
public void onSendClick(String text) {
|
||||
// hide publish button, show progress bar
|
||||
input.hideSoftKeyboard();
|
||||
input.setVisibility(GONE);
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
|
||||
body = StringUtils.truncateUtf8(body, MAX_BLOG_POST_BODY_LENGTH);
|
||||
storePost(body);
|
||||
text = StringUtils.truncateUtf8(text, MAX_BLOG_POST_TEXT_LENGTH);
|
||||
storePost(text);
|
||||
}
|
||||
|
||||
private void storePost(String body) {
|
||||
private void storePost(String text) {
|
||||
runOnDbThread(() -> {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
try {
|
||||
LocalAuthor author = identityManager.getLocalAuthor();
|
||||
BlogPost p = blogPostFactory
|
||||
.createBlogPost(groupId, timestamp, null, author, body);
|
||||
.createBlogPost(groupId, timestamp, null, author, text);
|
||||
blogManager.addLocalPost(p);
|
||||
postPublished();
|
||||
} catch (DbException | GeneralSecurityException
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
package org.briarproject.briar.android.contact;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
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.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute;
|
||||
|
||||
@NotNullByDefault
|
||||
public class ContactAliasInputFragment extends BaseFragment
|
||||
implements TextWatcher {
|
||||
|
||||
private EditText contactNameInput;
|
||||
private Button addButton;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
|
||||
getActivity().setTitle("Enter Contact Name");
|
||||
|
||||
View v = inflater.inflate(R.layout.fragment_contact_alias_input,
|
||||
container, false);
|
||||
|
||||
contactNameInput = v.findViewById(R.id.contactNameInput);
|
||||
contactNameInput.addTextChangedListener(this);
|
||||
|
||||
addButton = v.findViewById(R.id.addButton);
|
||||
addButton.setOnClickListener(view -> onAddButtonClicked());
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public static final String TAG = ContactAliasInputFragment.class.getName();
|
||||
|
||||
@Override
|
||||
public String getUniqueTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectFragment(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count,
|
||||
int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before,
|
||||
int count) {
|
||||
updateAddButtonState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
}
|
||||
|
||||
private boolean isBriarLink(CharSequence s) {
|
||||
return getActivity() != null &&
|
||||
((ContactInviteInputActivity) getActivity()).isBriarLink(s);
|
||||
}
|
||||
|
||||
private void updateAddButtonState() {
|
||||
addButton.setEnabled(contactNameInput.getText().length() > 0);
|
||||
}
|
||||
|
||||
private void onAddButtonClicked() {
|
||||
if (getActivity() == null || getContext() == null) return;;
|
||||
|
||||
((ContactInviteInputActivity) getActivity())
|
||||
.addFakeRequest(contactNameInput.getText().toString());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext(), R.style.BriarDialogTheme_Neutral);
|
||||
builder.setTitle("Contact requested");
|
||||
builder.setMessage(getString(R.string.add_contact_link_question));
|
||||
builder.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||
Intent intent = new Intent(getContext(), NavDrawerActivity.class);
|
||||
intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
});
|
||||
builder.setNegativeButton(R.string.no, (dialog, which) -> {
|
||||
startActivity(
|
||||
new Intent(getContext(), ContactInviteOutputActivity.class));
|
||||
finish();
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
package org.briarproject.briar.android.contact;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BriarActivity;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.app.AlarmManager.ELAPSED_REALTIME;
|
||||
import static android.content.Intent.ACTION_SEND;
|
||||
import static android.content.Intent.ACTION_VIEW;
|
||||
import static android.content.Intent.EXTRA_TEXT;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.os.SystemClock.elapsedRealtime;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
||||
|
||||
public class ContactInviteInputActivity extends BriarActivity implements
|
||||
BaseFragmentListener {
|
||||
|
||||
@Inject
|
||||
LifecycleManager lifecycleManager;
|
||||
@Inject
|
||||
MessagingManager messagingManager;
|
||||
@Inject
|
||||
Clock clock;
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle state) {
|
||||
super.onCreate(state);
|
||||
setContentView(R.layout.activity_fragment_container);
|
||||
|
||||
ActionBar ab = getSupportActionBar();
|
||||
if (ab != null) {
|
||||
ab.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
Intent i = getIntent();
|
||||
if (i != null) {
|
||||
String action = i.getAction();
|
||||
if (ACTION_SEND.equals(action) || ACTION_VIEW.equals(action)) {
|
||||
String text = i.getStringExtra(EXTRA_TEXT);
|
||||
if (text != null) {
|
||||
showInitialFragment(
|
||||
ContactLinkInputFragment.newInstance(text));
|
||||
return;
|
||||
}
|
||||
String uri = i.getDataString();
|
||||
if (uri != null) {
|
||||
showInitialFragment(
|
||||
ContactLinkInputFragment.newInstance(uri));
|
||||
return;
|
||||
}
|
||||
} else if ("addContact".equals(action)) {
|
||||
removeFakeRequest(i.getStringExtra("name"),
|
||||
i.getLongExtra("timestamp", 0));
|
||||
setIntent(null);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
if (state == null) {
|
||||
showInitialFragment(ContactLinkInputFragment.newInstance(null));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
onBackPressed();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isBriarLink(CharSequence s) {
|
||||
String link = s.toString().trim();
|
||||
return link.matches("^(briar://)?[A-Z2-7]{64}$");
|
||||
}
|
||||
|
||||
void showLink(@Nullable String link) {
|
||||
showInitialFragment(ContactLinkInputFragment.newInstance(link));
|
||||
}
|
||||
|
||||
void showCode() {
|
||||
showNextFragment(new ContactQrCodeInputFragment());
|
||||
}
|
||||
|
||||
void showAlias() {
|
||||
showNextFragment(new ContactAliasInputFragment());
|
||||
}
|
||||
|
||||
void addFakeRequest(String name) {
|
||||
long timestamp = clock.currentTimeMillis();
|
||||
try {
|
||||
messagingManager.addNewPendingContact(name, timestamp);
|
||||
} catch (DbException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
AlarmManager alarmManager =
|
||||
(AlarmManager) requireNonNull(getSystemService(ALARM_SERVICE));
|
||||
long m = MINUTES.toMillis(1);
|
||||
long fromNow = (long) (-m * Math.log(new Random().nextDouble()));
|
||||
long triggerAt = elapsedRealtime() + fromNow;
|
||||
|
||||
Intent i = new Intent(this, ContactInviteInputActivity.class);
|
||||
i.setAction("addContact");
|
||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||
i.putExtra("name", name);
|
||||
i.putExtra("timestamp", timestamp);
|
||||
PendingIntent pendingIntent = PendingIntent
|
||||
.getActivity(this, (int) timestamp / 1000, i, 0);
|
||||
alarmManager.set(ELAPSED_REALTIME, triggerAt, pendingIntent);
|
||||
|
||||
Log.e("TEST", "Setting Alarm in " + MILLISECONDS.toSeconds(fromNow) +
|
||||
" seconds");
|
||||
Log.e("TEST", "with contact: " + name);
|
||||
}
|
||||
|
||||
private void removeFakeRequest(String name, long timestamp) {
|
||||
if (lifecycleManager.getLifecycleState() != RUNNING) {
|
||||
Log.e("TEST", "Lifecycle not started, not adding contact " + name);
|
||||
return;
|
||||
}
|
||||
Log.e("TEST", "Adding Contact " + name);
|
||||
try {
|
||||
messagingManager.removePendingContact(name, timestamp);
|
||||
} catch (DbException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package org.briarproject.briar.android.contact;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BriarActivity;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ContactInviteOutputActivity extends BriarActivity implements
|
||||
BaseFragmentListener {
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle state) {
|
||||
super.onCreate(state);
|
||||
|
||||
setContentView(R.layout.activity_fragment_container);
|
||||
|
||||
ActionBar ab = getSupportActionBar();
|
||||
if (ab != null) {
|
||||
ab.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
if (state == null) {
|
||||
showInitialFragment(new ContactLinkOutputFragment());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
onBackPressed();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
void showLink() {
|
||||
showInitialFragment(new ContactLinkOutputFragment());
|
||||
}
|
||||
|
||||
void showCode() {
|
||||
showNextFragment(new ContactQrCodeOutputFragment());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
package org.briarproject.briar.android.contact;
|
||||
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
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.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.content.ClipDescription.MIMETYPE_TEXT_PLAIN;
|
||||
import static android.content.Context.CLIPBOARD_SERVICE;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.support.v4.graphics.drawable.DrawableCompat.setTint;
|
||||
import static android.support.v4.graphics.drawable.DrawableCompat.wrap;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute;
|
||||
|
||||
@NotNullByDefault
|
||||
public class ContactLinkInputFragment extends BaseFragment
|
||||
implements TextWatcher {
|
||||
|
||||
private ClipboardManager clipboard;
|
||||
private EditText linkInput;
|
||||
private Button pasteButton;
|
||||
private EditText contactNameInput;
|
||||
private Button addButton;
|
||||
|
||||
static BaseFragment newInstance(@Nullable String link) {
|
||||
BaseFragment f = new ContactLinkInputFragment();
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("link", link);
|
||||
f.setArguments(bundle);
|
||||
return f;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
|
||||
getActivity().setTitle(R.string.open_link_title);
|
||||
|
||||
View v = inflater.inflate(R.layout.fragment_contact_link_input,
|
||||
container, false);
|
||||
|
||||
clipboard = (ClipboardManager) requireNonNull(
|
||||
getContext().getSystemService(CLIPBOARD_SERVICE));
|
||||
|
||||
int color = resolveColorAttribute(getContext(), R.attr.colorControlNormal);
|
||||
|
||||
linkInput = v.findViewById(R.id.linkInput);
|
||||
linkInput.addTextChangedListener(this);
|
||||
if (SDK_INT < 23) {
|
||||
Drawable drawable = wrap(linkInput.getCompoundDrawables()[0]);
|
||||
setTint(drawable, color);
|
||||
linkInput.setCompoundDrawables(drawable, null, null, null);
|
||||
}
|
||||
|
||||
pasteButton = v.findViewById(R.id.pasteButton);
|
||||
pasteButton.setOnClickListener(view -> linkInput
|
||||
.setText(clipboard.getPrimaryClip().getItemAt(0).getText()));
|
||||
|
||||
contactNameInput = v.findViewById(R.id.contactNameInput);
|
||||
contactNameInput.addTextChangedListener(this);
|
||||
if (SDK_INT < 23) {
|
||||
Drawable drawable =
|
||||
wrap(contactNameInput.getCompoundDrawables()[0]);
|
||||
setTint(drawable, color);
|
||||
contactNameInput.setCompoundDrawables(drawable, null, null, null);
|
||||
}
|
||||
|
||||
addButton = v.findViewById(R.id.addButton);
|
||||
addButton.setOnClickListener(view -> onAddButtonClicked());
|
||||
|
||||
Button scanCodeButton = v.findViewById(R.id.scanCodeButton);
|
||||
scanCodeButton.setOnClickListener(view ->
|
||||
((ContactInviteInputActivity) getActivity()).showCode());
|
||||
|
||||
linkInput.setText(getArguments().getString("link"));
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public static final String TAG = ContactLinkInputFragment.class.getName();
|
||||
|
||||
@Override
|
||||
public String getUniqueTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectFragment(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (hasLinkInClipboard()) pasteButton.setEnabled(true);
|
||||
else pasteButton.setEnabled(false);
|
||||
}
|
||||
|
||||
private boolean hasLinkInClipboard() {
|
||||
return clipboard.hasPrimaryClip() &&
|
||||
clipboard.getPrimaryClip().getDescription()
|
||||
.hasMimeType(MIMETYPE_TEXT_PLAIN) &&
|
||||
clipboard.getPrimaryClip().getItemCount() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count,
|
||||
int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before,
|
||||
int count) {
|
||||
if (isBriarLink(linkInput.getText()) && getActivity() != null) {
|
||||
updateAddButtonState();
|
||||
// linkInput.setText(null);
|
||||
// ((ContactInviteInputActivity) getActivity()).showAlias();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
}
|
||||
|
||||
private boolean isBriarLink(CharSequence s) {
|
||||
return getActivity() != null &&
|
||||
((ContactInviteInputActivity) getActivity()).isBriarLink(s);
|
||||
}
|
||||
|
||||
private void updateAddButtonState() {
|
||||
addButton.setEnabled(isBriarLink(linkInput.getText()) &&
|
||||
contactNameInput.getText().length() > 0);
|
||||
}
|
||||
|
||||
private void onAddButtonClicked() {
|
||||
if (getActivity() == null || getContext() == null) return;
|
||||
|
||||
((ContactInviteInputActivity) getActivity())
|
||||
.addFakeRequest(contactNameInput.getText().toString());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext(),
|
||||
R.style.BriarDialogTheme_Neutral);
|
||||
builder.setTitle("Contact requested");
|
||||
builder.setMessage(getString(R.string.add_contact_link_question));
|
||||
builder.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||
Intent intent = new Intent(getContext(), PendingRequestsActivity.class);
|
||||
// intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
});
|
||||
builder.setNegativeButton(R.string.no, (dialog, which) -> {
|
||||
startActivity(
|
||||
new Intent(getContext(),
|
||||
ContactInviteOutputActivity.class));
|
||||
finish();
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package org.briarproject.briar.android.contact;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.content.Context.CLIPBOARD_SERVICE;
|
||||
import static android.content.Intent.ACTION_SEND;
|
||||
import static android.content.Intent.EXTRA_TEXT;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomBase32String;
|
||||
|
||||
@NotNullByDefault
|
||||
public class ContactLinkOutputFragment extends BaseFragment {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
|
||||
getActivity().setTitle(R.string.send_link_title);
|
||||
|
||||
View v = inflater.inflate(R.layout.fragment_contact_link_output,
|
||||
container, false);
|
||||
|
||||
String link = "briar://" + getRandomBase32String(64);
|
||||
|
||||
TextView linkView = v.findViewById(R.id.linkView);
|
||||
linkView.setText(link);
|
||||
|
||||
ClipboardManager clipboard = (ClipboardManager)
|
||||
getContext().getSystemService(CLIPBOARD_SERVICE);
|
||||
if (clipboard == null) throw new AssertionError();
|
||||
ClipData clip = ClipData.newPlainText(
|
||||
getString(R.string.link_clip_label), link);
|
||||
|
||||
Button copyButton = v.findViewById(R.id.copyButton);
|
||||
copyButton.setOnClickListener(view -> {
|
||||
clipboard.setPrimaryClip(clip);
|
||||
Toast.makeText(getContext(), R.string.link_copied_toast,
|
||||
LENGTH_SHORT).show();
|
||||
});
|
||||
|
||||
Button shareButton = v.findViewById(R.id.shareButton);
|
||||
shareButton.setOnClickListener(view -> {
|
||||
Intent i = new Intent(ACTION_SEND);
|
||||
i.putExtra(EXTRA_TEXT, link);
|
||||
i.setType("text/plain");
|
||||
startActivity(i);
|
||||
});
|
||||
|
||||
Button showCodeButton = v.findViewById(R.id.showCodeButton);
|
||||
showCodeButton.setOnClickListener(
|
||||
view -> ((ContactInviteOutputActivity) getActivity()).showCode());
|
||||
return v;
|
||||
}
|
||||
|
||||
public static final String TAG = ContactLinkOutputFragment.class.getName();
|
||||
|
||||
@Override
|
||||
public String getUniqueTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectFragment(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -2,11 +2,8 @@ package org.briarproject.briar.android.contact;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.ActivityOptionsCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.util.Pair;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -15,12 +12,10 @@ import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
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.contact.event.ContactAddedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
@@ -42,24 +37,17 @@ import org.briarproject.briar.android.view.BriarRecyclerView;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
||||
import org.briarproject.briar.api.messaging.ConversationManager;
|
||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||
import org.briarproject.briar.api.messaging.MessagingManager.PendingContact;
|
||||
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
||||
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.github.kobakei.materialfabspeeddial.FabSpeedDial;
|
||||
import io.github.kobakei.materialfabspeeddial.FabSpeedDial.OnMenuItemClickListener;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.support.design.widget.Snackbar.LENGTH_INDEFINITE;
|
||||
import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAnimation;
|
||||
import static android.support.v4.view.ViewCompat.getTransitionName;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
@@ -70,8 +58,7 @@ import static org.briarproject.briar.android.contact.ConversationActivity.CONTAC
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class ContactListFragment extends BaseFragment implements EventListener,
|
||||
OnMenuItemClickListener {
|
||||
public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
|
||||
public static final String TAG = ContactListFragment.class.getName();
|
||||
private static final Logger LOG = Logger.getLogger(TAG);
|
||||
@@ -83,13 +70,8 @@ public class ContactListFragment extends BaseFragment implements EventListener,
|
||||
@Inject
|
||||
AndroidNotificationManager notificationManager;
|
||||
|
||||
// TODO remove
|
||||
@Inject
|
||||
MessagingManager messagingManager;
|
||||
|
||||
private ContactListAdapter adapter;
|
||||
private BriarRecyclerView list;
|
||||
private Snackbar snackbar;
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject
|
||||
@@ -122,12 +104,7 @@ public class ContactListFragment extends BaseFragment implements EventListener,
|
||||
|
||||
getActivity().setTitle(R.string.contact_list_button);
|
||||
|
||||
View contentView =
|
||||
inflater.inflate(R.layout.fragment_contact_list, container,
|
||||
false);
|
||||
|
||||
FabSpeedDial speedDialView = contentView.findViewById(R.id.speedDial);
|
||||
speedDialView.addOnMenuItemClickListener(this);
|
||||
View contentView = inflater.inflate(R.layout.list, container, false);
|
||||
|
||||
OnContactClickListener<ContactListItem> onContactClickListener =
|
||||
(view, item) -> {
|
||||
@@ -166,20 +143,12 @@ public class ContactListFragment extends BaseFragment implements EventListener,
|
||||
list.setEmptyText(getString(R.string.no_contacts));
|
||||
list.setEmptyAction(getString(R.string.no_contacts_action));
|
||||
|
||||
snackbar = Snackbar.make(contentView,
|
||||
R.string.pending_contact_requests_snackbar, LENGTH_INDEFINITE);
|
||||
snackbar.getView().setBackgroundResource(R.color.briar_primary);
|
||||
snackbar.setAction(R.string.show, v -> startActivity(
|
||||
new Intent(getContext(), PendingRequestsActivity.class)));
|
||||
snackbar.setActionTextColor(ContextCompat
|
||||
.getColor(getContext(), R.color.briar_button_text_positive));
|
||||
|
||||
return contentView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
// inflater.inflate(R.menu.contact_list_actions, menu);
|
||||
inflater.inflate(R.menu.contact_list_actions, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@@ -197,28 +166,6 @@ public class ContactListFragment extends BaseFragment implements EventListener,
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMenuItemClick(FloatingActionButton fab, TextView v,
|
||||
int itemId) {
|
||||
switch (itemId) {
|
||||
case R.id.action_add_contact:
|
||||
Intent intent =
|
||||
new Intent(getContext(), ContactExchangeActivity.class);
|
||||
startActivity(intent);
|
||||
return;
|
||||
case R.id.action_open_link:
|
||||
startActivity(new Intent(getContext(),
|
||||
ContactInviteInputActivity.class));
|
||||
return;
|
||||
case R.id.action_send_link:
|
||||
startActivity(new Intent(getContext(),
|
||||
ContactInviteOutputActivity.class));
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
@@ -227,26 +174,6 @@ public class ContactListFragment extends BaseFragment implements EventListener,
|
||||
notificationManager.clearAllIntroductionNotifications();
|
||||
loadContacts();
|
||||
list.startPeriodicUpdate();
|
||||
|
||||
// TODO remove
|
||||
checkForPendingContacts();
|
||||
}
|
||||
|
||||
// TODO remove
|
||||
private void checkForPendingContacts() {
|
||||
listener.runOnDbThread(() -> {
|
||||
try {
|
||||
Collection<PendingContact> contacts =
|
||||
messagingManager.getPendingContacts();
|
||||
if (contacts.isEmpty()) {
|
||||
runOnUiThreadUnlessDestroyed(() -> snackbar.dismiss());
|
||||
} else {
|
||||
runOnUiThreadUnlessDestroyed(() -> snackbar.show());
|
||||
}
|
||||
} catch (DbException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -321,11 +248,6 @@ public class ContactListFragment extends BaseFragment implements EventListener,
|
||||
PrivateMessageHeader h = p.getMessageHeader();
|
||||
updateItem(p.getContactId(), h);
|
||||
}
|
||||
|
||||
// TODO remove
|
||||
else if (e instanceof ContactAddedEvent) {
|
||||
checkForPendingContacts();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateItem(ContactId c, PrivateMessageHeader h) {
|
||||
|
||||
@@ -10,6 +10,8 @@ import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
||||
import org.briarproject.briar.android.util.UiUtils;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.support.v4.view.ViewCompat.setTransitionName;
|
||||
@@ -36,7 +38,7 @@ class ContactListItemViewHolder extends ContactItemViewHolder<ContactListItem> {
|
||||
// unread count
|
||||
int unreadCount = item.getUnreadCount();
|
||||
if (unreadCount > 0) {
|
||||
unread.setText(String.valueOf(unreadCount));
|
||||
unread.setText(String.format(Locale.getDefault(), "%d", unreadCount));
|
||||
unread.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
unread.setVisibility(View.INVISIBLE);
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
package org.briarproject.briar.android.contact;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.zxing.Result;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
import org.briarproject.briar.android.keyagreement.CameraException;
|
||||
import org.briarproject.briar.android.keyagreement.CameraView;
|
||||
import org.briarproject.briar.android.keyagreement.QrCodeDecoder;
|
||||
import org.briarproject.briar.android.util.UiUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.Manifest.permission.CAMERA;
|
||||
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.widget.Toast.LENGTH_LONG;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA;
|
||||
|
||||
@NotNullByDefault
|
||||
public class ContactQrCodeInputFragment extends BaseFragment
|
||||
implements QrCodeDecoder.ResultCallback {
|
||||
|
||||
private CameraView cameraView;
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
getActivity().setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR);
|
||||
cameraView.setPreviewConsumer(new QrCodeDecoder(this));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
|
||||
getActivity().setTitle("Scan QR Code");
|
||||
|
||||
View v = inflater.inflate(R.layout.fragment_contact_qr_code_input,
|
||||
container, false);
|
||||
|
||||
cameraView = v.findViewById(R.id.camera_view);
|
||||
|
||||
|
||||
// Button enterLinkButton = v.findViewById(R.id.enterLinkButton);
|
||||
// enterLinkButton.setOnClickListener(view ->
|
||||
// ((ContactInviteInputActivity) getActivity()).showLink());
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public static final String TAG = ContactQrCodeInputFragment.class.getName();
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
if (checkPermissions()) {
|
||||
startCamera();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
try {
|
||||
cameraView.stop();
|
||||
} catch (CameraException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void startCamera() {
|
||||
try {
|
||||
cameraView.start();
|
||||
} catch (CameraException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(getContext(), "Camera Error", LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectFragment(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
private boolean checkPermissions() {
|
||||
if (ActivityCompat.checkSelfPermission(getContext(), CAMERA) !=
|
||||
PERMISSION_GRANTED) {
|
||||
// Should we show an explanation?
|
||||
if (shouldShowRequestPermissionRationale(CAMERA)) {
|
||||
DialogInterface.OnClickListener continueListener =
|
||||
(dialog, which) -> requestPermission();
|
||||
AlertDialog.Builder
|
||||
builder = new AlertDialog.Builder(getContext(), R.style.BriarDialogTheme);
|
||||
builder.setTitle(R.string.permission_camera_title);
|
||||
builder.setMessage(R.string.permission_camera_request_body);
|
||||
builder.setNeutralButton(R.string.continue_button,
|
||||
continueListener);
|
||||
builder.show();
|
||||
} else {
|
||||
requestPermission();
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode,
|
||||
@NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
if (requestCode == REQUEST_PERMISSION_CAMERA) {
|
||||
// If request is cancelled, the result arrays are empty.
|
||||
if (grantResults.length > 0 &&
|
||||
grantResults[0] == PERMISSION_GRANTED) {
|
||||
startCamera();
|
||||
} else {
|
||||
if (!shouldShowRequestPermissionRationale(CAMERA)) {
|
||||
// The user has permanently denied the request
|
||||
AlertDialog.Builder
|
||||
builder = new AlertDialog.Builder(getContext(),
|
||||
R.style.BriarDialogTheme);
|
||||
builder.setTitle(R.string.permission_camera_title);
|
||||
builder.setMessage(R.string.permission_camera_denied_body);
|
||||
builder.setPositiveButton(R.string.ok,
|
||||
UiUtils.getGoToSettingsListener(getContext()));
|
||||
builder.setNegativeButton(R.string.cancel,
|
||||
(dialog, which) -> showLink(null));
|
||||
builder.show();
|
||||
} else {
|
||||
Toast.makeText(getContext(),
|
||||
R.string.permission_camera_denied_toast,
|
||||
LENGTH_LONG).show();
|
||||
showLink(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void requestPermission() {
|
||||
requestPermissions(new String[] {CAMERA}, REQUEST_PERMISSION_CAMERA);
|
||||
}
|
||||
|
||||
private void showLink(@Nullable String link) {
|
||||
if (getActivity() != null)
|
||||
((ContactInviteInputActivity) getActivity()).showLink(link);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResult(Result result) {
|
||||
Log.e("TEST", result.toString());
|
||||
if (getActivity() != null &&
|
||||
((ContactInviteInputActivity) getActivity())
|
||||
.isBriarLink(result.getText())) {
|
||||
showLink(result.getText());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package org.briarproject.briar.android.contact;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
import org.briarproject.briar.android.view.QrCodeView;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomBase32String;
|
||||
import static org.briarproject.briar.android.keyagreement.QrCodeUtils.createQrCode;
|
||||
|
||||
@NotNullByDefault
|
||||
public class ContactQrCodeOutputFragment extends BaseFragment
|
||||
implements QrCodeView.FullscreenListener {
|
||||
|
||||
private View linkIntro;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
|
||||
getActivity().setTitle("Show my QR Code");
|
||||
|
||||
View v = inflater.inflate(R.layout.fragment_contact_qr_code_output,
|
||||
container, false);
|
||||
linkIntro = v.findViewById(R.id.linkIntro);
|
||||
|
||||
String link = "briar://" + getRandomBase32String(64);
|
||||
DisplayMetrics dm = getResources().getDisplayMetrics();
|
||||
Bitmap qrCode = createQrCode(dm, link);
|
||||
QrCodeView qrCodeView = v.findViewById(R.id.qrCodeView);
|
||||
qrCodeView.setQrCode(qrCode);
|
||||
qrCodeView.setFullscreenListener(this);
|
||||
|
||||
Button showLinkButton = v.findViewById(R.id.showLinkButton);
|
||||
showLinkButton.setOnClickListener(
|
||||
view -> ((ContactInviteOutputActivity) getActivity()).showLink());
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public static final String TAG = ContactQrCodeOutputFragment.class.getName();
|
||||
|
||||
@Override
|
||||
public String getUniqueTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectFragment(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFullscreen(boolean fullscreen) {
|
||||
linkIntro.setVisibility(fullscreen ? GONE : VISIBLE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,7 +53,7 @@ import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BriarActivity;
|
||||
import org.briarproject.briar.android.blog.BlogActivity;
|
||||
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.introduction.IntroductionActivity;
|
||||
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
|
||||
@@ -105,7 +105,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.util.UiUtils.getAvatarTransitionName;
|
||||
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_FINISHED;
|
||||
|
||||
@@ -113,7 +113,7 @@ import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.S
|
||||
@ParametersNotNullByDefault
|
||||
public class ConversationActivity extends BriarActivity
|
||||
implements EventListener, ConversationListener, TextInputListener,
|
||||
BodyCache {
|
||||
TextCache {
|
||||
|
||||
public static final String CONTACT_ID = "briar.CONTACT_ID";
|
||||
|
||||
@@ -130,7 +130,7 @@ public class ConversationActivity extends BriarActivity
|
||||
@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 ConversationVisitor visitor;
|
||||
@@ -370,28 +370,28 @@ public class ConversationActivity extends BriarActivity
|
||||
return items;
|
||||
}
|
||||
|
||||
private void loadMessageBody(MessageId m) {
|
||||
private void loadMessageText(MessageId m) {
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
long start = now();
|
||||
String body = messagingManager.getMessageBody(m);
|
||||
logDuration(LOG, "Loading body", start);
|
||||
displayMessageBody(m, body);
|
||||
String text = messagingManager.getMessageText(m);
|
||||
logDuration(LOG, "Loading text", start);
|
||||
displayMessageText(m, text);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void displayMessageBody(MessageId m, String body) {
|
||||
private void displayMessageText(MessageId m, String text) {
|
||||
runOnUiThreadUnlessDestroyed(() -> {
|
||||
bodyCache.put(m, body);
|
||||
textCache.put(m, text);
|
||||
SparseArray<ConversationItem> messages =
|
||||
adapter.getPrivateMessages();
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
ConversationItem item = messages.valueAt(i);
|
||||
if (item.getId().equals(m)) {
|
||||
item.setBody(body);
|
||||
item.setText(text);
|
||||
adapter.notifyItemChanged(messages.keyAt(i));
|
||||
list.scrollToPosition(adapter.getItemCount() - 1);
|
||||
return;
|
||||
@@ -470,7 +470,7 @@ public class ConversationActivity extends BriarActivity
|
||||
}
|
||||
} else {
|
||||
addConversationItem(h.accept(visitor));
|
||||
loadMessageBody(h.getId());
|
||||
loadMessageText(h.getId());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -495,8 +495,8 @@ public class ConversationActivity extends BriarActivity
|
||||
|
||||
@Override
|
||||
public void onSendClick(String text) {
|
||||
if (text.equals("")) return;
|
||||
text = StringUtils.truncateUtf8(text, MAX_PRIVATE_MESSAGE_BODY_LENGTH);
|
||||
if (text.isEmpty()) return;
|
||||
text = StringUtils.truncateUtf8(text, MAX_PRIVATE_MESSAGE_TEXT_LENGTH);
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
|
||||
if (messagingGroupId == null) loadGroupId(text, timestamp);
|
||||
@@ -510,12 +510,12 @@ public class ConversationActivity extends BriarActivity
|
||||
return item == null ? 0 : item.getTime() + 1;
|
||||
}
|
||||
|
||||
private void loadGroupId(String body, long timestamp) {
|
||||
private void loadGroupId(String text, long timestamp) {
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
messagingGroupId =
|
||||
messagingManager.getConversationId(contactId);
|
||||
createMessage(body, timestamp);
|
||||
createMessage(text, timestamp);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
@@ -523,19 +523,19 @@ public class ConversationActivity extends BriarActivity
|
||||
});
|
||||
}
|
||||
|
||||
private void createMessage(String body, long timestamp) {
|
||||
private void createMessage(String text, long timestamp) {
|
||||
cryptoExecutor.execute(() -> {
|
||||
try {
|
||||
//noinspection ConstantConditions init in loadGroupId()
|
||||
storeMessage(privateMessageFactory.createPrivateMessage(
|
||||
messagingGroupId, timestamp, body), body);
|
||||
messagingGroupId, timestamp, text), text);
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void storeMessage(PrivateMessage m, String body) {
|
||||
private void storeMessage(PrivateMessage m, String text) {
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
long start = now();
|
||||
@@ -545,7 +545,7 @@ public class ConversationActivity extends BriarActivity
|
||||
PrivateMessageHeader h = new PrivateMessageHeader(
|
||||
message.getId(), message.getGroupId(),
|
||||
message.getTimestamp(), true, false, false, false);
|
||||
bodyCache.put(message.getId(), body);
|
||||
textCache.put(message.getId(), text);
|
||||
addConversationItem(h.accept(visitor));
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
@@ -761,9 +761,9 @@ public class ConversationActivity extends BriarActivity
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getBody(MessageId m) {
|
||||
String body = bodyCache.get(m);
|
||||
if (body == null) loadMessageBody(m);
|
||||
return body;
|
||||
public String getText(MessageId m) {
|
||||
String text = textCache.get(m);
|
||||
if (text == null) loadMessageText(m);
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,17 +14,17 @@ import javax.annotation.concurrent.NotThreadSafe;
|
||||
abstract class ConversationItem {
|
||||
|
||||
@Nullable
|
||||
protected String body;
|
||||
protected String text;
|
||||
private final MessageId id;
|
||||
private final GroupId groupId;
|
||||
private final long time;
|
||||
private boolean read;
|
||||
|
||||
ConversationItem(MessageId id, GroupId groupId, @Nullable String body,
|
||||
ConversationItem(MessageId id, GroupId groupId, @Nullable String text,
|
||||
long time, boolean read) {
|
||||
this.id = id;
|
||||
this.groupId = groupId;
|
||||
this.body = body;
|
||||
this.text = text;
|
||||
this.time = time;
|
||||
this.read = read;
|
||||
}
|
||||
@@ -37,13 +37,13 @@ abstract class ConversationItem {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
void setBody(String body) {
|
||||
this.body = body;
|
||||
void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getBody() {
|
||||
return body;
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
long getTime() {
|
||||
|
||||
@@ -29,10 +29,10 @@ class ConversationItemViewHolder extends ViewHolder {
|
||||
|
||||
@CallSuper
|
||||
void bind(ConversationItem item) {
|
||||
if (item.getBody() == null) {
|
||||
if (item.getText() == null) {
|
||||
text.setText("\u2026");
|
||||
} else {
|
||||
text.setText(StringUtils.trim(item.getBody()));
|
||||
text.setText(StringUtils.trim(item.getText()));
|
||||
}
|
||||
|
||||
long timestamp = item.getTime();
|
||||
|
||||
@@ -29,13 +29,13 @@ class ConversationNoticeInViewHolder extends ConversationItemViewHolder {
|
||||
ConversationNoticeInItem item =
|
||||
(ConversationNoticeInItem) conversationItem;
|
||||
|
||||
String message = item.getMsgText();
|
||||
if (StringUtils.isNullOrEmpty(message)) {
|
||||
String text = item.getMsgText();
|
||||
if (StringUtils.isNullOrEmpty(text)) {
|
||||
msgText.setVisibility(GONE);
|
||||
layout.setBackgroundResource(R.drawable.notice_in);
|
||||
} else {
|
||||
msgText.setVisibility(VISIBLE);
|
||||
msgText.setText(StringUtils.trim(message));
|
||||
msgText.setText(StringUtils.trim(text));
|
||||
layout.setBackgroundResource(R.drawable.notice_in_bottom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class ConversationNoticeOutItem extends ConversationOutItem {
|
||||
ConversationNoticeOutItem(String text, PrivateRequest r) {
|
||||
super(r.getId(), r.getGroupId(), text, r.getTimestamp(), r.isSent(),
|
||||
r.isSeen());
|
||||
this.msgText = r.getMessage();
|
||||
this.msgText = r.getText();
|
||||
}
|
||||
|
||||
ConversationNoticeOutItem(String text, PrivateResponse r) {
|
||||
|
||||
@@ -29,13 +29,13 @@ class ConversationNoticeOutViewHolder extends ConversationOutItemViewHolder {
|
||||
ConversationNoticeOutItem item =
|
||||
(ConversationNoticeOutItem) conversationItem;
|
||||
|
||||
String message = item.getMsgText();
|
||||
if (StringUtils.isNullOrEmpty(message)) {
|
||||
String text = item.getMsgText();
|
||||
if (StringUtils.isNullOrEmpty(text)) {
|
||||
msgText.setVisibility(GONE);
|
||||
layout.setBackgroundResource(R.drawable.notice_out);
|
||||
} else {
|
||||
msgText.setVisibility(VISIBLE);
|
||||
msgText.setText(StringUtils.trim(message));
|
||||
msgText.setText(StringUtils.trim(text));
|
||||
layout.setBackgroundResource(R.drawable.notice_out_bottom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class ConversationRequestItem extends ConversationNoticeInItem {
|
||||
private boolean answered;
|
||||
|
||||
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());
|
||||
this.requestType = type;
|
||||
this.sessionId = r.getSessionId();
|
||||
|
||||
@@ -30,13 +30,13 @@ import static org.briarproject.briar.android.contact.ConversationRequestItem.Req
|
||||
class ConversationVisitor implements PrivateMessageVisitor<ConversationItem> {
|
||||
|
||||
private final Context ctx;
|
||||
private final BodyCache bodyCache;
|
||||
private final TextCache textCache;
|
||||
private final LiveData<String> contactName;
|
||||
|
||||
ConversationVisitor(Context ctx, BodyCache bodyCache,
|
||||
ConversationVisitor(Context ctx, TextCache textCache,
|
||||
LiveData<String> contactName) {
|
||||
this.ctx = ctx;
|
||||
this.bodyCache = bodyCache;
|
||||
this.textCache = textCache;
|
||||
this.contactName = contactName;
|
||||
}
|
||||
|
||||
@@ -45,8 +45,8 @@ class ConversationVisitor implements PrivateMessageVisitor<ConversationItem> {
|
||||
ConversationItem item;
|
||||
if (h.isLocal()) item = new ConversationMessageOutItem(h);
|
||||
else item = new ConversationMessageInItem(h);
|
||||
String body = bodyCache.getBody(h.getId());
|
||||
if (body != null) item.setBody(body);
|
||||
String text = textCache.getText(h.getId());
|
||||
if (text != null) item.setText(text);
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -239,8 +239,8 @@ class ConversationVisitor implements PrivateMessageVisitor<ConversationItem> {
|
||||
}
|
||||
}
|
||||
|
||||
interface BodyCache {
|
||||
interface TextCache {
|
||||
@Nullable
|
||||
String getBody(MessageId m);
|
||||
String getText(MessageId m);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
package org.briarproject.briar.android.contact;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BriarActivity;
|
||||
import org.briarproject.briar.android.view.BriarRecyclerView;
|
||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||
import org.briarproject.briar.api.messaging.MessagingManager.PendingContact;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class PendingRequestsActivity extends BriarActivity
|
||||
implements EventListener {
|
||||
|
||||
@Inject
|
||||
MessagingManager messagingManager;
|
||||
@Inject
|
||||
ContactManager contactManager;
|
||||
@Inject
|
||||
EventBus eventBus;
|
||||
|
||||
private PendingRequestsAdapter adapter;
|
||||
private BriarRecyclerView list;
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle state) {
|
||||
super.onCreate(state);
|
||||
|
||||
setContentView(R.layout.list);
|
||||
|
||||
ActionBar ab = getSupportActionBar();
|
||||
if (ab != null) {
|
||||
ab.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
adapter = new PendingRequestsAdapter(this, PendingContact.class);
|
||||
list = findViewById(R.id.list);
|
||||
list.setLayoutManager(new LinearLayoutManager(this));
|
||||
list.setAdapter(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
eventBus.addListener(this);
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
Collection<PendingContact> contacts =
|
||||
messagingManager.getPendingContacts();
|
||||
addPendingContacts(contacts);
|
||||
} catch (DbException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
adapter.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
onBackPressed();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof ContactAddedEvent) {
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
Contact contact = contactManager
|
||||
.getContact(((ContactAddedEvent) e).getContactId());
|
||||
runOnUiThreadUnlessDestroyed(() -> {
|
||||
adapter.remove(contact);
|
||||
if (adapter.isEmpty()) finish();
|
||||
});
|
||||
} catch (DbException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void addPendingContacts(Collection<PendingContact> contacts) {
|
||||
runOnUiThreadUnlessDestroyed(() -> {
|
||||
if (contacts.isEmpty()) {
|
||||
list.showData();
|
||||
} else {
|
||||
adapter.addAll(contacts);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package org.briarproject.briar.android.contact;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.util.BriarAdapter;
|
||||
import org.briarproject.briar.api.messaging.MessagingManager.PendingContact;
|
||||
|
||||
@NotNullByDefault
|
||||
public class PendingRequestsAdapter extends
|
||||
BriarAdapter<PendingContact, PendingRequestsViewHolder> {
|
||||
|
||||
public PendingRequestsAdapter(Context ctx, Class<PendingContact> c) {
|
||||
super(ctx, c);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PendingRequestsViewHolder onCreateViewHolder(
|
||||
ViewGroup viewGroup, int i) {
|
||||
View v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_pending_contact, viewGroup, false);
|
||||
return new PendingRequestsViewHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(
|
||||
PendingRequestsViewHolder pendingRequestsViewHolder, int i) {
|
||||
pendingRequestsViewHolder.bind(items.get(i));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(PendingContact item1, PendingContact item2) {
|
||||
return (int) (item1.getTimestamp() - item2.getTimestamp());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(PendingContact item1,
|
||||
PendingContact item2) {
|
||||
return item1.getName().equals(item2.getName()) &&
|
||||
item1.getTimestamp() == item2.getTimestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areItemsTheSame(PendingContact item1,
|
||||
PendingContact item2) {
|
||||
return item1.getName().equals(item2.getName()) &&
|
||||
item1.getTimestamp() == item2.getTimestamp();
|
||||
}
|
||||
|
||||
// TODO remove
|
||||
public void remove(Contact contact) {
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
if (items.get(i).getName().equals(contact.getAuthor().getName())) {
|
||||
items.removeItemAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package org.briarproject.briar.android.contact;
|
||||
|
||||
import android.support.v7.widget.RecyclerView.ViewHolder;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.view.TextAvatarView;
|
||||
import org.briarproject.briar.api.messaging.MessagingManager.PendingContact;
|
||||
|
||||
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
||||
import static org.briarproject.briar.android.util.UiUtils.formatDate;
|
||||
|
||||
@NotNullByDefault
|
||||
public class PendingRequestsViewHolder extends ViewHolder {
|
||||
|
||||
private final TextAvatarView avatar;
|
||||
private final TextView name;
|
||||
private final TextView time;
|
||||
|
||||
public PendingRequestsViewHolder(View v) {
|
||||
super(v);
|
||||
avatar = v.findViewById(R.id.avatar);
|
||||
name = v.findViewById(R.id.name);
|
||||
time = v.findViewById(R.id.time);
|
||||
}
|
||||
|
||||
public void bind(PendingContact item) {
|
||||
avatar.setText(item.getName());
|
||||
avatar.setBackgroundBytes(toUtf8(item.getName() + item.getTimestamp()));
|
||||
name.setText(item.getName());
|
||||
time.setText(formatDate(time.getContext(), item.getTimestamp()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,7 +33,7 @@ import javax.inject.Inject;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
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
|
||||
@ParametersNotNullByDefault
|
||||
@@ -130,8 +130,8 @@ public class ForumActivity extends
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMaxBodyLength() {
|
||||
return MAX_FORUM_POST_BODY_LENGTH;
|
||||
protected int getMaxTextLength() {
|
||||
return MAX_FORUM_POST_TEXT_LENGTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -79,7 +79,7 @@ class ForumControllerImpl extends
|
||||
ForumPostReceivedEvent f = (ForumPostReceivedEvent) e;
|
||||
if (f.getGroupId().equals(getGroupId())) {
|
||||
LOG.info("Forum post received, adding...");
|
||||
onForumPostReceived(f.getHeader(), f.getBody());
|
||||
onForumPostReceived(f.getHeader(), f.getText());
|
||||
}
|
||||
} else if (e instanceof ForumInvitationResponseReceivedEvent) {
|
||||
ForumInvitationResponseReceivedEvent f =
|
||||
@@ -109,8 +109,8 @@ class ForumControllerImpl extends
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String loadMessageBody(ForumPostHeader h) throws DbException {
|
||||
return forumManager.getPostBody(h.getId());
|
||||
protected String loadMessageText(ForumPostHeader h) throws DbException {
|
||||
return forumManager.getPostText(h.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -137,7 +137,7 @@ class ForumControllerImpl extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createAndStoreMessage(String body,
|
||||
public void createAndStoreMessage(String text,
|
||||
@Nullable ForumItem parentItem,
|
||||
ResultExceptionHandler<ForumItem, DbException> handler) {
|
||||
runOnDbThread(() -> {
|
||||
@@ -148,7 +148,7 @@ class ForumControllerImpl extends
|
||||
clock.currentTimeMillis());
|
||||
MessageId parentId = parentItem != null ?
|
||||
parentItem.getId() : null;
|
||||
createMessage(body, timestamp, parentId, author, handler);
|
||||
createMessage(text, timestamp, parentId, author, handler);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, 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,
|
||||
ResultExceptionHandler<ForumItem, DbException> handler) {
|
||||
cryptoExecutor.execute(() -> {
|
||||
LOG.info("Creating forum post...");
|
||||
ForumPost msg = forumManager.createLocalPost(getGroupId(), body,
|
||||
ForumPost msg = forumManager.createLocalPost(getGroupId(), text,
|
||||
timestamp, parentId, author);
|
||||
storePost(msg, body, handler);
|
||||
storePost(msg, text, handler);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -179,12 +179,12 @@ class ForumControllerImpl extends
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ForumItem buildItem(ForumPostHeader header, String body) {
|
||||
return new ForumItem(header, body);
|
||||
protected ForumItem buildItem(ForumPostHeader header, String text) {
|
||||
return new ForumItem(header, text);
|
||||
}
|
||||
|
||||
private void onForumPostReceived(ForumPostHeader h, String body) {
|
||||
ForumItem item = buildItem(h, body);
|
||||
private void onForumPostReceived(ForumPostHeader h, String text) {
|
||||
ForumItem item = buildItem(h, text);
|
||||
listener.runOnUiThreadUnlessDestroyed(
|
||||
() -> listener.onItemReceived(item));
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import javax.annotation.concurrent.NotThreadSafe;
|
||||
@NotThreadSafe
|
||||
class ForumItem extends ThreadItem {
|
||||
|
||||
ForumItem(ForumPostHeader h, String body) {
|
||||
super(h.getId(), h.getParentId(), body, h.getTimestamp(), h.getAuthor(),
|
||||
ForumItem(ForumPostHeader h, String text) {
|
||||
super(h.getId(), h.getParentId(), text, h.getTimestamp(), h.getAuthor(),
|
||||
h.getAuthorStatus(), h.isRead());
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ import static android.view.View.VISIBLE;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
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.api.introduction.IntroductionConstants.MAX_INTRODUCTION_TEXT_LENGTH;
|
||||
|
||||
public class IntroductionMessageFragment extends BaseFragment
|
||||
implements TextInputListener {
|
||||
@@ -187,10 +187,10 @@ public class IntroductionMessageFragment extends BaseFragment
|
||||
// disable button to prevent accidental double invitations
|
||||
ui.message.setSendButtonEnabled(false);
|
||||
|
||||
String msg = ui.message.getText().toString();
|
||||
if (msg.equals("")) msg = null;
|
||||
else msg = StringUtils.truncateUtf8(msg, MAX_REQUEST_MESSAGE_LENGTH);
|
||||
makeIntroduction(contact1, contact2, msg);
|
||||
String txt = ui.message.getText().toString();
|
||||
if (txt.isEmpty()) txt = null;
|
||||
else txt = StringUtils.truncateUtf8(txt, MAX_INTRODUCTION_TEXT_LENGTH);
|
||||
makeIntroduction(contact1, contact2, txt);
|
||||
|
||||
// don't wait for the introduction to be made before finishing activity
|
||||
introductionActivity.hideSoftKeyboard(ui.message);
|
||||
@@ -199,12 +199,12 @@ public class IntroductionMessageFragment extends BaseFragment
|
||||
}
|
||||
|
||||
private void makeIntroduction(Contact c1, Contact c2,
|
||||
@Nullable String msg) {
|
||||
@Nullable String text) {
|
||||
introductionActivity.runOnDbThread(() -> {
|
||||
// actually make the introduction
|
||||
try {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
introductionManager.makeIntroduction(c1, c2, msg, timestamp);
|
||||
introductionManager.makeIntroduction(c1, c2, text, timestamp);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
introductionError();
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.briarproject.briar.android.keyagreement;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class CameraException extends IOException {
|
||||
class CameraException extends IOException {
|
||||
|
||||
CameraException(String message) {
|
||||
super(message);
|
||||
|
||||
@@ -7,7 +7,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@NotNullByDefault
|
||||
public interface PreviewConsumer {
|
||||
interface PreviewConsumer {
|
||||
|
||||
@UiThread
|
||||
void start(Camera camera, int cameraIndex);
|
||||
|
||||
@@ -29,7 +29,7 @@ import static java.util.logging.Level.WARNING;
|
||||
@SuppressWarnings("deprecation")
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
||||
class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(QrCodeDecoder.class.getName());
|
||||
@@ -40,7 +40,7 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
||||
private Camera camera = null;
|
||||
private int cameraIndex = 0;
|
||||
|
||||
public QrCodeDecoder(ResultCallback callback) {
|
||||
QrCodeDecoder(ResultCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
||||
}
|
||||
|
||||
@NotNullByDefault
|
||||
public interface ResultCallback {
|
||||
interface ResultCallback {
|
||||
|
||||
void handleResult(Result result);
|
||||
}
|
||||
|
||||
@@ -21,13 +21,13 @@ import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@NotNullByDefault
|
||||
public class QrCodeUtils {
|
||||
class QrCodeUtils {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(QrCodeUtils.class.getName());
|
||||
|
||||
@Nullable
|
||||
public static Bitmap createQrCode(DisplayMetrics dm, String input) {
|
||||
static Bitmap createQrCode(DisplayMetrics dm, String input) {
|
||||
int smallestDimen = Math.min(dm.widthPixels, dm.heightPixels);
|
||||
try {
|
||||
// Generate QR code
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
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.STARTING_SERVICES;
|
||||
|
||||
@@ -34,7 +35,7 @@ public class OpenDatabaseActivity extends BriarActivity
|
||||
|
||||
private TextView textView;
|
||||
private ImageView imageView;
|
||||
private boolean showingMigration = false;
|
||||
private boolean showingMigration = false, showingCompaction = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle state) {
|
||||
@@ -57,6 +58,7 @@ public class OpenDatabaseActivity extends BriarActivity
|
||||
finishAndStartApp();
|
||||
} else {
|
||||
if (state == MIGRATING_DATABASE) showMigration();
|
||||
else if (state == COMPACTING_DATABASE) showCompaction();
|
||||
eventBus.addListener(this);
|
||||
}
|
||||
}
|
||||
@@ -75,6 +77,8 @@ public class OpenDatabaseActivity extends BriarActivity
|
||||
runOnUiThreadUnlessDestroyed(this::finishAndStartApp);
|
||||
else if (state == MIGRATING_DATABASE)
|
||||
runOnUiThreadUnlessDestroyed(this::showMigration);
|
||||
else if (state == COMPACTING_DATABASE)
|
||||
runOnUiThreadUnlessDestroyed(this::showCompaction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +89,13 @@ public class OpenDatabaseActivity extends BriarActivity
|
||||
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() {
|
||||
startActivity(new Intent(this, NavDrawerActivity.class));
|
||||
supportFinishAfterTransition();
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package org.briarproject.briar.android.panic;
|
||||
|
||||
import info.guardianproject.trustedintents.ApkSignaturePin;
|
||||
|
||||
// needs to be public, because TrustedIntents will instantiate
|
||||
public final class FDroidSignaturePin extends ApkSignaturePin {
|
||||
|
||||
public FDroidSignaturePin() {
|
||||
this.fingerprints = new String[] {
|
||||
"927f7e38b6acbecd84e02dace33efa9a7a2f0979750f28f585688ee38b3a4e28"};
|
||||
this.certificates = new byte[][] {
|
||||
{48, -126, 3, 95, 48, -126, 2, 71, -96, 3, 2, 1, 2, 2, 4, 28,
|
||||
-30, 107, -102, 48, 13, 6, 9, 42, -122, 72, -122, -9,
|
||||
13, 1, 1, 11, 5, 0, 48, 96, 49, 11, 48, 9, 6, 3, 85, 4,
|
||||
6, 19, 2, 85, 75, 49, 12, 48, 10, 6, 3, 85, 4, 8, 19, 3,
|
||||
79, 82, 71, 49, 12, 48, 10, 6, 3, 85, 4, 7, 19, 3, 79,
|
||||
82, 71, 49, 19, 48, 17, 6, 3, 85, 4, 10, 19, 10, 102,
|
||||
100, 114, 111, 105, 100, 46, 111, 114, 103, 49, 15, 48,
|
||||
13, 6, 3, 85, 4, 11, 19, 6, 70, 68, 114, 111, 105, 100,
|
||||
49, 15, 48, 13, 6, 3, 85, 4, 3, 19, 6, 70, 68, 114, 111,
|
||||
105, 100, 48, 30, 23, 13, 49, 55, 49, 50, 48, 55, 49,
|
||||
55, 51, 48, 52, 50, 90, 23, 13, 52, 53, 48, 52, 50, 52,
|
||||
49, 55, 51, 48, 52, 50, 90, 48, 96, 49, 11, 48, 9, 6, 3,
|
||||
85, 4, 6, 19, 2, 85, 75, 49, 12, 48, 10, 6, 3, 85, 4, 8,
|
||||
19, 3, 79, 82, 71, 49, 12, 48, 10, 6, 3, 85, 4, 7, 19,
|
||||
3, 79, 82, 71, 49, 19, 48, 17, 6, 3, 85, 4, 10, 19, 10,
|
||||
102, 100, 114, 111, 105, 100, 46, 111, 114, 103, 49, 15,
|
||||
48, 13, 6, 3, 85, 4, 11, 19, 6, 70, 68, 114, 111, 105,
|
||||
100, 49, 15, 48, 13, 6, 3, 85, 4, 3, 19, 6, 70, 68, 114,
|
||||
111, 105, 100, 48, -126, 1, 34, 48, 13, 6, 9, 42, -122,
|
||||
72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -126, 1, 15, 0, 48,
|
||||
-126, 1, 10, 2, -126, 1, 1, 0, -107, -115, -106, 1, -26,
|
||||
72, -105, -99, 62, 3, -55, 34, 99, -112, -68, -20, -115,
|
||||
31, 34, 118, -50, 12, -32, -59, 74, -58, -37, -87, 21,
|
||||
105, 36, -82, 13, -51, 66, 4, 55, -111, 13, -46, -7,
|
||||
-69, -15, 36, 118, -7, 101, -86, 123, -83, -103, 110,
|
||||
116, -54, 112, 46, 12, 96, -76, -48, -70, -33, -81, 52,
|
||||
59, 73, 107, -126, -72, -25, 32, 93, 29, -20, 5, -41,
|
||||
-27, 123, -9, 104, -31, -59, -1, -83, -93, 99, 85, -116,
|
||||
-62, -55, 18, -63, 6, -51, -110, 33, 9, 7, -49, 102,
|
||||
-20, -122, -124, -68, 93, -102, 31, 48, 86, 96, -99,
|
||||
105, -52, 95, 12, 57, 99, 12, -24, 70, 40, -99, -20,
|
||||
-21, -85, -70, -105, 95, 117, -31, 126, -126, -39, 46,
|
||||
-62, 59, -23, -74, 108, -12, -56, -40, -96, 79, -37,
|
||||
-82, 1, 99, -104, 48, -60, 92, 14, 109, 127, -22, 31,
|
||||
115, -27, 108, 9, 92, 118, -45, 103, 117, 57, -50, -82,
|
||||
114, -113, 68, -82, 87, 96, 111, 72, 65, -63, 12, 31,
|
||||
-34, -31, -55, -101, 101, 101, 59, 73, -119, -122, 82,
|
||||
28, 47, -108, -85, 59, 46, 89, -93, -1, 9, -11, -51, 63,
|
||||
-44, 109, -76, -103, -26, -49, -80, 6, 52, -27, 73,
|
||||
-104, 40, 2, -101, -124, 60, -52, -105, -70, -24, -62,
|
||||
88, 38, 53, -99, -92, 31, 119, 26, 79, 60, -124, 25,
|
||||
-115, -89, -115, -109, 0, 6, 122, -78, 116, 82, 3, 39,
|
||||
-67, 45, -43, 17, -39, 2, 3, 1, 0, 1, -93, 33, 48, 31,
|
||||
48, 29, 6, 3, 85, 29, 14, 4, 22, 4, 20, 63, 109, -42,
|
||||
-109, 25, 22, 7, -37, -22, -41, -38, 58, -56, 2, -68,
|
||||
-38, -22, 65, -28, -60, 48, 13, 6, 9, 42, -122, 72,
|
||||
-122, -9, 13, 1, 1, 11, 5, 0, 3, -126, 1, 1, 0, 94, 17,
|
||||
31, 36, 85, -11, 85, 44, 19, -80, -20, -92, -118, 93,
|
||||
40, 45, 96, 31, -3, -37, -110, -96, 102, 81, 61, -74,
|
||||
-125, -117, -112, 58, -47, 17, 78, -18, 111, -116, 26,
|
||||
-91, 73, 100, 84, -99, 21, 87, 73, -106, 108, -51, -125,
|
||||
-21, 119, -88, -78, 2, 82, -109, -64, -9, -86, -112,
|
||||
-115, 66, -86, 46, 71, 107, -65, 96, -102, 47, 35, -45,
|
||||
-126, 33, 34, 121, -25, -85, -121, -56, -42, 22, -1,
|
||||
-95, -86, 81, 100, -70, 113, 104, -73, 22, -19, 79, -19,
|
||||
52, 62, 42, 76, -112, 94, -34, 42, -57, -75, -90, -58,
|
||||
118, 127, -106, -39, 108, -56, -79, 103, -33, 22, 3, 47,
|
||||
103, -76, -81, 53, -22, -44, -26, -102, 63, -99, 39, 38,
|
||||
-108, 75, 33, 10, 25, -110, -125, -115, 114, -69, 73,
|
||||
-112, 36, 74, 77, -82, -44, 29, -123, -8, -117, 71,
|
||||
-105, 15, -109, 51, 22, 4, 80, 1, 43, 118, 121, -113,
|
||||
-70, 83, -56, 82, -110, 4, -63, 16, -57, 126, -70, 81,
|
||||
73, 61, 2, -61, 24, -14, -10, 4, -21, 90, 24, 66, 41,
|
||||
-57, -60, -113, -18, -54, -1, 103, -75, 32, -64, 67,
|
||||
103, 109, -79, -12, -113, -27, 114, 89, 116, 115, -13,
|
||||
-123, -70, 61, -41, -46, -118, 29, -105, -97, -75, 39,
|
||||
-51, 60, 88, 125, 55, -46, -95, 52, 57, 52, -115, 80,
|
||||
44, 109, 119, -116, -62, -77, -74, -88, 41, 57, -65,
|
||||
-71, -115, -67, 23, 66, -21, 56, 51, -91, 109}};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.briar.android.panic;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
@@ -22,34 +21,39 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import info.guardianproject.panic.Panic;
|
||||
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
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
public static final String KEY_LOCK = "pref_key_lock";
|
||||
public static final String KEY_PANIC_APP = "pref_key_panic_app";
|
||||
public static final String KEY_PURGE = "pref_key_purge";
|
||||
public static final String KEY_UNINSTALL = "pref_key_uninstall";
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(PanicPreferencesFragment.class.getName());
|
||||
|
||||
private PackageManager pm;
|
||||
private SwitchPreference lockPref, purgePref, uninstallPref;
|
||||
private SwitchPreference lockPref, purgePref;
|
||||
private ListPreference panicAppPref;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle bundle, String s) {
|
||||
addPreferencesFromResource(R.xml.panic_preferences);
|
||||
}
|
||||
|
||||
private void updatePreferences() {
|
||||
pm = getActivity().getPackageManager();
|
||||
|
||||
lockPref = (SwitchPreference) findPreference(KEY_LOCK);
|
||||
panicAppPref = (ListPreference) findPreference(KEY_PANIC_APP);
|
||||
purgePref = (SwitchPreference) findPreference(KEY_PURGE);
|
||||
uninstallPref = (SwitchPreference) findPreference(KEY_UNINSTALL);
|
||||
|
||||
// check for connect/disconnect intents from panic trigger apps
|
||||
if (PanicResponder.checkForDisconnectIntent(getActivity())) {
|
||||
@@ -76,7 +80,7 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
||||
ArrayList<CharSequence> entries = new ArrayList<>();
|
||||
ArrayList<CharSequence> entryValues = new ArrayList<>();
|
||||
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)) {
|
||||
if (resolveInfo.activityInfo == null)
|
||||
@@ -85,28 +89,21 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
||||
entryValues.add(resolveInfo.activityInfo.packageName);
|
||||
}
|
||||
|
||||
panicAppPref.setEntries(
|
||||
entries.toArray(new CharSequence[entries.size()]));
|
||||
panicAppPref.setEntryValues(
|
||||
entryValues.toArray(new CharSequence[entryValues.size()]));
|
||||
panicAppPref.setDefaultValue(Panic.PACKAGE_NAME_NONE);
|
||||
panicAppPref.setEntries(entries.toArray(new CharSequence[0]));
|
||||
panicAppPref.setEntryValues(entryValues.toArray(new CharSequence[0]));
|
||||
panicAppPref.setDefaultValue(PACKAGE_NAME_NONE);
|
||||
|
||||
panicAppPref.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
String packageName = (String) newValue;
|
||||
PanicResponder.setTriggerPackageName(getActivity(), packageName);
|
||||
showPanicApp(packageName);
|
||||
|
||||
if (packageName.equals(Panic.PACKAGE_NAME_NONE)) {
|
||||
lockPref.setEnabled(false);
|
||||
if (packageName.equals(PACKAGE_NAME_NONE)) {
|
||||
purgePref.setChecked(false);
|
||||
purgePref.setEnabled(false);
|
||||
uninstallPref.setChecked(false);
|
||||
uninstallPref.setEnabled(false);
|
||||
getActivity().setResult(Activity.RESULT_CANCELED);
|
||||
getActivity().setResult(RESULT_CANCELED);
|
||||
} else {
|
||||
lockPref.setEnabled(true);
|
||||
purgePref.setEnabled(true);
|
||||
uninstallPref.setEnabled(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -114,16 +111,18 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
||||
|
||||
if (entries.size() <= 1) {
|
||||
panicAppPref.setOnPreferenceClickListener(preference -> {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
Intent intent = new Intent(ACTION_VIEW);
|
||||
intent.setData(Uri.parse(
|
||||
"market://details?id=info.guardianproject.ripple"));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||
if (intent.resolveActivity(getActivity().getPackageManager())
|
||||
!= null) {
|
||||
startActivity(intent);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
panicAppPref.setOnPreferenceClickListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +131,7 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
||||
super.onStart();
|
||||
getPreferenceScreen().getSharedPreferences()
|
||||
.registerOnSharedPreferenceChangeListener(this);
|
||||
updatePreferences();
|
||||
showPanicApp(PanicResponder.getTriggerPackageName(getActivity()));
|
||||
}
|
||||
|
||||
@@ -145,44 +145,30 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
|
||||
String key) {
|
||||
if (key.equals(KEY_PURGE)) {
|
||||
// enable locking if purging gets enabled
|
||||
if (sharedPreferences.getBoolean(KEY_PURGE, false)) {
|
||||
lockPref.setChecked(true);
|
||||
}
|
||||
// disable uninstall if purging gets disabled
|
||||
else {
|
||||
uninstallPref.setChecked(false);
|
||||
}
|
||||
}
|
||||
// enable purging and locking if uninstall gets enabled
|
||||
if (key.equals(KEY_UNINSTALL) &&
|
||||
sharedPreferences.getBoolean(KEY_UNINSTALL, false)) {
|
||||
// enable locking if purging gets enabled
|
||||
if (key.equals(KEY_PURGE) &&
|
||||
sharedPreferences.getBoolean(KEY_PURGE, false)) {
|
||||
lockPref.setChecked(true);
|
||||
purgePref.setChecked(true);
|
||||
}
|
||||
// disable purging and uninstalling if locking gets disabled
|
||||
// disable purging if locking gets disabled
|
||||
if (key.equals(KEY_LOCK) &&
|
||||
!sharedPreferences.getBoolean(KEY_LOCK, true)) {
|
||||
purgePref.setChecked(false);
|
||||
uninstallPref.setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void showPanicApp(String triggerPackageName) {
|
||||
if (TextUtils.isEmpty(triggerPackageName)
|
||||
|| triggerPackageName.equals(Panic.PACKAGE_NAME_NONE)) {
|
||||
|| triggerPackageName.equals(PACKAGE_NAME_NONE)) {
|
||||
// no panic app set
|
||||
panicAppPref.setValue(Panic.PACKAGE_NAME_NONE);
|
||||
panicAppPref.setValue(PACKAGE_NAME_NONE);
|
||||
panicAppPref
|
||||
.setSummary(getString(R.string.panic_app_setting_summary));
|
||||
panicAppPref.setIcon(
|
||||
android.R.drawable.ic_menu_close_clear_cancel);
|
||||
|
||||
// disable panic actions
|
||||
lockPref.setEnabled(false);
|
||||
// disable destructive panic actions
|
||||
purgePref.setEnabled(false);
|
||||
uninstallPref.setEnabled(false);
|
||||
} else {
|
||||
// display connected panic app
|
||||
try {
|
||||
@@ -192,15 +178,13 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
||||
panicAppPref.setIcon(
|
||||
pm.getApplicationIcon(triggerPackageName));
|
||||
|
||||
// enable panic actions
|
||||
lockPref.setEnabled(true);
|
||||
// enable destructive panic actions
|
||||
purgePref.setEnabled(true);
|
||||
uninstallPref.setEnabled(true);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// revert back to no app, just to be safe
|
||||
PanicResponder.setTriggerPackageName(getActivity(),
|
||||
Panic.PACKAGE_NAME_NONE);
|
||||
showPanicApp(Panic.PACKAGE_NAME_NONE);
|
||||
PACKAGE_NAME_NONE);
|
||||
showPanicApp(PACKAGE_NAME_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,10 +193,10 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
||||
DialogInterface.OnClickListener okListener = (dialog, which) -> {
|
||||
PanicResponder.setTriggerPackageName(getActivity());
|
||||
showPanicApp(PanicResponder.getTriggerPackageName(getActivity()));
|
||||
getActivity().setResult(Activity.RESULT_OK);
|
||||
getActivity().setResult(RESULT_OK);
|
||||
};
|
||||
DialogInterface.OnClickListener cancelListener = (dialog, which) -> {
|
||||
getActivity().setResult(Activity.RESULT_CANCELED);
|
||||
getActivity().setResult(RESULT_CANCELED);
|
||||
getActivity().finish();
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.briarproject.briar.android.panic;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.preference.PreferenceManager;
|
||||
@@ -11,7 +10,6 @@ import org.briarproject.bramble.api.account.AccountManager;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BriarActivity;
|
||||
import org.iilab.IilabEngineeringRSA2048Pin;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -22,10 +20,8 @@ import info.guardianproject.panic.Panic;
|
||||
import info.guardianproject.panic.PanicResponder;
|
||||
import info.guardianproject.trustedintents.TrustedIntents;
|
||||
|
||||
import static android.content.Intent.ACTION_DELETE;
|
||||
import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_LOCK;
|
||||
import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_PURGE;
|
||||
import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_UNINSTALL;
|
||||
|
||||
public class PanicResponderActivity extends BriarActivity {
|
||||
|
||||
@@ -44,8 +40,8 @@ public class PanicResponderActivity extends BriarActivity {
|
||||
TrustedIntents trustedIntents = TrustedIntents.get(this);
|
||||
// Guardian Project Ripple
|
||||
trustedIntents.addTrustedSigner(GuardianProjectRSA4096.class);
|
||||
// Amnesty International's Panic Button, made by iilab.org
|
||||
trustedIntents.addTrustedSigner(IilabEngineeringRSA2048Pin.class);
|
||||
// F-Droid
|
||||
trustedIntents.addTrustedSigner(FDroidSignaturePin.class);
|
||||
|
||||
Intent intent = trustedIntents.getIntentFromTrustedSender(this);
|
||||
if (intent != null) {
|
||||
@@ -60,23 +56,16 @@ public class PanicResponderActivity extends BriarActivity {
|
||||
LOG.info("Panic Trigger came from connected app");
|
||||
|
||||
// Performing panic responses
|
||||
if (sharedPref.getBoolean(KEY_UNINSTALL, false)) {
|
||||
if (sharedPref.getBoolean(KEY_PURGE, false)) {
|
||||
LOG.info("Purging all data...");
|
||||
deleteAllData();
|
||||
|
||||
LOG.info("Uninstalling...");
|
||||
Intent uninstall = new Intent(ACTION_DELETE);
|
||||
uninstall.setData(
|
||||
Uri.parse("package:" + getPackageName()));
|
||||
startActivity(uninstall);
|
||||
} else if (sharedPref.getBoolean(KEY_PURGE, false)) {
|
||||
LOG.info("Purging all data...");
|
||||
deleteAllData();
|
||||
} else if (sharedPref.getBoolean(KEY_LOCK, true)) {
|
||||
LOG.info("Signing out...");
|
||||
signOut(true);
|
||||
}
|
||||
}
|
||||
// non-destructive actions are allowed by non-connected trusted apps
|
||||
if (sharedPref.getBoolean(KEY_LOCK, true)) {
|
||||
LOG.info("Signing out...");
|
||||
signOut(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ import javax.inject.Inject;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
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
|
||||
@ParametersNotNullByDefault
|
||||
@@ -179,8 +179,8 @@ public class GroupActivity extends
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMaxBodyLength() {
|
||||
return MAX_GROUP_POST_BODY_LENGTH;
|
||||
protected int getMaxTextLength() {
|
||||
return MAX_GROUP_POST_TEXT_LENGTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -84,7 +84,7 @@ class GroupControllerImpl extends
|
||||
GroupMessageAddedEvent g = (GroupMessageAddedEvent) e;
|
||||
if (!g.isLocal() && g.getGroupId().equals(getGroupId())) {
|
||||
LOG.info("Group message received, adding...");
|
||||
GroupMessageItem item = buildItem(g.getHeader(), g.getBody());
|
||||
GroupMessageItem item = buildItem(g.getHeader(), g.getText());
|
||||
listener.runOnUiThreadUnlessDestroyed(
|
||||
() -> listener.onItemReceived(item));
|
||||
}
|
||||
@@ -124,13 +124,13 @@ class GroupControllerImpl extends
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String loadMessageBody(GroupMessageHeader header)
|
||||
protected String loadMessageText(GroupMessageHeader header)
|
||||
throws DbException {
|
||||
if (header instanceof JoinMessageHeader) {
|
||||
// will be looked up later
|
||||
return "";
|
||||
}
|
||||
return privateGroupManager.getMessageBody(header.getId());
|
||||
return privateGroupManager.getMessageText(header.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -159,7 +159,7 @@ class GroupControllerImpl extends
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createAndStoreMessage(String body,
|
||||
public void createAndStoreMessage(String text,
|
||||
@Nullable GroupMessageItem parentItem,
|
||||
ResultExceptionHandler<GroupMessageItem, DbException> handler) {
|
||||
runOnDbThread(() -> {
|
||||
@@ -173,7 +173,7 @@ class GroupControllerImpl extends
|
||||
long timestamp = count.getLatestMsgTime();
|
||||
if (parentItem != null) parentId = parentItem.getId();
|
||||
timestamp = max(clock.currentTimeMillis(), timestamp + 1);
|
||||
createMessage(body, timestamp, parentId, author, previousMsgId,
|
||||
createMessage(text, timestamp, parentId, author, previousMsgId,
|
||||
handler);
|
||||
} catch (DbException 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,
|
||||
MessageId previousMsgId,
|
||||
ResultExceptionHandler<GroupMessageItem, DbException> handler) {
|
||||
@@ -190,8 +190,8 @@ class GroupControllerImpl extends
|
||||
LOG.info("Creating group message...");
|
||||
GroupMessage msg = groupMessageFactory
|
||||
.createGroupMessage(getGroupId(), timestamp,
|
||||
parentId, author, body, previousMsgId);
|
||||
storePost(msg, body, handler);
|
||||
parentId, author, text, previousMsgId);
|
||||
storePost(msg, text, handler);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -208,11 +208,11 @@ class GroupControllerImpl extends
|
||||
|
||||
@Override
|
||||
protected GroupMessageItem buildItem(GroupMessageHeader header,
|
||||
String body) {
|
||||
String text) {
|
||||
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
|
||||
|
||||
@@ -18,6 +18,6 @@ public interface CreateGroupController
|
||||
ResultExceptionHandler<GroupId, DbException> result);
|
||||
|
||||
void sendInvitation(GroupId g, Collection<ContactId> contacts,
|
||||
String message, ResultExceptionHandler<Void, DbException> result);
|
||||
String text, ResultExceptionHandler<Void, DbException> result);
|
||||
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
|
||||
|
||||
@Override
|
||||
public void sendInvitation(GroupId g, Collection<ContactId> contactIds,
|
||||
String message, ResultExceptionHandler<Void, DbException> handler) {
|
||||
String text, ResultExceptionHandler<Void, DbException> handler) {
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
LocalAuthor localAuthor = identityManager.getLocalAuthor();
|
||||
@@ -135,7 +135,7 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
|
||||
// Continue
|
||||
}
|
||||
}
|
||||
signInvitations(g, localAuthor, contacts, message, handler);
|
||||
signInvitations(g, localAuthor, contacts, text, handler);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
handler.onException(e);
|
||||
@@ -144,7 +144,7 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
|
||||
}
|
||||
|
||||
private void signInvitations(GroupId g, LocalAuthor localAuthor,
|
||||
Collection<Contact> contacts, String message,
|
||||
Collection<Contact> contacts, String text,
|
||||
ResultExceptionHandler<Void, DbException> handler) {
|
||||
cryptoExecutor.execute(() -> {
|
||||
long timestamp = clock.currentTimeMillis();
|
||||
@@ -155,20 +155,20 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
|
||||
contexts.add(new InvitationContext(c.getId(), timestamp,
|
||||
signature));
|
||||
}
|
||||
sendInvitations(g, contexts, message, handler);
|
||||
sendInvitations(g, contexts, text, handler);
|
||||
});
|
||||
}
|
||||
|
||||
private void sendInvitations(GroupId g,
|
||||
Collection<InvitationContext> contexts, String message,
|
||||
Collection<InvitationContext> contexts, String text,
|
||||
ResultExceptionHandler<Void, DbException> handler) {
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
String msg = message.isEmpty() ? null : message;
|
||||
String txt = text.isEmpty() ? null : text;
|
||||
for (InvitationContext context : contexts) {
|
||||
try {
|
||||
groupInvitationManager.sendInvitation(g,
|
||||
context.contactId, msg, context.timestamp,
|
||||
context.contactId, txt, context.timestamp,
|
||||
context.signature);
|
||||
} catch (NoSuchContactException e) {
|
||||
// Continue
|
||||
|
||||
@@ -18,7 +18,7 @@ import java.util.Collection;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_MSG_LENGTH;
|
||||
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_TEXT_LENGTH;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
@@ -55,10 +55,10 @@ public class GroupInviteActivity extends ContactSelectorActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onButtonClick(String message) {
|
||||
public boolean onButtonClick(String text) {
|
||||
if (groupId == null)
|
||||
throw new IllegalStateException("GroupId was not initialized");
|
||||
controller.sendInvitation(groupId, contacts, message,
|
||||
controller.sendInvitation(groupId, contacts, text,
|
||||
new UiResultExceptionHandler<Void, DbException>(this) {
|
||||
@Override
|
||||
public void onResultUi(Void result) {
|
||||
@@ -76,7 +76,7 @@ public class GroupInviteActivity extends ContactSelectorActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumMessageLength() {
|
||||
return MAX_GROUP_INVITATION_MSG_LENGTH;
|
||||
public int getMaximumTextLength() {
|
||||
return MAX_GROUP_INVITATION_TEXT_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.support.annotation.NonNull;
|
||||
import org.acra.collector.CrashReportData;
|
||||
import org.acra.sender.ReportSender;
|
||||
import org.acra.sender.ReportSenderException;
|
||||
import org.acra.util.JSONReportBuilder.JSONReportException;
|
||||
import org.briarproject.bramble.api.reporting.DevReporter;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
import org.briarproject.briar.android.AndroidComponent;
|
||||
@@ -34,12 +33,7 @@ public class BriarReportSender implements ReportSender {
|
||||
@NonNull CrashReportData errorContent)
|
||||
throws ReportSenderException {
|
||||
component.inject(this);
|
||||
String crashReport;
|
||||
try {
|
||||
crashReport = errorContent.toJSON().toString();
|
||||
} catch (JSONReportException e) {
|
||||
throw new ReportSenderException("Couldn't create JSON", e);
|
||||
}
|
||||
String crashReport = errorContent.toJSON().toString();
|
||||
try {
|
||||
File reportDir = AndroidUtils.getReportDir(ctx);
|
||||
String reportId = errorContent.getProperty(REPORT_ID);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.briar.android.reporting;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
@@ -23,8 +24,11 @@ import org.acra.ReportField;
|
||||
import org.acra.collector.CrashReportData;
|
||||
import org.acra.dialog.BaseCrashReportDialog;
|
||||
import org.acra.file.CrashReportPersister;
|
||||
import org.acra.model.Element;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.Localizer;
|
||||
import org.briarproject.briar.android.util.UserFeedback;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -38,6 +42,7 @@ import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
||||
import static android.view.inputmethod.InputMethodManager.SHOW_FORCED;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.acra.ACRAConstants.EXTRA_REPORT_FILE;
|
||||
@@ -47,6 +52,7 @@ import static org.acra.ReportField.APP_VERSION_NAME;
|
||||
import static org.acra.ReportField.PACKAGE_NAME;
|
||||
import static org.acra.ReportField.REPORT_ID;
|
||||
import static org.acra.ReportField.STACK_TRACE;
|
||||
import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
|
||||
|
||||
public class DevReportActivity extends BaseCrashReportDialog
|
||||
implements CompoundButton.OnCheckedChangeListener {
|
||||
@@ -107,6 +113,8 @@ public class DevReportActivity extends BaseCrashReportDialog
|
||||
public void init(Bundle state) {
|
||||
super.init(state);
|
||||
|
||||
if (PREVENT_SCREENSHOTS) getWindow().addFlags(FLAG_SECURE);
|
||||
|
||||
getDelegate().setContentView(R.layout.activity_dev_report);
|
||||
|
||||
Toolbar tb = findViewById(R.id.toolbar);
|
||||
@@ -166,6 +174,12 @@ public class DevReportActivity extends BaseCrashReportDialog
|
||||
requestReport.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(
|
||||
Localizer.getInstance().setLocale(base));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostCreate(Bundle state) {
|
||||
super.onPostCreate(state);
|
||||
@@ -270,7 +284,7 @@ public class DevReportActivity extends BaseCrashReportDialog
|
||||
CrashReportPersister persister = new CrashReportPersister();
|
||||
try {
|
||||
return persister.load(reportFile);
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | JSONException e) {
|
||||
LOG.log(WARNING, "Could not load report file", e);
|
||||
return null;
|
||||
}
|
||||
@@ -280,9 +294,10 @@ public class DevReportActivity extends BaseCrashReportDialog
|
||||
protected void onPostExecute(CrashReportData crashData) {
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
if (crashData != null) {
|
||||
for (Entry<ReportField, String> e : crashData.entrySet()) {
|
||||
for (Entry<ReportField, Element> e : crashData.entrySet()) {
|
||||
ReportField field = e.getKey();
|
||||
String value = e.getValue().replaceAll("\\\\n", "\n");
|
||||
String value = e.getValue().toString()
|
||||
.replaceAll("\\\\n", "\n");
|
||||
boolean required = requiredFields.contains(field);
|
||||
boolean excluded = excludedFields.contains(field);
|
||||
View v = inflater.inflate(R.layout.list_item_crash,
|
||||
@@ -331,10 +346,10 @@ public class DevReportActivity extends BaseCrashReportDialog
|
||||
data.remove(field);
|
||||
}
|
||||
} else {
|
||||
Iterator<Entry<ReportField, String>> iter =
|
||||
Iterator<Entry<ReportField, Element>> iter =
|
||||
data.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Entry<ReportField, String> e = iter.next();
|
||||
Entry<ReportField, Element> e = iter.next();
|
||||
if (!requiredFields.contains(e.getKey())) {
|
||||
iter.remove();
|
||||
}
|
||||
@@ -342,7 +357,7 @@ public class DevReportActivity extends BaseCrashReportDialog
|
||||
}
|
||||
persister.store(data, reportFile);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | JSONException e) {
|
||||
LOG.log(WARNING, "Error processing report file", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
import org.briarproject.briar.android.view.LargeTextInputView;
|
||||
@@ -19,7 +18,8 @@ import org.briarproject.briar.android.view.TextInputView.TextInputListener;
|
||||
|
||||
import static android.support.design.widget.Snackbar.LENGTH_SHORT;
|
||||
import static org.briarproject.bramble.util.StringUtils.truncateUtf8;
|
||||
import static org.briarproject.briar.api.sharing.SharingConstants.MAX_INVITATION_MESSAGE_LENGTH;
|
||||
import static org.briarproject.bramble.util.StringUtils.utf8IsTooLong;
|
||||
import static org.briarproject.briar.api.sharing.SharingConstants.MAX_INVITATION_TEXT_LENGTH;
|
||||
|
||||
public abstract class BaseMessageFragment extends BaseFragment
|
||||
implements TextInputListener {
|
||||
@@ -76,8 +76,8 @@ public abstract class BaseMessageFragment extends BaseFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendClick(String msg) {
|
||||
if (StringUtils.utf8IsTooLong(msg, listener.getMaximumMessageLength())) {
|
||||
public void onSendClick(String text) {
|
||||
if (utf8IsTooLong(text, listener.getMaximumTextLength())) {
|
||||
Snackbar.make(message, R.string.text_too_long, LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
@@ -86,8 +86,8 @@ public abstract class BaseMessageFragment extends BaseFragment
|
||||
message.setSendButtonEnabled(false);
|
||||
message.hideSoftKeyboard();
|
||||
|
||||
msg = truncateUtf8(msg, MAX_INVITATION_MESSAGE_LENGTH);
|
||||
if(!listener.onButtonClick(msg)) {
|
||||
text = truncateUtf8(text, MAX_INVITATION_TEXT_LENGTH);
|
||||
if(!listener.onButtonClick(text)) {
|
||||
message.setSendButtonEnabled(true);
|
||||
message.showSoftKeyboard();
|
||||
}
|
||||
@@ -102,9 +102,9 @@ public abstract class BaseMessageFragment extends BaseFragment
|
||||
void setTitle(@StringRes int titleRes);
|
||||
|
||||
/** Returns true when the button click has been consumed. */
|
||||
boolean onButtonClick(String message);
|
||||
boolean onButtonClick(String text);
|
||||
|
||||
int getMaximumMessageLength();
|
||||
int getMaximumTextLength();
|
||||
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user