mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Compare commits
104 Commits
release-1.
...
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 | ||
|
|
071d961ed1 | ||
|
|
cb9efc5fec | ||
|
|
f9e292f734 |
9
.idea/codeStyles/Project.xml
generated
9
.idea/codeStyles/Project.xml
generated
@@ -36,6 +36,9 @@
|
||||
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
|
||||
<option name="JD_ALIGN_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>
|
||||
@@ -1,6 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All tests in bramble-android" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<module name="bramble-android" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
@@ -11,12 +10,10 @@
|
||||
<option name="VM_PARAMETERS" value="-ea" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-android" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
<value defaultName="singleModule" />
|
||||
</option>
|
||||
<envs />
|
||||
<patterns />
|
||||
<method />
|
||||
</configuration>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All tests in bramble-api" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<module name="bramble-api" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
@@ -11,12 +10,10 @@
|
||||
<option name="VM_PARAMETERS" value="-ea" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-api" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
<value defaultName="singleModule" />
|
||||
</option>
|
||||
<envs />
|
||||
<patterns />
|
||||
<method />
|
||||
</configuration>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All tests in bramble-core" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<module name="bramble-core" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
@@ -11,12 +10,10 @@
|
||||
<option name="VM_PARAMETERS" value="-ea" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-core" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
<value defaultName="singleModule" />
|
||||
</option>
|
||||
<envs />
|
||||
<patterns />
|
||||
<method />
|
||||
</configuration>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All tests in bramble-java" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<module name="bramble-java" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
@@ -11,12 +10,10 @@
|
||||
<option name="VM_PARAMETERS" value="-ea -Djava.library.path=libs" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/bramble-java" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
<value defaultName="singleModule" />
|
||||
</option>
|
||||
<envs />
|
||||
<patterns />
|
||||
<method />
|
||||
</configuration>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All tests in briar-android" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<module name="briar-android" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
@@ -11,12 +10,10 @@
|
||||
<option name="VM_PARAMETERS" value="-ea" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-android" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
<value defaultName="singleModule" />
|
||||
</option>
|
||||
<envs />
|
||||
<patterns />
|
||||
<method />
|
||||
</configuration>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="All tests in briar-core" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<module name="briar-core" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
@@ -11,12 +10,10 @@
|
||||
<option name="VM_PARAMETERS" value="-ea" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/briar-core" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
<value defaultName="singleModule" />
|
||||
</option>
|
||||
<envs />
|
||||
<patterns />
|
||||
<method />
|
||||
</configuration>
|
||||
|
||||
3
.idea/runConfigurations/H2_Performance_Test.xml
generated
3
.idea/runConfigurations/H2_Performance_Test.xml
generated
@@ -1,6 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="H2 Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<module name="bramble-core" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
@@ -11,12 +10,10 @@
|
||||
<option name="VM_PARAMETERS" value="-ea" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
<value defaultName="singleModule" />
|
||||
</option>
|
||||
<envs />
|
||||
<patterns />
|
||||
<method />
|
||||
</configuration>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="HyperSQL Performance Test" type="AndroidJUnit" factoryName="Android JUnit">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<module name="bramble-core" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
@@ -11,12 +10,10 @@
|
||||
<option name="VM_PARAMETERS" value="-ea" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
<value defaultName="singleModule" />
|
||||
</option>
|
||||
<envs />
|
||||
<patterns />
|
||||
<method />
|
||||
</configuration>
|
||||
|
||||
17
.idea/runConfigurations/briar_headless.xml
generated
Normal file
17
.idea/runConfigurations/briar_headless.xml
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="briar-headless" type="JetRunConfigurationType" factoryName="Kotlin" singleton="true">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<option name="VM_PARAMETERS" value="" />
|
||||
<option name="PROGRAM_PARAMETERS" value="-v" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="MAIN_CLASS_NAME" value="org.briarproject.briar.headless.MainKt" />
|
||||
<option name="WORKING_DIRECTORY" value="" />
|
||||
<module name="briar-headless" />
|
||||
<envs />
|
||||
<method>
|
||||
<option name="Gradle.BeforeRunTask" enabled="true" tasks="jar" externalProjectPath="$PROJECT_DIR$/briar-headless" vmOptions="" scriptParameters="" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -3,14 +3,14 @@ apply plugin: 'witness'
|
||||
apply from: 'witness.gradle'
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion '27.0.3'
|
||||
compileSdkVersion 28
|
||||
buildToolsVersion '28.0.3'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 26
|
||||
versionCode 10102
|
||||
versionName "1.1.2"
|
||||
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;
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -17,13 +17,13 @@ def getStdout = { command, defaultValue ->
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
buildToolsVersion '28.0.2'
|
||||
buildToolsVersion '28.0.3'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 26
|
||||
versionCode 10102
|
||||
versionName "1.1.2"
|
||||
versionCode 10103
|
||||
versionName "1.1.3"
|
||||
applicationId "org.briarproject.briar.android"
|
||||
buildConfigField "String", "GitHash",
|
||||
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
|
||||
@@ -90,7 +90,7 @@ dependencies {
|
||||
implementation project(path: ':bramble-core', configuration: 'default')
|
||||
implementation project(':bramble-android')
|
||||
|
||||
def supportVersion = '27.1.1'
|
||||
def supportVersion = '28.0.0'
|
||||
implementation "com.android.support:support-v4:$supportVersion"
|
||||
implementation("com.android.support:appcompat-v7:$supportVersion") {
|
||||
exclude module: 'support-v4'
|
||||
@@ -106,15 +106,15 @@ dependencies {
|
||||
implementation "com.android.support:support-annotations:$supportVersion"
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||
|
||||
implementation('ch.acra:acra:4.9.1') {
|
||||
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.0'
|
||||
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.8.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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -387,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"/>
|
||||
@@ -397,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,9 +21,14 @@ 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 {
|
||||
|
||||
@@ -42,7 +46,9 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle bundle, String s) {
|
||||
addPreferencesFromResource(R.xml.panic_preferences);
|
||||
}
|
||||
|
||||
private void updatePreferences() {
|
||||
pm = getActivity().getPackageManager();
|
||||
|
||||
lockPref = (SwitchPreference) findPreference(KEY_LOCK);
|
||||
@@ -74,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)
|
||||
@@ -83,21 +89,19 @@ 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)) {
|
||||
if (packageName.equals(PACKAGE_NAME_NONE)) {
|
||||
purgePref.setChecked(false);
|
||||
purgePref.setEnabled(false);
|
||||
getActivity().setResult(Activity.RESULT_CANCELED);
|
||||
getActivity().setResult(RESULT_CANCELED);
|
||||
} else {
|
||||
purgePref.setEnabled(true);
|
||||
}
|
||||
@@ -107,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +131,7 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
||||
super.onStart();
|
||||
getPreferenceScreen().getSharedPreferences()
|
||||
.registerOnSharedPreferenceChangeListener(this);
|
||||
updatePreferences();
|
||||
showPanicApp(PanicResponder.getTriggerPackageName(getActivity()));
|
||||
}
|
||||
|
||||
@@ -152,9 +159,9 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
||||
|
||||
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(
|
||||
@@ -176,8 +183,8 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,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();
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -41,13 +41,13 @@ public abstract class ShareActivity extends ContactSelectorActivity
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public boolean onButtonClick(String message) {
|
||||
share(contacts, message);
|
||||
public boolean onButtonClick(String text) {
|
||||
share(contacts, text);
|
||||
setResult(RESULT_OK);
|
||||
supportFinishAfterTransition();
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract void share(Collection<ContactId> contacts, String msg);
|
||||
abstract void share(Collection<ContactId> contacts, String text);
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||
import static org.briarproject.briar.api.sharing.SharingConstants.MAX_INVITATION_TEXT_LENGTH;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
@@ -46,13 +46,13 @@ public class ShareBlogActivity extends ShareActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumMessageLength() {
|
||||
return MAX_MESSAGE_BODY_LENGTH;
|
||||
public int getMaximumTextLength() {
|
||||
return MAX_INVITATION_TEXT_LENGTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
void share(Collection<ContactId> contacts, String msg) {
|
||||
controller.share(groupId, contacts, msg,
|
||||
void share(Collection<ContactId> contacts, String text) {
|
||||
controller.share(groupId, contacts, text,
|
||||
new UiExceptionHandler<DbException>(this) {
|
||||
@Override
|
||||
public void onExceptionUi(DbException exception) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.util.Collection;
|
||||
public interface ShareBlogController
|
||||
extends ContactSelectorController<SelectableContactItem> {
|
||||
|
||||
void share(GroupId g, Collection<ContactId> contacts, String msg,
|
||||
void share(GroupId g, Collection<ContactId> contacts, String text,
|
||||
ExceptionHandler<DbException> handler);
|
||||
|
||||
}
|
||||
|
||||
@@ -56,17 +56,17 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void share(GroupId g, Collection<ContactId> contacts, String message,
|
||||
public void share(GroupId g, Collection<ContactId> contacts, String text,
|
||||
ExceptionHandler<DbException> handler) {
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
String msg = isNullOrEmpty(message) ? null : message;
|
||||
String txt = isNullOrEmpty(text) ? null : text;
|
||||
for (ContactId c : contacts) {
|
||||
try {
|
||||
long time = Math.max(clock.currentTimeMillis(),
|
||||
conversationManager.getGroupCount(c)
|
||||
.getLatestMsgTime() + 1);
|
||||
blogSharingManager.sendInvitation(g, c, msg, time);
|
||||
blogSharingManager.sendInvitation(g, c, txt, time);
|
||||
} catch (NoSuchContactException | NoSuchGroupException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||
import static org.briarproject.briar.api.sharing.SharingConstants.MAX_INVITATION_TEXT_LENGTH;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
@@ -46,13 +46,13 @@ public class ShareForumActivity extends ShareActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumMessageLength() {
|
||||
return MAX_MESSAGE_BODY_LENGTH;
|
||||
public int getMaximumTextLength() {
|
||||
return MAX_INVITATION_TEXT_LENGTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
void share(Collection<ContactId> contacts, String msg) {
|
||||
controller.share(groupId, contacts, msg,
|
||||
void share(Collection<ContactId> contacts, String text) {
|
||||
controller.share(groupId, contacts, text,
|
||||
new UiExceptionHandler<DbException>(this) {
|
||||
@Override
|
||||
public void onExceptionUi(DbException exception) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.util.Collection;
|
||||
public interface ShareForumController
|
||||
extends ContactSelectorController<SelectableContactItem> {
|
||||
|
||||
void share(GroupId g, Collection<ContactId> contacts, String msg,
|
||||
void share(GroupId g, Collection<ContactId> contacts, String text,
|
||||
ExceptionHandler<DbException> handler);
|
||||
|
||||
}
|
||||
|
||||
@@ -57,16 +57,16 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
|
||||
|
||||
@Override
|
||||
public void share(GroupId g, Collection<ContactId> contacts,
|
||||
String message, ExceptionHandler<DbException> handler) {
|
||||
String text, ExceptionHandler<DbException> handler) {
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
String msg = isNullOrEmpty(message) ? null : message;
|
||||
String txt = isNullOrEmpty(text) ? null : text;
|
||||
for (ContactId c : contacts) {
|
||||
try {
|
||||
long time = Math.max(clock.currentTimeMillis(),
|
||||
conversationManager.getGroupCount(c)
|
||||
.getLatestMsgTime() + 1);
|
||||
forumSharingManager.sendInvitation(g, c, msg, time);
|
||||
forumSharingManager.sendInvitation(g, c, txt, time);
|
||||
} catch (NoSuchContactException | NoSuchGroupException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.BriarActivity;
|
||||
import org.briarproject.briar.android.controller.SharingController;
|
||||
@@ -43,6 +42,7 @@ import static android.support.design.widget.Snackbar.make;
|
||||
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
||||
import static android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static org.briarproject.bramble.util.StringUtils.utf8IsTooLong;
|
||||
import static org.briarproject.briar.android.threaded.ThreadItemAdapter.UnreadCount;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@@ -351,7 +351,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
||||
public void onSendClick(String text) {
|
||||
if (text.trim().length() == 0)
|
||||
return;
|
||||
if (StringUtils.utf8IsTooLong(text, getMaxBodyLength())) {
|
||||
if (utf8IsTooLong(text, getMaxTextLength())) {
|
||||
displaySnackbar(R.string.text_too_long);
|
||||
return;
|
||||
}
|
||||
@@ -375,7 +375,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
||||
updateTextInput();
|
||||
}
|
||||
|
||||
protected abstract int getMaxBodyLength();
|
||||
protected abstract int getMaxTextLength();
|
||||
|
||||
@Override
|
||||
public void onItemReceived(I item) {
|
||||
|
||||
@@ -35,7 +35,7 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
|
||||
|
||||
void markItemsRead(Collection<I> items);
|
||||
|
||||
void createAndStoreMessage(String body, @Nullable I parentItem,
|
||||
void createAndStoreMessage(String text, @Nullable I parentItem,
|
||||
ResultExceptionHandler<I, DbException> handler);
|
||||
|
||||
void deleteNamedGroup(ExceptionHandler<DbException> handler);
|
||||
|
||||
@@ -50,7 +50,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
Logger.getLogger(ThreadListControllerImpl.class.getName());
|
||||
|
||||
private final EventBus eventBus;
|
||||
private final Map<MessageId, String> bodyCache = new ConcurrentHashMap<>();
|
||||
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>();
|
||||
private volatile GroupId groupId;
|
||||
|
||||
protected final IdentityManager identityManager;
|
||||
@@ -161,9 +161,9 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
// Load bodies into cache
|
||||
start = now();
|
||||
for (H header : headers) {
|
||||
if (!bodyCache.containsKey(header.getId())) {
|
||||
bodyCache.put(header.getId(),
|
||||
loadMessageBody(header));
|
||||
if (!textCache.containsKey(header.getId())) {
|
||||
textCache.put(header.getId(),
|
||||
loadMessageText(header));
|
||||
}
|
||||
}
|
||||
logDuration(LOG, "Loading bodies", start);
|
||||
@@ -181,7 +181,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
protected abstract Collection<H> loadHeaders() throws DbException;
|
||||
|
||||
@DatabaseExecutor
|
||||
protected abstract String loadMessageBody(H header) throws DbException;
|
||||
protected abstract String loadMessageText(H header) throws DbException;
|
||||
|
||||
@Override
|
||||
public void markItemRead(I item) {
|
||||
@@ -206,15 +206,15 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
@DatabaseExecutor
|
||||
protected abstract void markRead(MessageId id) throws DbException;
|
||||
|
||||
protected void storePost(M msg, String body,
|
||||
protected void storePost(M msg, String text,
|
||||
ResultExceptionHandler<I, DbException> resultHandler) {
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
long start = now();
|
||||
H header = addLocalMessage(msg);
|
||||
bodyCache.put(msg.getMessage().getId(), body);
|
||||
textCache.put(msg.getMessage().getId(), text);
|
||||
logDuration(LOG, "Storing message", start);
|
||||
resultHandler.onResult(buildItem(header, body));
|
||||
resultHandler.onResult(buildItem(header, text));
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
resultHandler.onException(e);
|
||||
@@ -247,7 +247,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
throws DbException {
|
||||
ThreadItemList<I> items = new ThreadItemListImpl<>();
|
||||
for (H h : headers) {
|
||||
items.add(buildItem(h, bodyCache.get(h.getId())));
|
||||
items.add(buildItem(h, textCache.get(h.getId())));
|
||||
}
|
||||
MessageId msgId = messageTracker.loadStoredMessageId(groupId);
|
||||
if (LOG.isLoggable(INFO))
|
||||
@@ -256,7 +256,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
return items;
|
||||
}
|
||||
|
||||
protected abstract I buildItem(H header, String body);
|
||||
protected abstract I buildItem(H header, String text);
|
||||
|
||||
protected GroupId getGroupId() {
|
||||
checkGroupId();
|
||||
|
||||
@@ -8,6 +8,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
@@ -45,7 +47,8 @@ public class ThreadPostViewHolder<I extends ThreadItem>
|
||||
}
|
||||
if (item.getLevel() > 5) {
|
||||
lvlText.setVisibility(VISIBLE);
|
||||
lvlText.setText(String.valueOf(item.getLevel()));
|
||||
lvlText.setText(
|
||||
String.format(Locale.getDefault(), "%d", item.getLevel()));
|
||||
} else {
|
||||
lvlText.setVisibility(GONE);
|
||||
}
|
||||
|
||||
@@ -106,13 +106,13 @@ public class UiUtils {
|
||||
return (int) daysBeforeExpiry;
|
||||
}
|
||||
|
||||
public static SpannableStringBuilder getTeaser(Context ctx, Spanned body) {
|
||||
if (body.length() < TEASER_LENGTH)
|
||||
public static SpannableStringBuilder getTeaser(Context ctx, Spanned text) {
|
||||
if (text.length() < TEASER_LENGTH)
|
||||
throw new IllegalArgumentException(
|
||||
"String is shorter than TEASER_LENGTH");
|
||||
|
||||
SpannableStringBuilder builder =
|
||||
new SpannableStringBuilder(body.subSequence(0, TEASER_LENGTH));
|
||||
new SpannableStringBuilder(text.subSequence(0, TEASER_LENGTH));
|
||||
String ellipsis = ctx.getString(R.string.ellipsis);
|
||||
builder.append(ellipsis).append(" ");
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ import android.widget.TextView;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import de.hdodenhof.circleimageview.CircleImageView;
|
||||
@@ -44,7 +46,7 @@ public class TextAvatarView extends FrameLayout {
|
||||
|
||||
public void setUnreadCount(int count) {
|
||||
if (count > 0) {
|
||||
badge.setText(String.valueOf(count));
|
||||
badge.setText(String.format(Locale.getDefault(), "%d", count));
|
||||
badge.setVisibility(VISIBLE);
|
||||
} else {
|
||||
badge.setVisibility(INVISIBLE);
|
||||
|
||||
@@ -12,6 +12,8 @@ import android.widget.TextView;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@UiThread
|
||||
@@ -69,7 +71,7 @@ public class UnreadMessageButton extends FrameLayout {
|
||||
} else {
|
||||
fab.show();
|
||||
unread.setVisibility(VISIBLE);
|
||||
unread.setText(String.valueOf(count));
|
||||
unread.setText(String.format(Locale.getDefault(), "%d", count));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
tools:context=".android.blog.WriteBlogPostActivity">
|
||||
|
||||
<org.briarproject.briar.android.view.LargeTextInputView
|
||||
android:id="@+id/bodyInput"
|
||||
android:id="@+id/textInput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="bottom"
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:keepScreenOn="true">
|
||||
|
||||
<org.briarproject.briar.android.keyagreement.CameraView
|
||||
android:id="@+id/camera_view"
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
app:persona="commenter"/>
|
||||
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/bodyView"
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="@dimen/listitem_vertical_margin"
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
app:tint="?attr/colorControlNormal"/>
|
||||
|
||||
<com.vanniktech.emoji.EmojiTextView
|
||||
android:id="@+id/bodyView"
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/listitem_vertical_margin"
|
||||
@@ -72,7 +72,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/listitem_vertical_margin"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toBottomOf="@+id/bodyView">
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView">
|
||||
|
||||
<include
|
||||
layout="@layout/list_item_blog_comment"
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<string name="setup_name_explanation">سيتم إظهار اسمك المستعار بجانب كل ما تنشره من محتوى. لا يمكنك تغيير الاسم بعد إنشاء حسابك.</string>
|
||||
<string name="setup_next">التالي</string>
|
||||
<string name="setup_password_intro">اختيار كلمة السر</string>
|
||||
<string name="setup_password_explanation">حسابك على Briar (براير) سيتم تخزينه مشفرا على جهازك، وليس على الإنترنت. لذا فإنك إذا نسيت كلمة السر الخاصة بك أو قمت بإلغاء تثبيت Briar (براير)، فلا توجد طريقه لاسترجاع حسابك.\n\nاختر كلمة سر طويلة يصعب تخمينها مثل أربعة كلمات عشوائية أو عشر حروف عشوائية مع أرقام ورموز.</string>
|
||||
<string name="setup_doze_title">اتصالات الخلفية</string>
|
||||
<string name="setup_password_explanation">سيتم تخزين حسابك على Briar (براير) مشفرا على جهازك، وليس على الإنترنت. لذا في حالة نسيان كلمة السر الخاصة بك أو إلغاء تثبيت Briar (براير) لا توجد طريقه لاسترجاع حسابك.\n\nالرجاء اختيار كلمة سر طويلة يصعب تخمينها مثل أربع كلمات عشوائية أو عشرة حروف عشوائية مع أرقام ورموز.</string>
|
||||
<string name="setup_doze_title">الاتصالات الخلفية</string>
|
||||
<string name="setup_doze_intro">لاستقبال الرسائل، يحتاج Briar (براير) أن يبقى متصلا في الخلفية.</string>
|
||||
<string name="setup_doze_explanation">لاستقبال الرسائل، يحتاج Briar (براير) أن يبقى متصلا في الخلفية. الرجاء تعطيل تحسين البطارية حتى يتمكن Briar (براير) من البقاء متصلا.</string>
|
||||
<string name="setup_doze_button">السماح بالاتصالات</string>
|
||||
@@ -19,10 +19,10 @@
|
||||
<string name="create_account_button">إنشاء الحساب</string>
|
||||
<string name="more_info">معلومات أكثر</string>
|
||||
<string name="don_t_ask_again">عدم الطلب مرة أخرى</string>
|
||||
<string name="setup_huawei_text">الرجاء الظغط على الزر في الأسفل والتأكد من أن Briar (براير) محمى في شاشة \"التطبيقات المحمية\".</string>
|
||||
<string name="setup_huawei_text">الرجاء الضغط على الزر في الأسفل والتأكد من أن Briar (براير) محمى في شاشة \"التطبيقات المحمية\".</string>
|
||||
<string name="setup_huawei_button">حماية Briar (براير)</string>
|
||||
<string name="setup_huawei_help">إذا لم يتم إضافة Briar (براير) في قائمة التطبيقات المحمية، فلن يتمكن من العمل في الخلفية.</string>
|
||||
<string name="warning_dozed">%s لم يمكن تشغيله في الخلفية</string>
|
||||
<string name="warning_dozed">%s لم يتمكن من الاشتغال في الخلفية</string>
|
||||
<!--Login-->
|
||||
<string name="enter_password">كلمة السّر</string>
|
||||
<string name="try_again">كلمة السرّ خاطئة, الرجاء المحاولة مجدّدا</string>
|
||||
@@ -31,12 +31,12 @@
|
||||
<string name="dialog_title_lost_password">فقدت كلمة السر</string>
|
||||
<string name="dialog_message_lost_password">حسابك على Briar (براير) سيتم تخزينه مشفرا على جهازك، وليس على الإنترنت. لذا فلا يمكننا إعادة تعيين كلمة السر الخاصة بك. فهل تودّ/ين حذف حسابك والبدء من جديد؟\n\nتحذير: سيتمّ حذف هويّاتك وجهات اتصالك ورسائلك الى الأبد.</string>
|
||||
<string name="startup_failed_notification_title">Briar (براير) لم يمكنه البدء</string>
|
||||
<string name="startup_failed_notification_text">المس لمزيد من المعلومات</string>
|
||||
<string name="startup_failed_notification_text">المس/ي لمزيد من المعلومات</string>
|
||||
<string name="startup_failed_activity_title">فشل تشغيل Briar (براير)</string>
|
||||
<string name="startup_failed_db_error">لسبب ما، فإن قاعدة بيانات Briar (براير) قد تلفت ولم يعد من الممكن إصلاحها. حسابك وبياناتك وكل جهات اتصالك قد فقدت. للأسف، يلزم إعادة تثبيت Briar (براير) أو إنشاء حساب جديد عن طريق خيار \"نسيت كلمة السر\" عند سؤالك عنها.</string>
|
||||
<string name="startup_failed_data_too_old_error">حسابك تم إنشاؤه بواسطة إصدار قديم من هذا التطبيق ولا يمكن فتحه بهذا الإصدار. يتوجب إعادة تثبيت الإصدار القديم أو أن تنشئ حسابا جديدًا باختيار \"نسيت كلمة السر\" عند سؤالك عنها.</string>
|
||||
<string name="startup_failed_data_too_new_error">هذا الإصدار من التطبيق قديم جدًا. من فضلك قم بالتحديث لآخر إصدار ثم حاول مجددًا.</string>
|
||||
<string name="startup_failed_service_error">Briar (براير) لم يستطع تشغيل إضافة لازمة. إعادة تشغيل Briar (براير) يحل مثل هذه المشكلة عادة. لكن ذلك يعني أنك ستفقد حسابك وكل بياناتك عليه حيث أن Briar (براير) لا يستعمل خوادم مركزية لتخزين بياناتك.</string>
|
||||
<string name="startup_failed_data_too_new_error">هذا الإصدار من التطبيق قديم جدًا. الرجاء التحديث لآخر إصدار ثم المحاولة مجددًا.</string>
|
||||
<string name="startup_failed_service_error">Briar (براير) لم يستطع تشغيل إضافة لازمة. إعادة تشغيل Briar (براير) يحل مثل هذه المشكلة عادة. لكن ذلك يعني أنك فقدان حسابك وكل بياناتك عليه حيث أن Briar (براير) لا يستعمل خوادم مركزية لتخزين بياناتك.</string>
|
||||
<plurals name="expiry_warning">
|
||||
<item quantity="zero">هذا إصدار تجريبي من Briar (براير). حسابك سينتهي اليوم ولن يمكن تجديده.</item>
|
||||
<item quantity="one">هذا إصدار تجريبي من Briar (براير). حسابك سينتهي خلال يوم ولن يمكن تجديده.</item>
|
||||
@@ -47,14 +47,14 @@
|
||||
</plurals>
|
||||
<string name="expiry_update">تم تمديد تاريخ انتهاء التجربة. حسابك الآن سوف ينتهي بعد %d يوم.</string>
|
||||
<string name="expiry_date_reached">هذا البرنامج قد انتهت مدته.\nشكرا لك للتجربة!</string>
|
||||
<string name="download_briar">لتستمر في إستعمال Briar (براير)، من فضلك قم بتحميل إصدار رقم 1.0.</string>
|
||||
<string name="create_new_account">تحتاج أن تنشئ حساب جديد، لكن يمكنك استعمال نفس الاسم المستعار.</string>
|
||||
<string name="download_briar">للاستمرار في إستعمال Briar (براير)، من فضلك قم بتحميل إصدار رقم 1.0.</string>
|
||||
<string name="create_new_account">انت في حاجة لانشاء حساب جديد، لكن يمكنك استعمال نفس الاسم المستعار.</string>
|
||||
<string name="download_briar_button">تحميل Briar (براير) 1.0</string>
|
||||
<string name="startup_open_database">جارِ فك تشفير قاعدة البيانات...</string>
|
||||
<string name="startup_migrate_database">جارِ ترقية قاعدة البيانات...</string>
|
||||
<!--Navigation Drawer-->
|
||||
<string name="nav_drawer_open_description">إفتح راسم التصفح</string>
|
||||
<string name="nav_drawer_close_description">أغلق راسم التصفح</string>
|
||||
<string name="nav_drawer_open_description">فتح راسم التصفح</string>
|
||||
<string name="nav_drawer_close_description">غلق راسم التصفح</string>
|
||||
<string name="contact_list_button">جهات الاتصال</string>
|
||||
<string name="groups_button">المجموعات الخاصة</string>
|
||||
<string name="forums_button">المنتديات</string>
|
||||
@@ -64,16 +64,16 @@
|
||||
<string name="settings_button">الإعدادات</string>
|
||||
<string name="sign_out_button">تسجيل الخروج</string>
|
||||
<!--Transports-->
|
||||
<string name="transport_tor">الإنترنت</string>
|
||||
<string name="transport_tor">إنترنت</string>
|
||||
<string name="transport_bt">بلوتوث</string>
|
||||
<string name="transport_lan">واي-فاي</string>
|
||||
<!--Notifications-->
|
||||
<string name="reminder_notification_title">تم تسجيل الخروج من Briar (براير)</string>
|
||||
<string name="reminder_notification_text">المس لإعادة الدخول</string>
|
||||
<string name="reminder_notification_text">الرجاء اللمس لإعادة الدخول</string>
|
||||
<string name="reminder_notification_channel_title">التذكير بتسجيل الدخول إلى Briar (براير)</string>
|
||||
<string name="reminder_notification_dismiss">رفض</string>
|
||||
<string name="ongoing_notification_title">سجِل الدخول إلى Briar (براير)</string>
|
||||
<string name="ongoing_notification_text">المس لفتح Briar (براير).</string>
|
||||
<string name="ongoing_notification_title">تم تسجيل الدخول إلى Briar (براير)</string>
|
||||
<string name="ongoing_notification_text">الرجاء اللمس لفتح Briar (براير).</string>
|
||||
<plurals name="private_message_notification_text">
|
||||
<item quantity="zero">لا رسائل خاصة جديدة.</item>
|
||||
<item quantity="one"> رسالة خاصة جديدة.</item>
|
||||
@@ -110,41 +110,41 @@
|
||||
<string name="now">الآن</string>
|
||||
<string name="show">عرض</string>
|
||||
<string name="hide">إخفاء</string>
|
||||
<string name="ok">موافق</string>
|
||||
<string name="ok">موافقة</string>
|
||||
<string name="cancel">إلغاء</string>
|
||||
<string name="got_it">فهمت ذلك</string>
|
||||
<string name="delete">حذف</string>
|
||||
<string name="accept">قبول</string>
|
||||
<string name="decline">إرفض</string>
|
||||
<string name="decline">رفض</string>
|
||||
<string name="options">الخيارات</string>
|
||||
<string name="online">متصل</string>
|
||||
<string name="offline">غير متصل</string>
|
||||
<string name="send">أرسلْ</string>
|
||||
<string name="allow">إسمح</string>
|
||||
<string name="send">ارسال</string>
|
||||
<string name="allow">السماح</string>
|
||||
<string name="open">فتح</string>
|
||||
<string name="no_data">لا يوجد بيانات</string>
|
||||
<string name="ellipsis">...</string>
|
||||
<string name="text_too_long">النص المُدخل طويل جدًا</string>
|
||||
<string name="show_onboarding">أظهر نافذة المساعدة</string>
|
||||
<string name="show_onboarding">اظهار نافذة المساعدة</string>
|
||||
<string name="fix">إصلاح</string>
|
||||
<string name="help">مساعدة</string>
|
||||
<string name="sorry">معذرة</string>
|
||||
<!--Contacts and Private Conversations-->
|
||||
<string name="no_contacts">لا جهات اتصال للعرض</string>
|
||||
<string name="no_contacts_action">المس علامة + لإضافة جهة إتصال</string>
|
||||
<string name="no_contacts_action">لمس علامة + لإضافة جهة إتصال</string>
|
||||
<string name="date_no_private_messages">لا رسائل.</string>
|
||||
<string name="no_private_messages">لا رسائل للعرض.</string>
|
||||
<string name="message_hint">أكتب رسالة</string>
|
||||
<string name="message_hint">كتابة رسالة</string>
|
||||
<string name="delete_contact">حذف جهة الإتصال</string>
|
||||
<string name="dialog_title_delete_contact">تأكيد حذف جهة الإتصال</string>
|
||||
<string name="dialog_message_delete_contact">هل متأكد من أنك تريد حذف جهة الإتصال هذه وكل الرسائل المتبادلة بينكما؟</string>
|
||||
<string name="dialog_message_delete_contact">هل أنت متأكد/ة من أنك تريد حذف جهة الإتصال هذه وكل الرسائل المتبادلة بينكما؟</string>
|
||||
<string name="contact_deleted_toast">تم حذف جهة اتصال</string>
|
||||
<!--Adding Contacts-->
|
||||
<string name="add_contact_title">إضافة جهة اتصال</string>
|
||||
<string name="face_to_face">لابد أن تقابل الشخص الذي تريد إضافته كجهة اتصال.\n\nهذا سيمنع أي شخص من انتحال شخصيتك أو قراءة رسائلك في المستقبل.</string>
|
||||
<string name="face_to_face">لا بد من مقابلة الشخص الذي تريد/ين إضافته كجهة اتصال.\n\nهذا سيمنع أي شخص من انتحال شخصيتك أو قراءة رسائلك في المستقبل.</string>
|
||||
<string name="continue_button">إستمرار</string>
|
||||
<string name="try_again_button">حاول مجددًا</string>
|
||||
<string name="waiting_for_contact_to_scan">بإنتظار جهة الإتصال ليقوم بالمسح والإتصال\u2026</string>
|
||||
<string name="try_again_button">الرجاء المحاولة مجددًا</string>
|
||||
<string name="waiting_for_contact_to_scan">بإنتظار أن تقوم جهة الإتصال بالمسح والإتصال\u2026</string>
|
||||
<string name="exchanging_contact_details">يتم تبادل معلومات جهة الإتصال\u2026</string>
|
||||
<string name="contact_added_toast">تم إضافة جهة إتصال: %s</string>
|
||||
<string name="contact_already_exists">جهة الإتصال %s بالفعل موجودة</string>
|
||||
@@ -172,11 +172,11 @@
|
||||
<string name="introduction_request_exists_received">قد طلب %1$s أن يقدمك إلى %2$s، لكن %2$s هو بالفعل في قائمة جهات اتصالك. حيث أن %1$s قد لا يعرف هذا، فلا زال يمكنك الرد:</string>
|
||||
<string name="introduction_request_answered_received">لقد طلب %1$s أن يقوم بتقديمك إلى %2$s.</string>
|
||||
<string name="introduction_response_accepted_sent">لقد قبلت التقديم إلى %1$s.</string>
|
||||
<string name="introduction_response_accepted_sent_info">قبل أن يتم إضافة %1$s إلى جهات اتصالك، يحتاج أن يقبل هو أيضًا التقديم. هذا يمكن أن يستغرق بعض الوقت.</string>
|
||||
<string name="introduction_response_accepted_sent_info">قبل أن يتم إضافة %1$s إلى جهات اتصالك، يحتاج أن ي/تقبل هو/هي أيضًا التقديم. هذا يمكن أن يستغرق بعض الوقت.</string>
|
||||
<string name="introduction_response_declined_sent">لقد رفضت التقديم إلى %1$s.</string>
|
||||
<string name="introduction_response_accepted_received">لقد وافق %1$s على تقديمه إلى %2$s.</string>
|
||||
<string name="introduction_response_declined_received">لقد رفض %1$sتقديمه إلى %2$s.</string>
|
||||
<string name="introduction_response_declined_received_by_introducee">يقول %1$sأن %2$s قد رفض التقدمة.</string>
|
||||
<string name="introduction_response_accepted_received">لقد وافق/ت %1$s على تقديمه/ها إلى %2$s.</string>
|
||||
<string name="introduction_response_declined_received">لقد رفض/ت %1$sتقديمه/ا إلى %2$s.</string>
|
||||
<string name="introduction_response_declined_received_by_introducee">ي/تقول %1$sأن %2$s قد رفض/ت التقدمة.</string>
|
||||
<plurals name="introduction_notification_text">
|
||||
<item quantity="zero">لا جهة اتصال تم إضافتها.</item>
|
||||
<item quantity="one">جهة اتصال تم إضافتها.</item>
|
||||
@@ -187,7 +187,7 @@
|
||||
</plurals>
|
||||
<!--Private Groups-->
|
||||
<string name="groups_list_empty">لا مجموعات للعرض</string>
|
||||
<string name="groups_list_empty_action">المس علامة + لإنشاء مجموعة، أو اطلب من جهات إتصالك مشاركتك في مجموعاتهم</string>
|
||||
<string name="groups_list_empty_action">الرجاء لمس علامة + لإنشاء مجموعة، أو اطلب من جهات إتصالك مشاركتك في مجموعاتهم</string>
|
||||
<string name="groups_created_by">تم إنشاؤها بواسطة %s</string>
|
||||
<plurals name="messages">
|
||||
<item quantity="zero">%d رسالة</item>
|
||||
@@ -200,26 +200,26 @@
|
||||
<string name="groups_group_is_empty">هذه المجموعة فارغة</string>
|
||||
<string name="groups_group_is_dissolved">هذه المجموعة قد تلاشت</string>
|
||||
<string name="groups_remove">حذف</string>
|
||||
<string name="groups_create_group_title">قم بإنشاء مجموعة خاصة</string>
|
||||
<string name="groups_create_group_button">أنشئ مجموعة</string>
|
||||
<string name="groups_create_group_invitation_button">أرسل دعوة</string>
|
||||
<string name="groups_create_group_hint">اختر اسمًا لمجموعتك الخاصة</string>
|
||||
<string name="groups_create_group_title">إنشاء مجموعة خاصة</string>
|
||||
<string name="groups_create_group_button">إنشاء مجموعة</string>
|
||||
<string name="groups_create_group_invitation_button">إرسال دعوة</string>
|
||||
<string name="groups_create_group_hint">اختيار اسم لمجموعتك الخاصة</string>
|
||||
<string name="groups_invitation_sent">تم إرسال دعوة للمجموعة</string>
|
||||
<string name="groups_message_sent">تم إرسال الرسالة</string>
|
||||
<string name="groups_member_list">قائمة الأعضاء</string>
|
||||
<string name="groups_invite_members">دعوة أعضاء</string>
|
||||
<string name="groups_member_created_you">أنت أنشأت المجموعة</string>
|
||||
<string name="groups_member_created">%s أنشأ المجموعة</string>
|
||||
<string name="groups_member_joined_you">قد انضممت للمجموعة</string>
|
||||
<string name="groups_member_joined">قد انضم %s للمجموعة</string>
|
||||
<string name="groups_leave">غادر المجموعة</string>
|
||||
<string name="groups_member_joined_you">قد انضممت/ي للمجموعة</string>
|
||||
<string name="groups_member_joined">قد انضم/ت %s للمجموعة</string>
|
||||
<string name="groups_leave">مغادرة المجموعة</string>
|
||||
<string name="groups_leave_dialog_title">تأكيد مغادرة المجموعة</string>
|
||||
<string name="groups_leave_dialog_message">هل متأكد من مغادرة هذه المجموعة؟</string>
|
||||
<string name="groups_leave_dialog_message">هل أنت متأكد/ة من مغادرة هذه المجموعة؟</string>
|
||||
<string name="groups_dissolve">حلّ المجموعة</string>
|
||||
<string name="groups_dissolve_dialog_title">أكد حلّ وتلاشي المجموعة</string>
|
||||
<string name="groups_dissolve_dialog_message">هل متأكد أنك تريد حلّ هذه المجموعة؟\n\nجميع الأعضاء الآخرين لن يتمكنوا من استكمال حوارهم وربما لن يستقبلوا الرسائل الأخيرة.</string>
|
||||
<string name="groups_dissolve_dialog_title">تأكيد حلّ المجموعة</string>
|
||||
<string name="groups_dissolve_dialog_message">هل تريد/ين فعلا حلّ هذه المجموعة؟\n\nجميع الأعضاء الآخرين لن يتمكنوا من استكمال حوارهم وربما لن يستقبلوا الرسائل الأخيرة.</string>
|
||||
<string name="groups_dissolve_button">حلّ</string>
|
||||
<string name="groups_dissolved_dialog_title">تم حلّ المجموعة وتلاشيها</string>
|
||||
<string name="groups_dissolved_dialog_title">تم حلّ المجموعة </string>
|
||||
<string name="groups_dissolved_dialog_message">منشئ هذه المجموعة قام بحلّها.\n\n لن يمكنك كتابة رسائل إلى المجموعة كما ويحتمل أنك لم تستقبل جميع الرسائل التي تم كتابتها.</string>
|
||||
<!--Private Group Invitations-->
|
||||
<string name="groups_invitations_title">دعوات المجموعة</string>
|
||||
@@ -233,26 +233,27 @@
|
||||
<item quantity="two">دعوتيْ مجموعة مفتوحتين</item>
|
||||
<item quantity="few">%d دعوات مجموعة مفتوحة</item>
|
||||
<item quantity="many">%d دعوة مجموعة مفتوحة</item>
|
||||
<item quantity="other">%d دعوة مجموعة مفتوحة</item>
|
||||
<item quantity="other">%d
|
||||
فتح دعوات المجموعات</item>
|
||||
</plurals>
|
||||
<string name="groups_invitations_response_accepted_sent">قد قبلت دعوة الانضمام للمجموعة من %s.</string>
|
||||
<string name="groups_invitations_response_declined_sent">قد رفضت دعوة الانضمام للمجموعة من %s.</string>
|
||||
<string name="groups_invitations_response_accepted_received">قبل %s دعوة الانضمام للمجموعة.</string>
|
||||
<string name="groups_invitations_response_declined_received">رفض %s دعوة الانضمام للمجموعة.</string>
|
||||
<string name="sharing_status_groups">فقط المُنشئ يمكنه دعوة أعضاء جدد للمجموعة. في الأسفل جميع الأعضاء الحاليين بالمجموعة.</string>
|
||||
<string name="groups_invitations_response_accepted_received">قبل/ت %s دعوة الانضمام للمجموعة.</string>
|
||||
<string name="groups_invitations_response_declined_received">رفض/ت %s دعوة الانضمام للمجموعة.</string>
|
||||
<string name="sharing_status_groups">فقط مُنشئ/ة المجموعة يمكنه/ها دعوة أعضاء جدد. في الأسفل جميع الأعضاء الحاليين بالمجموعة.</string>
|
||||
<!--Private Groups Revealing Contacts-->
|
||||
<string name="groups_reveal_contacts">أظهر الأعضاء</string>
|
||||
<string name="groups_reveal_dialog_message">يمكن أن تختار إظهار جميع جهات الاتصال الحالية والمستقبلية في هذه المجموعة لبعضهم البعض.\n\nإظهار جهات الاتصال سيجعل اتصالك بالمجموعة أسرع وأفضل اعتمادية، حيث سيمكن التواصل بين جهات الاتصال الظاهرة حتى لو كان منشئ المجموعة غير متصل.</string>
|
||||
<string name="groups_reveal_contacts">اظهار الأعضاء</string>
|
||||
<string name="groups_reveal_dialog_message">يمكن اختيار إظهار جميع جهات الاتصال الحالية والمستقبلية في هذه المجموعة لبعضهم البعض.\n\nإظهار جهات الاتصال سيجعل اتصالك بالمجموعة أسرع وأفضل اعتمادية، حيث سيمكن التواصل بين جهات الاتصال الظاهرة حتى لو كان منشئ المجموعة غير متصل.</string>
|
||||
<string name="groups_reveal_visible">علاقات جهات الاتصال ستكون ظاهرة للمجموعة</string>
|
||||
<string name="groups_reveal_visible_revealed_by_us">علاقات جهات الاتصال ظاهرة للمجموعة (قمت أنت بكشفها)</string>
|
||||
<string name="groups_reveal_visible_revealed_by_contact">علاقات جهات الاتصال ظاهرة للمجموعة (قام %s بكشفها)</string>
|
||||
<string name="groups_reveal_visible_revealed_by_us">علاقات جهات الاتصال ظاهرة للمجموعة (قمت/ي أنت بكشفها)</string>
|
||||
<string name="groups_reveal_visible_revealed_by_contact">علاقات جهات الاتصال ظاهرة للمجموعة (قام/ت %s بكشفها)</string>
|
||||
<string name="groups_reveal_invisible">علاقات جهات الاتصال غير ظاهرة للمجموعة</string>
|
||||
<!--Forums-->
|
||||
<string name="no_forums">لا منتديات للعرض</string>
|
||||
<string name="no_forums_action">المس علامة + لإنشاء منتدى، أو اطلب من جهات اتصالك مشاركتك في منتدياتهم</string>
|
||||
<string name="create_forum_title">أنشئ منتدىَ</string>
|
||||
<string name="choose_forum_hint">اختر إسم لمنتداك</string>
|
||||
<string name="create_forum_button">أنشئ منتدىَ</string>
|
||||
<string name="no_forums_action">الرجاء لمس علامة + لإنشاء منتدى، أو طلب من جهات اتصالك مشاركتك في منتدياتهم</string>
|
||||
<string name="create_forum_title">انشاء منتدى</string>
|
||||
<string name="choose_forum_hint">اختيار إسم لمنتداك</string>
|
||||
<string name="create_forum_button">انشاء منتدى</string>
|
||||
<string name="forum_created_toast">تم إنشاء المنتدى</string>
|
||||
<string name="no_forum_posts">لا منشورات للعرض</string>
|
||||
<string name="no_posts">لا منشورات</string>
|
||||
@@ -270,7 +271,7 @@
|
||||
<string name="btn_reply">الرد</string>
|
||||
<string name="forum_leave">مغادرة المنتدى</string>
|
||||
<string name="dialog_title_leave_forum">تأكيد مغادرة المنتدى</string>
|
||||
<string name="dialog_message_leave_forum">هل متأكد من رغبتك في مغادرة المنتدى؟\n\nأي جهة اتصال قمت بمشاركة هذا المنتدى معها قد يتوقف عنها استلام التحديثات.</string>
|
||||
<string name="dialog_message_leave_forum">هل أنت متأكد/ة من رغبتك في مغادرة المنتدى؟\n\nأي جهة اتصال قمت/ي بمشاركة هذا المنتدى معها قد يتوقف عنها استلام التحديثات.</string>
|
||||
<string name="dialog_button_leave">مغادرة</string>
|
||||
<string name="forum_left_toast">تم مغادرة المنتدى</string>
|
||||
<!--Forum Sharing-->
|
||||
@@ -278,25 +279,25 @@
|
||||
<string name="contacts_selected">جهات الاتصال المختارة</string>
|
||||
<string name="activity_share_toolbar_header">إختر جهات الاتصال</string>
|
||||
<string name="no_contacts_selector">لا جهات إتصال للعرض</string>
|
||||
<string name="no_contacts_selector_action">رجاءًا العودة لاحقًا هنا بعد إضافة جهة إتصال</string>
|
||||
<string name="no_contacts_selector_action">الرجاء العودة لاحقًا هنا بعد إضافة جهة إتصال</string>
|
||||
<string name="forum_shared_snackbar">تم مشاركة المنتدى مع جهات الاتصال المختارة</string>
|
||||
<string name="forum_share_message">أضف رسالة (إختياري)</string>
|
||||
<string name="forum_share_message">اضافة رسالة (إختياري)</string>
|
||||
<string name="forum_share_error">حدث خطأ أثناء مشاركة المنتدى</string>
|
||||
<string name="forum_invitation_received">قام %1$s بمشاركة المنتدى \"%2$s\" معك.</string>
|
||||
<string name="forum_invitation_sent">لقد قمت بمشاركة المنتدى \"%1$s\" مع %2$s.</string>
|
||||
<string name="forum_invitation_received">قام/ت %1$s بمشاركة المنتدى \"%2$s\" معك.</string>
|
||||
<string name="forum_invitation_sent">لقد قمت/ي بمشاركة المنتدى \"%1$s\" مع %2$s.</string>
|
||||
<string name="forum_invitations_title">دعوات المنتدى</string>
|
||||
<string name="forum_invitation_exists">لقد قمت بالفعل بقبول دعوة لهذا المنتدى.\n\nقبول دعوات إضافية سيجعل اتصالك مع المنتدى أسرع وأكثر اعتمادية.</string>
|
||||
<string name="forum_invitation_exists">لقد قمت/ي بالفعل بقبول دعوة لهذا المنتدى.\n\nقبول دعوات إضافية سيجعل اتصالك مع المنتدى أسرع وأكثر اعتمادية.</string>
|
||||
<string name="forum_joined_toast">تم الإنضمام للمنتدى</string>
|
||||
<string name="forum_declined_toast">تم رفض الدعوة</string>
|
||||
<string name="shared_by_format">شورٍك بواسطة %s</string>
|
||||
<string name="forum_invitation_already_sharing">مُشارك بالفعل</string>
|
||||
<string name="shared_by_format">تمت المشاركة بواسطة %s</string>
|
||||
<string name="forum_invitation_already_sharing">في طور المشاركة</string>
|
||||
<string name="forum_invitation_response_accepted_sent">لقد قبلت دعوة المنتدى من %s.</string>
|
||||
<string name="forum_invitation_response_declined_sent">لقد رفضت دعوة المنتدى من %s.</string>
|
||||
<string name="forum_invitation_response_accepted_received">قبل %s دعوة المنتدى.</string>
|
||||
<string name="forum_invitation_response_declined_received">رفض %s دعوة المنتدى.</string>
|
||||
<string name="forum_invitation_response_accepted_received">قبل/ت %s دعوة المنتدى.</string>
|
||||
<string name="forum_invitation_response_declined_received">رفض/ت %s دعوة المنتدى.</string>
|
||||
<string name="sharing_status">حالة المشاركة</string>
|
||||
<string name="sharing_status_forum">أي عضو بالمنتدى يمكنه مشاركته مع جهات إتصاله. أنت تشارك هذا المنتدى مع جهات الإتصال هذه. قد يكون هناك أعضاء آخرين لا يمكنك رؤيتهم.</string>
|
||||
<string name="shared_with">مشارك مع %1$d (%2$d متصل)</string>
|
||||
<string name="sharing_status_forum">أي عضو بالمنتدى يمكنه مشاركته مع جهات إتصاله. يمكنك مشاركة هذا المنتدى مع جهات الإتصال هذه. قد يكون هناك أعضاء آخرين لا يمكنك/ي رؤيتهم.</string>
|
||||
<string name="shared_with">تمت المشاركة مع %1$d (%2$d متصل)</string>
|
||||
<plurals name="forums_shared">
|
||||
<item quantity="zero">لا منتدى مُشارَك من جهات الاتصال</item>
|
||||
<item quantity="one">منتدى مُشارَك من جهات الاتصال</item>
|
||||
@@ -308,54 +309,54 @@
|
||||
<string name="nobody">لا أحد</string>
|
||||
<!--Blogs-->
|
||||
<string name="blogs_other_blog_empty_state">لا منشورات للعرض</string>
|
||||
<string name="read_more">اقرأ المزيد</string>
|
||||
<string name="blogs_write_blog_post">أكتب تدوينة</string>
|
||||
<string name="blogs_write_blog_post_body_hint">أكتب تدوينتك</string>
|
||||
<string name="read_more">قراءة المزيد</string>
|
||||
<string name="blogs_write_blog_post">كتابة تدوينة</string>
|
||||
<string name="blogs_write_blog_post_body_hint">كتابة تدوينتك</string>
|
||||
<string name="blogs_publish_blog_post">نشر</string>
|
||||
<string name="blogs_blog_post_created">تم إنشاء تدوينة</string>
|
||||
<string name="blogs_blog_post_received">تم إستلام تدوينة جديدة</string>
|
||||
<string name="blogs_blog_post_scroll_to">إنزل إلى</string>
|
||||
<string name="blogs_blog_post_scroll_to">النزول إلى</string>
|
||||
<string name="blogs_feed_empty_state">لا منشورات للعرض</string>
|
||||
<string name="blogs_feed_empty_state_action">التدوينات من جهات اتصالك والمدونات التي تشترك بها ستظهر هنا\n\nالمس أيقونة القلم لتكتب تدوينة</string>
|
||||
<string name="blogs_remove_blog">احذف المدونة</string>
|
||||
<string name="blogs_remove_blog_dialog_message">هل متأكد أنك تريد حذف هذه المدونة؟\n\nالتدوينات سيتم حذفها من جهازك فقط وليس من أجهزة الآخرين.\n\nأي جهة اتصال قمت بمشاركة المدونة معه قد لا يتمكن من استلام التحديثات.</string>
|
||||
<string name="blogs_remove_blog_ok">أزِل</string>
|
||||
<string name="blogs_feed_empty_state_action">التدوينات من جهات اتصالك والمدونات التي تشترك/ين بها ستظهر هنا\n\nالرجاء لمس أيقونة القلم لكتابة تدوينة</string>
|
||||
<string name="blogs_remove_blog">حذف المدونة</string>
|
||||
<string name="blogs_remove_blog_dialog_message">هل أنت متأكد/ة من أنك تريد/ين حذف هذه المدونة؟\n\nالتدوينات سيتم حذفها من جهازك فقط وليس من أجهزة الآخرين.\n\nأي جهة اتصال قمت/ي بمشاركة المدونة معها قد لا تتمكن من استلام التحديثات.</string>
|
||||
<string name="blogs_remove_blog_ok">حذف</string>
|
||||
<string name="blogs_blog_removed">تم حذف المدونة</string>
|
||||
<string name="blogs_reblog_comment_hint">أضف تعليق (إختياري)</string>
|
||||
<string name="blogs_reblog_button">أعد التدوين</string>
|
||||
<string name="blogs_reblog_comment_hint">اضافة تعليق (إختياري)</string>
|
||||
<string name="blogs_reblog_button">اعادة التدوين</string>
|
||||
<!--Blog Sharing-->
|
||||
<string name="blogs_sharing_share">شارك المدونة</string>
|
||||
<string name="blogs_sharing_share">مشاركة المدونة</string>
|
||||
<string name="blogs_sharing_error">حدث خطأ في مشاركة هذه المدونة.</string>
|
||||
<string name="blogs_sharing_button">شارك المدونة</string>
|
||||
<string name="blogs_sharing_button">مشاركة المدونة</string>
|
||||
<string name="blogs_sharing_snackbar">تم مشاركة المدونة مع جهات الاتصال المختارة</string>
|
||||
<string name="blogs_sharing_response_accepted_sent">لقد قبلت دعوة المدونة من %s.</string>
|
||||
<string name="blogs_sharing_response_declined_sent">لقد رفضت دعوة المدونة من %s.</string>
|
||||
<string name="blogs_sharing_response_accepted_received">قبل %s دعوة المدونة.</string>
|
||||
<string name="blogs_sharing_response_declined_received">رفض %s دعوة المدونة.</string>
|
||||
<string name="blogs_sharing_invitation_received">قام %1$s بمشاركة المدونة \"%2$s\" معك.</string>
|
||||
<string name="blogs_sharing_invitation_sent">قمت بمشاركة المدونة \"%1$s\" مع %2$s.</string>
|
||||
<string name="blogs_sharing_response_accepted_received">قبل/ت %s دعوة المدونة.</string>
|
||||
<string name="blogs_sharing_response_declined_received">رفض/ت %s دعوة المدونة.</string>
|
||||
<string name="blogs_sharing_invitation_received">قام/ت %1$s بمشاركة المدونة \"%2$s\" معك.</string>
|
||||
<string name="blogs_sharing_invitation_sent">تمت مشاركة المدونة \"%1$s\" مع %2$s.</string>
|
||||
<string name="blogs_sharing_invitations_title">دعوات المدونة</string>
|
||||
<string name="blogs_sharing_joined_toast">أشترك في المدونة</string>
|
||||
<string name="blogs_sharing_joined_toast">مشترك/ة في المدونة</string>
|
||||
<string name="blogs_sharing_declined_toast">تم رفض الدعوة</string>
|
||||
<string name="sharing_status_blog">أي شخص أشترك في مدونة يمكنه مشاركتها مع جهات اتصاله. أنت تشارك هذه المدونة مع جهات الإتصال التالية. قد يكون هناك مشتركون آخرون لا يمكنك رؤيتهم.</string>
|
||||
<string name="sharing_status_blog">أي شخص أشترك في مدونة يمكنه مشاركتها مع جهات اتصاله. أنت تشارك/ين هذه المدونة مع جهات الإتصال التالية. قد يكون هناك مشتركون آخرون لا يمكن رؤيتهم.</string>
|
||||
<!--RSS Feeds-->
|
||||
<string name="blogs_rss_feeds_import">إستيراد تحديثات RSS</string>
|
||||
<string name="blogs_rss_feeds_import_button">إستيراد</string>
|
||||
<string name="blogs_rss_feeds_import_hint">أدخل رابط تحديثات RSS</string>
|
||||
<string name="blogs_rss_feeds_import_hint">ادخال رابط تحديثات RSS</string>
|
||||
<string name="blogs_rss_feeds_import_error">معذرة! حدث خطأ في استيراد التحديثات.</string>
|
||||
<string name="blogs_rss_feeds_manage">إدارة تحديثات RSS</string>
|
||||
<string name="blogs_rss_feeds_manage_imported">مستوردة:</string>
|
||||
<string name="blogs_rss_feeds_manage_author">المؤلف:</string>
|
||||
<string name="blogs_rss_feeds_manage_imported">تم استيراد:</string>
|
||||
<string name="blogs_rss_feeds_manage_author">المؤلف/ة:</string>
|
||||
<string name="blogs_rss_feeds_manage_updated">آخر تحديث:</string>
|
||||
<string name="blogs_rss_remove_feed">أزل الخلاصة</string>
|
||||
<string name="blogs_rss_remove_feed_dialog_message">هل متأكد من رغبتك في حذف هذه الخلاصة؟\n\nالمنشورات ستحذف من جهازك وليس من أجهزة الآخرين.\n\nأي جهة اتصال قمت بمشاركة هذه الخلاصة معهم قد لا يتمكنون من استلام التحديثات.</string>
|
||||
<string name="blogs_rss_remove_feed_ok">أزِل</string>
|
||||
<string name="blogs_rss_remove_feed">ازالة الخلاصة</string>
|
||||
<string name="blogs_rss_remove_feed_dialog_message">هل أنت متأكد/ة من رغبتك في حذف هذه الخلاصة؟\n\nالمنشورات ستحذف من جهازك وليس من أجهزة الآخرين.\n\nأي جهة اتصال قمت/ي بمشاركة هذه الخلاصة معها قد لا تتمكن من استلام التحديثات.</string>
|
||||
<string name="blogs_rss_remove_feed_ok">حذف</string>
|
||||
<string name="blogs_rss_feeds_manage_delete_error">لا يمكن حذف الخلاصة!</string>
|
||||
<string name="blogs_rss_feeds_manage_empty_state">لا خلاصات RSS للعرض\n\nالمس علامة + لإستيراد خلاصة.</string>
|
||||
<string name="blogs_rss_feeds_manage_error">حدث خطأ في جلب خلاصاتك. من فضلك حاول لاحقًا.</string>
|
||||
<string name="blogs_rss_feeds_manage_empty_state">لا خلاصات RSS للعرض\n\nالرجاء لمس علامة + لإستيراد خلاصة.</string>
|
||||
<string name="blogs_rss_feeds_manage_error">حدث خطأ في جلب خلاصاتك. الرجاء المحاولة لاحقًا.</string>
|
||||
<!--Settings Display-->
|
||||
<string name="pref_language_title">اللغة & المنطقة</string>
|
||||
<string name="pref_language_changed">هذا الإعداد سيتفعل بعد إعادة تشغيل Briar (براير). من فضلك سجل الخروج ثم أعد تشغيل Briar (براير).</string>
|
||||
<string name="pref_language_changed">هذا الإعداد سيتفعل بعد إعادة تشغيل Briar (براير). الرجاء تسجيل الخروج ثم اعادة تشغيل Briar (براير).</string>
|
||||
<string name="pref_language_default">النظام الإفتراضي</string>
|
||||
<string name="display_settings_title">العرض</string>
|
||||
<string name="pref_theme_title">السمة</string>
|
||||
@@ -368,20 +369,20 @@
|
||||
<string name="bluetooth_setting">الإتصال عبر بلوتوث</string>
|
||||
<string name="bluetooth_setting_enabled">متى ما كان أحد جهات الاتصال قريبًا</string>
|
||||
<string name="bluetooth_setting_disabled">فقط عند إضافة جهة إتصال</string>
|
||||
<string name="tor_network_setting">اتصل عبر الانترنت (تور)</string>
|
||||
<string name="tor_network_setting">اتصال عبر الانترنت (تور)</string>
|
||||
<string name="tor_network_setting_automatic">تلقائيًا حسب الموقع</string>
|
||||
<string name="tor_network_setting_without_bridges">استخدم تور بلا جسور</string>
|
||||
<string name="tor_network_setting_with_bridges">استخدم تور مع الجسور</string>
|
||||
<string name="tor_network_setting_never">لا تتصل</string>
|
||||
<string name="tor_network_setting_without_bridges">استخدام تور بلا جسور</string>
|
||||
<string name="tor_network_setting_with_bridges">استخدام تور مع الجسور</string>
|
||||
<string name="tor_network_setting_never">عدم الاتصال</string>
|
||||
<!--How and when Tor will connect after Automatic: E.g. Don't connect (in China) or Use Tor with bridges (in Belarus)-->
|
||||
<string name="tor_network_setting_summary">تلقائيا: %1$s (في %2$s)</string>
|
||||
<string name="tor_mobile_data_title">إستخدم بيانات الجوال</string>
|
||||
<string name="tor_mobile_data_title">إستخدام بيانات الجوال</string>
|
||||
<!--Settings Security and Panic-->
|
||||
<string name="security_settings_title">الأمان</string>
|
||||
<string name="pref_lock_summary">استخدم قفل شاشة الجهاز لحماية Briar (براير) في حال تسجيل الدخول</string>
|
||||
<string name="pref_lock_disabled_summary">لاستخدام هذه الميزة، قم بإعداد قفل شاشة لجهازك</string>
|
||||
<string name="pref_lock_summary">الرجاء استخدام قفل شاشة الجهاز لحماية Briar (براير) في حال تسجيل الدخول</string>
|
||||
<string name="pref_lock_disabled_summary">لاستخدام هذه الميزة، الرجاء إعداد قفل شاشة لجهازك</string>
|
||||
<!--The %s placeholder is replaced with the following time spans, e.g. 5 Minutes, 1 Hour-->
|
||||
<string name="pref_lock_timeout_summary">حينما لا تستخدم Briar (براير)، أقفله تلقائيا بعد %s</string>
|
||||
<string name="pref_lock_timeout_summary">في حالة عدم استعمال Briar (براير)، يقفل تلقائيا بعد %s</string>
|
||||
<!--Will be shown in a list of lock times. Should fit into the %s of "automatically lock it after %s"-->
|
||||
<string name="pref_lock_timeout_1">1 دقيقة</string>
|
||||
<!--Will be shown in a list of lock times. Should fit into the %s of "automatically lock it after %s"-->
|
||||
@@ -409,7 +410,7 @@
|
||||
<string name="dialog_title_connect_panic_app">تأكيد تطبيق الذعر</string>
|
||||
<string name="dialog_message_connect_panic_app">هل متأكد أنك تريد السماح ل %1$s أن يقوم بتفعيل إجراءات زر الذعر؟</string>
|
||||
<string name="panic_setting_signout_title">تسجيل الخروج</string>
|
||||
<string name="panic_setting_signout_summary">سجل الخروج من Briar (براير) إذا تم ضغط زر الذعر</string>
|
||||
<string name="panic_setting_signout_summary">تسجيل الخروج من Briar (براير) عند ضغط زر الذعر</string>
|
||||
<string name="purge_setting_title">حذف الحساب</string>
|
||||
<string name="purge_setting_summary">احذف حساب Briar (براير) إذا تم ضغط زر الذعر. انتبه: هذا سيسبب حذف دائم لكل معرفات وحساباتك ورسائلك.</string>
|
||||
<string name="uninstall_setting_title">ألغ تثبيت Briar (براير)</string>
|
||||
@@ -440,44 +441,51 @@
|
||||
<string name="feedback_settings_title">الإفادة حول التطبيق</string>
|
||||
<string name="send_feedback">أرسل ملاحظاتك</string>
|
||||
<!--Link Warning-->
|
||||
<string name="link_warning_title">تنبيه الرابط</string>
|
||||
<string name="link_warning_title">تنبيه بوجود رابط</string>
|
||||
<string name="link_warning_intro">أنت بصدد فتح هذا الرابط في تطبيق خارجي</string>
|
||||
<string name="link_warning_text">يمكن أن يستعمل هذا في تحديد هويتك. تأمل في مدى ثقتك بالشخص الذي أرسل لك هذا الرابط ويفضل بالنظر بفتحه بإستعمال أورفوكس.</string>
|
||||
<string name="link_warning_text">يمكن أن يستعمل هذا في تحديد هويتك. يرجى التفكير في مدى ثقتك بالشخص الذي أرسل لك هذا الرابط و من الأفضل فتحه بإستعمال أورفوكس.</string>
|
||||
<string name="link_warning_open_link">افتح الرابط</string>
|
||||
<!--Crash Reporter-->
|
||||
<string name="crash_report_title">تقرير إنهيار Briar (براير)</string>
|
||||
<string name="briar_crashed">معذرة، لقد انهار Briar (براير).</string>
|
||||
<string name="not_your_fault">هذا ليس خطؤك.</string>
|
||||
<string name="please_send_report">من فضلك ساعدنا في تحسين Briar (براير) عبر إرسال تقرير الإنهيار.</string>
|
||||
<string name="please_send_report">الرجاء مساعدتنا في تحسين Briar (براير) عبر إرسال تقرير الإنهيار.</string>
|
||||
<string name="report_is_encrypted">نعدك بأن التقرير سيرسل مشفرًا وبشكل آمن.</string>
|
||||
<string name="feedback_title">الإفادة حول التطبيق</string>
|
||||
<string name="describe_crash">صف ما حدث (إختياري)</string>
|
||||
<string name="enter_feedback">أدخل ملاحظاتك</string>
|
||||
<string name="describe_crash">وصف ما حدث (إختياري)</string>
|
||||
<string name="enter_feedback">ادخال ملاحظاتك</string>
|
||||
<string name="optional_contact_email">بريدك الالكتروني (إختياري)</string>
|
||||
<string name="include_debug_report_crash">ضمن بيانات مجهولة عن الإنهيار</string>
|
||||
<string name="include_debug_report_feedback">ضمن بيانات مجهولة عن هذا الجهاز</string>
|
||||
<string name="include_debug_report_crash">تضمين بيانات مجهولة عن الإنهيار</string>
|
||||
<string name="include_debug_report_feedback">تضمين بيانات مجهولة عن هذا الجهاز</string>
|
||||
<string name="could_not_load_report_data">لم يمكن تحميل بيانات التقرير.</string>
|
||||
<string name="send_report">أرسل التقرير</string>
|
||||
<string name="send_report">ارسال التقرير</string>
|
||||
<string name="close">إغلاق</string>
|
||||
<string name="dev_report_saved">تم حفظ التقرير. سيتم إرساله عند تسجيل الدخول إلى Briar (براير) في المرة القادمة.</string>
|
||||
<!--Sign Out-->
|
||||
<string name="progress_title_logout">تسجيل الخروج من Briar (براير)...</string>
|
||||
<!--Screen Filters & Tapjacking-->
|
||||
<string name="screen_filter_title">تم إكتشاف غلاف شاشة</string>
|
||||
<string name="screen_filter_body">تطبيق آخر يعمل كغلاف فوق Briar (براير). لحماية أمنك فإن Briar (براير) لن يستجيب للمس طالما كان تطبيق آخر فوقه.\n\nيحتمل أن يكون أحد هذه التطبيقات الذي يعمل بالفوق:\n\n %1$s</string>
|
||||
<string name="screen_filter_allow">إسمح لهذه التطبيقات بالعمل فوق غيرها</string>
|
||||
<string name="screen_filter_body">تطبيق آخر يعمل كغلاف فوق Briar (براير). لحماية أمنك فإن Briar (براير) لن يستجيب للمس طالما كان تطبيق آخر فوقه.\n\nيحتمل أن تكون أحد هذه التطبيقات التي تعمل بالفوق:\n\n %1$s</string>
|
||||
<string name="screen_filter_allow">السماح لهذه التطبيقات بالعمل فوق غيرها</string>
|
||||
<!--Permission Requests-->
|
||||
<string name="permission_camera_title">إذن الكاميرا</string>
|
||||
<string name="permission_camera_request_body">لتقم بمسح رمز QR، يحتاج Briar (براير) إلى إستعمال الكاميرا.</string>
|
||||
<string name="permission_camera_denied_body">قد رفضت إعطاء إذن الكاميرا، لكن إضافة جهات إتصال يتطلب إستعمال الكاميرا.\n\nمن فضلك إمنحنا الإذن.</string>
|
||||
<string name="permission_camera_request_body">للتمكن من مسح رمز QR، يحتاج Briar (براير) إلى إستعمال الكاميرا.</string>
|
||||
<string name="permission_camera_denied_body">قد رفضت إعطاء إذن الكاميرا، لكن إضافة جهات إتصال يتطلب إستعمال الكاميرا.\n\nالرجاء منح الإذن.</string>
|
||||
<string name="permission_camera_denied_toast">لم يتم منح الإذن بإستعمال الكاميرا</string>
|
||||
<string name="qr_code">رمز QR</string>
|
||||
<string name="show_qr_code_fullscreen">أظهر رمز QR بوضع ملء الشاشة</string>
|
||||
<string name="show_qr_code_fullscreen">اظهار رمز QR بوضع ملء الشاشة</string>
|
||||
<!--App Locking-->
|
||||
<string name="lock_unlock">فك قفل Briar (براير)</string>
|
||||
<string name="lock_unlock_verbose">أدخل رمز، نمط أو كلمة سر جهازك لإلغاء قفل Briar (براير)</string>
|
||||
<string name="lock_unlock_fingerprint_description">المس حساس البصمة ببصمتك المسجلة للإستمرار</string>
|
||||
<string name="lock_unlock_password">استخدم كلمة السر</string>
|
||||
<string name="lock_unlock_verbose">ادخال رمز، نمط أو كلمة سر جهازك لإلغاء قفل Briar (براير)</string>
|
||||
<string name="lock_unlock_fingerprint_description">لمس حساس البصمة ببصمتك المسجلة للإستمرار</string>
|
||||
<string name="lock_unlock_password">استخدام كلمة السر</string>
|
||||
<string name="lock_is_locked">Briar (براير) مقفل</string>
|
||||
<string name="lock_tap_to_unlock">إلمس لفك القفل</string>
|
||||
<string name="lock_tap_to_unlock">الرجاء اللمس لفك القفل</string>
|
||||
<!--Screenshots-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Bob!-->
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Alice!-->
|
||||
<!--This is a message to be used in screenshots.-->
|
||||
</resources>
|
||||
|
||||
@@ -440,4 +440,11 @@
|
||||
<string name="lock_unlock_password">Usa la contrasenya</string>
|
||||
<string name="lock_is_locked">Briar està bloquejat</string>
|
||||
<string name="lock_tap_to_unlock">Toqueu per desbloquejar-lo</string>
|
||||
<!--Screenshots-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Bob!-->
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Alice!-->
|
||||
<!--This is a message to be used in screenshots.-->
|
||||
</resources>
|
||||
|
||||
@@ -407,4 +407,11 @@
|
||||
<string name="qr_code">QR kód</string>
|
||||
<string name="show_qr_code_fullscreen">Zobrazit QR kód na celou obrazovku</string>
|
||||
<!--App Locking-->
|
||||
<!--Screenshots-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Bob!-->
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Alice!-->
|
||||
<!--This is a message to be used in screenshots.-->
|
||||
</resources>
|
||||
|
||||
@@ -440,4 +440,11 @@
|
||||
<string name="lock_unlock_password">Benutze Passwort</string>
|
||||
<string name="lock_is_locked">Briar ist gesperrt</string>
|
||||
<string name="lock_tap_to_unlock">Zum Entsperren antippen</string>
|
||||
<!--Screenshots-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Bob!-->
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Alice!-->
|
||||
<!--This is a message to be used in screenshots.-->
|
||||
</resources>
|
||||
|
||||
@@ -445,4 +445,17 @@
|
||||
<string name="lock_unlock_password">Usa la contraseña</string>
|
||||
<string name="lock_is_locked">Briar está bloqueado</string>
|
||||
<string name="lock_tap_to_unlock">Golpear para desbloquear</string>
|
||||
<!--Screenshots-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_alice">Alicia</string>
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_bob">Roberto</string>
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_carol">Carolina</string>
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Bob!-->
|
||||
<string name="screenshot_message_1">¡Hola Roberto!</string>
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Alice!-->
|
||||
<string name="screenshot_message_2">¡Hola Alicia! ¡Gracias por contarme acerca de Briar!</string>
|
||||
<!--This is a message to be used in screenshots.-->
|
||||
<string name="screenshot_message_3">Sin problema, espero que te guste 😀</string>
|
||||
</resources>
|
||||
|
||||
@@ -129,6 +129,8 @@
|
||||
<string name="contact_added_toast">Kontaktua gehitu da: %s</string>
|
||||
<string name="contact_already_exists">%s kontaktua badago aurretik ere</string>
|
||||
<string name="qr_code_invalid">QR kodea baliogabea da</string>
|
||||
<string name="qr_code_too_old">Eskaneatu duzun QR kodea %s aplikazioaren bertsio zaharrago batetik dator.\n\nEskatu mesedez zure kontaktuari azken bertsiora eguneratu dezala eta saiatu berriro.</string>
|
||||
<string name="qr_code_too_new">Eskaneatu duzun QR kodea %s aplikazioaren bertsio berriago batetik dator.\n\nEguneratu azken bertsiora eta saiatu berriro.</string>
|
||||
<string name="camera_error">Kameraren errorea</string>
|
||||
<string name="connecting_to_device">Gailura konektatzen\u2026</string>
|
||||
<string name="authenticating_with_device">Gailuarekin autentifikatzen\u2026</string>
|
||||
@@ -338,8 +340,10 @@
|
||||
<string name="tor_mobile_data_title">Erabili datu mugikorrak</string>
|
||||
<!--Settings Security and Panic-->
|
||||
<string name="security_settings_title">Segurtasuna</string>
|
||||
<string name="pref_lock_title">Aplikazioaren blokeoa</string>
|
||||
<string name="pref_lock_summary">Erabili gailuaren pantaila-blokeoa Briar babesteko saioa irekita dagoen bitartean</string>
|
||||
<string name="pref_lock_disabled_summary">Eginbide hau erabiltzeko, ezarri zure gailuarentzako pantaila-blokeo bat</string>
|
||||
<string name="pref_lock_timeout_title">Aplikazioa blokeatzeko jarduera ezaren denbora-muga</string>
|
||||
<!--The %s placeholder is replaced with the following time spans, e.g. 5 Minutes, 1 Hour-->
|
||||
<string name="pref_lock_timeout_summary">Briar ez erabiltzean, hura automatikoki blokeatu %s eta gero</string>
|
||||
<!--Will be shown in a list of lock times. Should fit into the %s of "automatically lock it after %s"-->
|
||||
@@ -368,6 +372,7 @@
|
||||
<string name="panic_app_setting_none">Bat ere ez</string>
|
||||
<string name="dialog_title_connect_panic_app">Baieztatu larrialdi-aplikazioa</string>
|
||||
<string name="dialog_message_connect_panic_app">Ziur zaude %1$s larrialdi-botoiaren ekintza suntsitzaileak eragitera baimendu nahi duzula?</string>
|
||||
<string name="panic_setting_destructive_action">Ekintza suntsigarriak</string>
|
||||
<string name="panic_setting_signout_title">Amaitu saioa</string>
|
||||
<string name="panic_setting_signout_summary">Amaitu Briar saioa larrialdi-botoia zapaltzen bada</string>
|
||||
<string name="purge_setting_title">Ezabatu kontua</string>
|
||||
@@ -440,4 +445,17 @@
|
||||
<string name="lock_unlock_password">Erabili pasahitza</string>
|
||||
<string name="lock_is_locked">Briar blokeatuta dago</string>
|
||||
<string name="lock_tap_to_unlock">Sakatu desblokeatzeko</string>
|
||||
<!--Screenshots-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_alice">Arantza</string>
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_bob">Jokin</string>
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_carol">Maitane</string>
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Bob!-->
|
||||
<string name="screenshot_message_1">Kaixo Jokin!</string>
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Alice!-->
|
||||
<string name="screenshot_message_2">Kaixo Arantza, eskerrik asko Briar ezagutzera emateagatik!</string>
|
||||
<!--This is a message to be used in screenshots.-->
|
||||
<string name="screenshot_message_3">Ez horregatik, ea gustuko duzun 😀</string>
|
||||
</resources>
|
||||
|
||||
@@ -136,6 +136,12 @@
|
||||
<string name="contact_added_toast">مخاطب اضافه شد: %s</string>
|
||||
<string name="contact_already_exists">مخاطب %s از قبل وجود دارد</string>
|
||||
<string name="qr_code_invalid">کد QR نامعتبر می باشد</string>
|
||||
<string name="qr_code_too_old">کد QR که شما اسکن کرده اید متعلق به یک نسخه قدیمی از %s می باشد.
|
||||
|
||||
لطفا از مخاطب خود بخواهید تا به آخرین نسخه آپگرید کرده و دوباره تلاش کنید.</string>
|
||||
<string name="qr_code_too_new">کد QR که شما اسکن کرده اید متعلق به یک نسخه جدید از %s می باشد.
|
||||
|
||||
لطفا به آخرین نسخه آپگرید کرده و دوباره تلاش کنید.</string>
|
||||
<string name="camera_error">خطای دوربین</string>
|
||||
<string name="connecting_to_device">اتصال به دستگاهu2026\</string>
|
||||
<string name="authenticating_with_device">تصدیق سازی با دستگاه u2026\</string>
|
||||
@@ -367,8 +373,10 @@
|
||||
<string name="tor_mobile_data_title">استفاده از داده موبایل</string>
|
||||
<!--Settings Security and Panic-->
|
||||
<string name="security_settings_title">امنیت</string>
|
||||
<string name="pref_lock_title">قفل برنامه</string>
|
||||
<string name="pref_lock_summary">استفاده از قفل صفحه دستگاه به منظور محافظت از Briar (برایر) در هنگام استفاده از آن</string>
|
||||
<string name="pref_lock_disabled_summary">برای استفاده از این ویژگی، قفل صفحه را برای دستگاه خود برپا کنید</string>
|
||||
<string name="pref_lock_timeout_title">اتمام وقت عدم فعالیت قفل صفحه</string>
|
||||
<!--The %s placeholder is replaced with the following time spans, e.g. 5 Minutes, 1 Hour-->
|
||||
<string name="pref_lock_timeout_summary">زمانی که از Briar (برایر) استفاده نمی شود، به صورت خودکار بعد از %s آن را قفل کن</string>
|
||||
<!--Will be shown in a list of lock times. Should fit into the %s of "automatically lock it after %s"-->
|
||||
@@ -397,6 +405,7 @@
|
||||
<string name="panic_app_setting_none">هیچکدام</string>
|
||||
<string name="dialog_title_connect_panic_app">تایید برنامه هراس</string>
|
||||
<string name="dialog_message_connect_panic_app">آیا مطمئن هستید که میخواهید به %1$s اجازه دهید تا باعث عملیات مخرب دکمه هراس بشود؟</string>
|
||||
<string name="panic_setting_destructive_action">فعالیت های مخرب</string>
|
||||
<string name="panic_setting_signout_title">خروج</string>
|
||||
<string name="panic_setting_signout_summary">در صورت کلیک بر روی کلید هراس از Briar (برایر) خارج شو</string>
|
||||
<string name="purge_setting_title">حذف حساب کاربری</string>
|
||||
@@ -475,4 +484,17 @@
|
||||
<string name="lock_unlock_password">از رمز عبور استفاده کنید</string>
|
||||
<string name="lock_is_locked">Briar (برایر) قفل می باشد</string>
|
||||
<string name="lock_tap_to_unlock">برای آنلاک کردن کلیک کنید</string>
|
||||
<!--Screenshots-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_alice">آلیس</string>
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_bob">باب</string>
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_carol">کارول</string>
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Bob!-->
|
||||
<string name="screenshot_message_1">هی باب!</string>
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Alice!-->
|
||||
<string name="screenshot_message_2">هی آلیس! ممنون از اینکه درباره Briar به من گفتی!</string>
|
||||
<!--This is a message to be used in screenshots.-->
|
||||
<string name="screenshot_message_3">خواهش میکنم، امیدوارم ازش خوشت بیاد 😀</string>
|
||||
</resources>
|
||||
|
||||
@@ -440,4 +440,11 @@
|
||||
<string name="lock_unlock_password">Käytä salasanaa</string>
|
||||
<string name="lock_is_locked">Briar on lukittu</string>
|
||||
<string name="lock_tap_to_unlock">Napauta avataksesi lukitus</string>
|
||||
<!--Screenshots-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Bob!-->
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Alice!-->
|
||||
<!--This is a message to be used in screenshots.-->
|
||||
</resources>
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
<string name="download_briar_button">Télécharger Briar 1.0</string>
|
||||
<string name="startup_open_database">Déchiffrement de la base de données…</string>
|
||||
<string name="startup_migrate_database">Mise à niveau de la base de données…</string>
|
||||
<string name="startup_compact_database">Compactage de la base de données…</string>
|
||||
<!--Navigation Drawer-->
|
||||
<string name="nav_drawer_open_description">Ouvrir le tiroir de navigation</string>
|
||||
<string name="nav_drawer_close_description">Fermer le tiroir de navigation</string>
|
||||
@@ -129,6 +130,8 @@
|
||||
<string name="contact_added_toast">Contact ajouté : %s</string>
|
||||
<string name="contact_already_exists">Le contact %s existe déjà</string>
|
||||
<string name="qr_code_invalid">Le code QR est invalide</string>
|
||||
<string name="qr_code_too_old">Le code QR que vous avez lu provient d’une version plus ancienne de %s.\n\nVeuillez demander à votre contact de passer à la version la plus récente, puis ressayez.</string>
|
||||
<string name="qr_code_too_new">Le code QR que vous avez lu provient d’une version plus récente de %s.\n\nVeuillez passer à la version la plus récente, puis ressayez.</string>
|
||||
<string name="camera_error">Erreur de la caméra</string>
|
||||
<string name="connecting_to_device">Connexion à l’appareil\u2026</string>
|
||||
<string name="authenticating_with_device">Autentification avec l’appareil\u2026</string>
|
||||
@@ -370,6 +373,7 @@
|
||||
<string name="panic_app_setting_none">Aucune</string>
|
||||
<string name="dialog_title_connect_panic_app">Confirmer l’application d’urgence</string>
|
||||
<string name="dialog_message_connect_panic_app">Voulez-vous vraiment autoriser %1$s à déclencher les actions destructrices du bouton d’urgence ?</string>
|
||||
<string name="panic_setting_destructive_action">Actions destructrices</string>
|
||||
<string name="panic_setting_signout_title">Déconnexion</string>
|
||||
<string name="panic_setting_signout_summary">Se déconnecter de Briar si l’on appuie sur un bouton d’urgence</string>
|
||||
<string name="purge_setting_title">Supprimer le compte</string>
|
||||
@@ -442,4 +446,17 @@
|
||||
<string name="lock_unlock_password">Utiliser un mot de passe</string>
|
||||
<string name="lock_is_locked">Briar est verrouillée</string>
|
||||
<string name="lock_tap_to_unlock">Toucher pour déverrouiller</string>
|
||||
<!--Screenshots-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_alice">Laurence</string>
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_bob">Thomas</string>
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_carol">Léa</string>
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Bob!-->
|
||||
<string name="screenshot_message_1">Bonjour, Thomas !</string>
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Alice!-->
|
||||
<string name="screenshot_message_2">Bonjour, Laurence ! Merci de m’avoir parlé de Briar !</string>
|
||||
<!--This is a message to be used in screenshots.-->
|
||||
<string name="screenshot_message_3">Pas de problème, j’espère que tu aimeras l’appli 😀</string>
|
||||
</resources>
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
<string name="download_briar_button">Descargar Briar 1.0</string>
|
||||
<string name="startup_open_database">Descifrando a Base de datos...</string>
|
||||
<string name="startup_migrate_database">Actualizando a Base de datos...</string>
|
||||
<string name="startup_compact_database">Compactando a base de datos...</string>
|
||||
<!--Navigation Drawer-->
|
||||
<string name="nav_drawer_open_description">Abra a gaveta de navegación</string>
|
||||
<string name="nav_drawer_close_description">Peche a gaveta de navegación</string>
|
||||
@@ -129,6 +130,8 @@
|
||||
<string name="contact_added_toast">Contacto engadido: %s</string>
|
||||
<string name="contact_already_exists">O contacto %s xa existe</string>
|
||||
<string name="qr_code_invalid">O código QR non é válido</string>
|
||||
<string name="qr_code_too_old">O código QR que escaneou procede de unha versión anterior de %s.\n\nPor favor, solicite ao seu contacto actualizar a nova versión e inténteo de novo.</string>
|
||||
<string name="qr_code_too_new">O código QR que escaneou procede de unha nova versión de %s.\n\nPor favor, actualice a última versión e inténteo de novo.</string>
|
||||
<string name="camera_error">Fallo na cámara</string>
|
||||
<string name="connecting_to_device">Conectando co dispositivo\u2026</string>
|
||||
<string name="authenticating_with_device">Autenticándose co dispositivo\u2026</string>
|
||||
@@ -370,6 +373,7 @@
|
||||
<string name="panic_app_setting_none">Ningún</string>
|
||||
<string name="dialog_title_connect_panic_app">Confirme a App do pánico</string>
|
||||
<string name="dialog_message_connect_panic_app">Está segura de querer permitir a %1$s activar accións destrutivas do botón do pánico?</string>
|
||||
<string name="panic_setting_destructive_action">Accións destructivas</string>
|
||||
<string name="panic_setting_signout_title">Finalizar sesión</string>
|
||||
<string name="panic_setting_signout_summary">Desconecte de Briar si o botón do pánico se preme</string>
|
||||
<string name="purge_setting_title">Eliminar conta</string>
|
||||
@@ -442,4 +446,17 @@
|
||||
<string name="lock_unlock_password">Utilizar contrasinal</string>
|
||||
<string name="lock_is_locked">Briar está bloqueada</string>
|
||||
<string name="lock_tap_to_unlock">Toque para desbloquear</string>
|
||||
<!--Screenshots-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_alice">Alice</string>
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_bob">Bob</string>
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_carol">Carol</string>
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Bob!-->
|
||||
<string name="screenshot_message_1">Ola Bob!</string>
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Alice!-->
|
||||
<string name="screenshot_message_2">Ola Alice! Grazas por falarme de Briar!</string>
|
||||
<!--This is a message to be used in screenshots.-->
|
||||
<string name="screenshot_message_3">Sen problema, agardo sexa do teu gusto 😀</string>
|
||||
</resources>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user