Compare commits

..

97 Commits

Author SHA1 Message Date
akwizgran
779d873a70 Bump version numbers for 1.1.3 release. 2018-10-17 12:42:42 +01:00
akwizgran
b296500e7a Update translations. 2018-10-17 12:41:24 +01:00
Torsten Grote
ae16a93522 Merge branch 'compact-at-startup' into 'master'
Compact the database at startup

See merge request briar/briar!955
2018-10-16 16:02:40 +00:00
Torsten Grote
c9a2ff71ae Merge branch 'tor-v3-hidden-service-addresses' into 'master'
Add support for v3 hidden service addresses

See merge request briar/briar!952
2018-10-16 15:55:34 +00:00
akwizgran
16f4c60a56 Add test for compaction time. 2018-10-16 11:39:06 +01:00
akwizgran
76121eb871 Always compact the DB if migrations have been applied. 2018-10-16 11:24:47 +01:00
akwizgran
47c91a96ae Compact the database at startup. 2018-10-16 11:13:37 +01:00
akwizgran
14befb957b Add support for v3 hidden service addresses. 2018-10-16 10:15:18 +01:00
Torsten Grote
b464fe1653 Merge branch 'message-terminology' into 'master'
Use "text" to refer to message text

See merge request briar/briar!948
2018-10-15 13:22:31 +00:00
akwizgran
09c6f09805 Merge branch '1420-tor-status-update' into 'master'
Disable TorPlugin before applying changed settings

Closes #1420

See merge request briar/briar!953
2018-10-15 12:54:41 +00:00
Torsten Grote
a93093182d Disable TorPlugin before applying changed settings
The is necessary for two reasons:

1. Since Tor 0.3.4.8, it doesn't reconnect when changing the value of
   UseBridges via the control connection without also resetting
   DisableNetwork
2. The TorPlugin needs to set itself to a disconnected state for the UI
   to properly reflect this new state

Fixes #1420
2018-10-15 09:43:12 -03:00
Torsten Grote
e776ee02b0 Merge branch 'tor-0.3.4.8' into 'master'
Upgrade Tor to 0.3.4.8

See merge request briar/briar!951
2018-10-11 22:44:45 +00:00
akwizgran
c0553ec11f Upgrade Linux Tor binaries to 0.3.4.8. 2018-10-11 15:05:39 +01:00
akwizgran
75a871a2f8 Upgrade Android Tor binaries to 0.3.4.8. 2018-10-11 14:38:18 +01:00
akwizgran
d6d3d5acef Merge branch '1240-no-empty-messages' into 'master'
Don't allow empty message bodies

See merge request briar/briar!949
2018-10-10 14:52:34 +00:00
Torsten Grote
a361a2613c Merge branch 'test-configurations' into 'master'
Update test configurations for Android Studio 3.2

See merge request briar/briar!950
2018-10-10 12:38:19 +00:00
akwizgran
b68dbd6a75 Merge branch 'upgrade-dependencies' into 'master'
Upgrade some of the things

See merge request briar/briar!935
2018-10-10 11:42:45 +00:00
akwizgran
f1e89a3ff4 Don't allow empty message bodies. 2018-10-10 10:57:41 +01:00
akwizgran
056c23167d Update test configurations for Android Studio 3.2. 2018-10-10 10:43:07 +01:00
akwizgran
79d5612645 Use "text" to refer to message text. 2018-10-10 10:40:30 +01:00
akwizgran
a030f92275 Merge branch 'headless' into 'master'
Add Briar headless client that exposes a REST API

See merge request briar/briar!931
2018-10-09 15:43:31 +00:00
Torsten Grote
b3615b4a77 briar-headless: Last round of review comments 2018-10-09 12:19:21 -03:00
akwizgran
8a15fb242a Merge branch 'briar-integration-test-transaction' into 'master'
Remove custom DB transaction code from BriarIntegrationTest

See merge request briar/briar!946
2018-10-09 14:16:23 +00:00
Torsten Grote
e3686186ee Fix closing server with ^C 2018-10-08 18:40:21 -03:00
akwizgran
18ae388137 Merge branch '1395-low-memory-crash' into 'master'
Don't use non-AppCompat theme for AppCompat activities

Closes #1395

See merge request briar/briar!947
2018-10-08 12:03:20 +00:00
Torsten Grote
775031e893 Don't use non-AppCompat theme for AppCompat activities
Fixes #1395
2018-10-05 16:50:11 -03:00
Torsten Grote
9f91b91a4f Remove custom DB transaction code from BriarIntegrationTest 2018-10-05 15:41:29 -03:00
Torsten Grote
280f3ba1fc briar-headless: POST text as JSON in body instead of form parameter 2018-10-05 15:23:31 -03:00
Torsten Grote
66619fd3a4 briar-headless: Next round of review comments 2018-10-05 15:23:31 -03:00
akwizgran
c7eb0cbb6d Include body of private request if present. 2018-10-05 15:23:31 -03:00
akwizgran
1617a95bb9 Only include "body" for headers that can have bodies. 2018-10-05 15:23:31 -03:00
Torsten Grote
6f54718756 Use short type labels in JSON API instead of long Java-like namespaces 2018-10-05 15:23:31 -03:00
Torsten Grote
ea749f2128 Minor improvements to JsonDict output classes 2018-10-05 15:23:31 -03:00
akwizgran
b4b0d3daa6 Allow null values in JsonDict. 2018-10-05 15:23:31 -03:00
akwizgran
609c90f57e Convert Map#put() to assignment. 2018-10-05 15:23:31 -03:00
akwizgran
5cf68fa134 Use JsonDict for blog post headers. 2018-10-05 15:23:31 -03:00
akwizgran
61c9c6b8eb Add visitor to dispatch output() dynamically. 2018-10-05 15:23:31 -03:00
akwizgran
e97608da40 Add test to show that static dispatch won't work. 2018-10-05 15:23:31 -03:00
akwizgran
0bb80b1a15 Add JsonDict class for JSON output. 2018-10-05 15:23:31 -03:00
akwizgran
bda52ea548 Use maps for JSON output. 2018-10-05 15:23:31 -03:00
Torsten Grote
cf033dc29d briar-headless: Address second round of review comments 2018-10-05 15:23:31 -03:00
Torsten Grote
c12cedc371 briar-headless: Address first round of review comments 2018-10-05 15:23:31 -03:00
Torsten Grote
4b5e9bd64f Ensure the use SecureRandom when creating authentication token 2018-10-05 15:23:31 -03:00
Torsten Grote
8d55911dab Add unit test for WebSocketController
Also move the controller into an event package
2018-10-05 15:23:31 -03:00
Torsten Grote
e381f83512 Last code cleanup before submitting merge request 2018-10-05 15:23:31 -03:00
Torsten Grote
e4c7f13832 Add a README.md with API documentation
Also fix some smaller issues found during writing the documentation
2018-10-05 15:23:31 -03:00
Torsten Grote
b089a204d3 Add support for websocket authentication via basic auth
The token should be used as username and the password left empty
2018-10-05 15:23:31 -03:00
Torsten Grote
85fcb34997 Add briar-headless Android Studio run configuration 2018-10-05 15:23:31 -03:00
Torsten Grote
d6d132a9cf Add Bearer Authentication to REST API 2018-10-05 15:23:31 -03:00
Torsten Grote
98d1ea7730 briar-headless: Add more controller tests
Current controller line coverage: 100%
2018-10-05 15:23:31 -03:00
Torsten Grote
159fd34c0c Use Conversation Manager for message retrieval 2018-10-05 15:23:31 -03:00
Torsten Grote
9e7a387ea4 Turn output classes into Kotlin data classes 2018-10-05 15:23:31 -03:00
Torsten Grote
138e520e6c briar-headless: Add command line arguments 2018-10-05 15:23:31 -03:00
Torsten Grote
5783c1dfd8 briar-headless: Add a websocket controller for private message events
Also version API URLs
2018-10-05 15:23:31 -03:00
Torsten Grote
348968018a Migrate REST classes to Kotlin and upgrade Javalin 2018-10-05 15:23:31 -03:00
Torsten Grote
33c509cd1f briar-headless: Add Kotlin and first unit test for blogs with Mockk 2018-10-05 15:23:31 -03:00
Torsten Grote
bea77151bd briar-headless: Add API to list all contacts 2018-10-05 15:23:31 -03:00
Torsten Grote
787e62345f Add simple MessagingController 2018-10-05 15:23:31 -03:00
Torsten Grote
48f6a3b91f Add Tor plugin to headless client and introduce new ConfigurationManager 2018-10-05 15:23:31 -03:00
Torsten Grote
a798e25bf2 Save app data always in $HOME/.briar for now 2018-10-05 15:23:31 -03:00
Torsten Grote
31e4045cf7 Try to shutdown cleanly when server stops or SIGINT is received 2018-10-05 15:23:31 -03:00
Torsten Grote
5334a8c9ca Add basic support for listing and writing blog posts 2018-10-05 15:23:31 -03:00
Torsten Grote
d11f1d2805 Add a poor man's argument to explicitly turn on verbose logging 2018-10-05 15:23:31 -03:00
Torsten Grote
0d1ebddcd2 Allow account creation and reading password from STDIN 2018-10-05 15:23:31 -03:00
Torsten Grote
6c296c1348 Proof-of-Concept Headless Client 2018-10-05 15:23:31 -03:00
akwizgran
87701e5f07 Merge branch 'transactional-db' into 'master'
Transactional DB interface

See merge request briar/briar!945
2018-10-05 15:19:40 +00:00
Torsten Grote
3aae01d152 Merge branch 'panic-app-list' into 'master'
Update list of panic button apps after installing app

Closes #1392

See merge request briar/briar!940
2018-10-05 14:18:44 +00:00
akwizgran
bc298ba68a Remove unnecessary final modifiers. 2018-10-05 09:34:41 +01:00
akwizgran
2623eaa149 Remove unnecessary throwing variants. 2018-10-05 09:32:01 +01:00
akwizgran
7359b6942a Use transactional DB interface for ForumManagerImpl. 2018-10-04 15:59:10 +01:00
akwizgran
3bcc532b4b Add transactional DB interface. 2018-10-04 15:56:21 +01:00
akwizgran
4d08c69779 Revert spurious changes to run configurations. 2018-10-03 13:08:00 +01:00
Torsten Grote
a6cd8937f7 Remove space reserved for icons from preferences
Works around https://issuetracker.google.com/issues/111907042

Might be replacable with a solution from
https://issuetracker.google.com/issues/111907042 in the future.
2018-10-03 12:58:49 +01:00
akwizgran
e8566906ef Update gradle-witness to exclude android.jar from dependencies. 2018-10-03 12:48:29 +01:00
akwizgran
929102ed60 Upgrade build tools to 28.0.3. 2018-10-03 12:48:29 +01:00
akwizgran
3b871f5932 Update ProGuard rules for new OkHttp version. 2018-10-03 12:48:29 +01:00
akwizgran
b972d1fc13 Update ACRA usage for new version. 2018-10-03 12:48:28 +01:00
akwizgran
ccbeee60a7 Upgrade Rome, OkHttp and jsoup libraries. 2018-10-03 12:48:28 +01:00
akwizgran
074b10e177 Upgrade JNA library. 2018-10-03 12:48:28 +01:00
akwizgran
031516ccce Upgrade curve25519-java library. 2018-10-03 12:48:28 +01:00
akwizgran
7d2f1abb94 Upgrade Gradle APT and Animal Sniffer plugins. 2018-10-03 12:48:28 +01:00
akwizgran
00b9c76bb8 Upgrade ACRA. 2018-10-03 12:48:28 +01:00
akwizgran
4d9fab85cb Upgrade zxing, material tap target libraries. 2018-10-03 12:48:28 +01:00
akwizgran
bd2514a299 Upgrade support library to 28.0.0. 2018-10-03 12:48:27 +01:00
akwizgran
e795efc7fc Bump compileSdkVersion for bramble-android. 2018-10-03 12:48:27 +01:00
akwizgran
6691d2164f Upgrade Gradle Android plugin and build tools. 2018-10-03 12:48:27 +01:00
Administrator
a384450c36 Merge branch '1373-format-numbers' into 'master'
Format numbers in locale as well (not all languages use the same)

See merge request briar/briar!943
2018-10-03 11:41:56 +00:00
akwizgran
b375e9873c Merge branch '1409-localize-crash-screen' into 'master'
Localize crash screen in language defined in settings

Closes #1409

See merge request briar/briar!944
2018-10-03 07:53:41 +00:00
Torsten Grote
cb30c3885a Localize crash screen in language defined in settings 2018-10-02 17:56:33 -03:00
Torsten Grote
6ee81eb24c Format numbers in locale as well (not all languages use the same)
Done according to
https://developer.android.com/training/basics/supporting-devices/languages#FormatNumbers
2018-10-02 17:31:23 -03:00
Torsten Grote
c14ebe82ce Merge branch '1365-keep-screen-on' into 'master'
Keep screen on while QR code viewfinder is open

See merge request briar/briar!942
2018-10-02 11:20:46 +00:00
Torsten Grote
00e9f894b1 Merge branch 'remove-tor-patch' into 'master'
Remove unused patches

See merge request briar/briar!941
2018-10-02 11:13:06 +00:00
akwizgran
499c586a59 Keep screen on while scanning QR code. 2018-10-02 12:03:29 +01:00
akwizgran
64f9ce7306 Remove unused patches. 2018-10-02 11:49:29 +01:00
akwizgran
071d961ed1 Remove debug logging. 2018-09-28 11:31:22 +01:00
akwizgran
cb9efc5fec Fix lint warnings. 2018-09-28 11:28:47 +01:00
akwizgran
f9e292f734 Update panic app list after installing app. 2018-09-28 11:27:08 +01:00
265 changed files with 4217 additions and 1895 deletions

View File

@@ -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>

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View 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>

View File

@@ -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'

View File

@@ -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.0:protos-26.2.0.jar:306784ac579be37f64f110204ea3fad8568e09e10817bb574bac0fc71b6402c5',
'com.android.tools.analytics-library:shared:26.2.0:shared-26.2.0.jar:10782c593b7edcaf63cf79f7179c4652fee1c01298e0d81d888eea5ea1c320d4',
'com.android.tools.analytics-library:tracker:26.2.0:tracker-26.2.0.jar:74e3cf587bcd6f5f14a6cddb8123c6940baf9947582f5356c25dd852c4726564',
'com.android.tools.build:aapt2:3.2.0-4818971:aapt2-3.2.0-4818971-linux.jar:0d86e045e9eb009ab8add53cf0e5c0f48674e8c8a4edd3b19fc978ea38508bb1',
'com.android.tools.build:apksig:3.2.0:apksig-3.2.0.jar:d393b14a2f85a4052645fa0b59e111b46dea0fd59a934bec6cf1e997a0640e30',
'com.android.tools.build:apkzlib:3.2.0:apkzlib-3.2.0.jar:d8a3404d63a1037207bc2eb472209c606f8143d201f484da9dacd04ffa538c31',
'com.android.tools.build:builder-model:3.2.0:builder-model-3.2.0.jar:7deb28465f2303fdc9df58ba83d080193edefe1624784d3ce9e0a0949ae05a49',
'com.android.tools.build:builder-test-api:3.2.0:builder-test-api-3.2.0.jar:b7c6676da0793240f16874c4008f12ed3ed1bc56a4a73f5e760044024ac9a02a',
'com.android.tools.build:builder:3.2.0:builder-3.2.0.jar:191df45703f615a02db16ce5ce78489f1ec91f9b83e677bec5de29de765e326d',
'com.android.tools.build:gradle-api:3.2.0:gradle-api-3.2.0.jar:5645c5ee4138b1354b24bed43e005964296a5f1ef48b6772ed9800159dcc2830',
'com.android.tools.build:manifest-merger:26.2.0:manifest-merger-26.2.0.jar:b821830bf067852688673cae01fb571c04da094985a3894878070b50f0587edf',
'com.android.tools.ddms:ddmlib:26.2.0:ddmlib-26.2.0.jar:ff9b6951698a6ec005b25e5e3b5f7fe960f566131ba8fcdf713392e10cc2dd50',
'com.android.tools.external.com-intellij:intellij-core:26.2.0:intellij-core-26.2.0.jar:03724f5aff30c8fff3d0b0c05e71f8dc21f246952fe1f3b5916bfb594e337bad',
'com.android.tools.external.com-intellij:kotlin-compiler:26.2.0:kotlin-compiler-26.2.0.jar:2f82e0c2687afe7c4941bdf6702d9a16d71d030a7ddb384a04a0537a361f4bb1',
'com.android.tools.external.org-jetbrains:uast:26.2.0:uast-26.2.0.jar:a439cafcddb2963620fd737005e3ce0560313362ede37e6c6eac5f10ff19eb94',
'com.android.tools.layoutlib:layoutlib-api:26.2.0:layoutlib-api-26.2.0.jar:5987488847729bad6a1e72cd5dcc46b68b61d8f05ef60858b87ed1d3b9bacc58',
'com.android.tools.lint:lint-api:26.2.0:lint-api-26.2.0.jar:aa4dc0399b6ae602286b9b106c661b50f6a8e1143c5949d3e7fd93c4a4d1513d',
'com.android.tools.lint:lint-checks:26.2.0:lint-checks-26.2.0.jar:c8c8dfdbd5c97a371e239b6000f4c6929bce315c2b13e690aaf34e3fcb045520',
'com.android.tools.lint:lint-gradle-api:26.2.0:lint-gradle-api-26.2.0.jar:befc1ad242e5e89e8977b2f799c465fee1b096ac645eba659acab832980599cd',
'com.android.tools.lint:lint-gradle:26.2.0:lint-gradle-26.2.0.jar:332d257f20537020fd268468f678ccfbcc0be494fa50526b63c532836dc7c5dd',
'com.android.tools.lint:lint-kotlin:26.2.0:lint-kotlin-26.2.0.jar:c149e09434aa24a6f7161dbd4b09f08c46f018cb0ee1ee2d1a32bf3b5015e081',
'com.android.tools.lint:lint:26.2.0:lint-26.2.0.jar:4df411ea5ef106780073c6e953ab591a55d52e94c9b1cd2f99970a124eca405e',
'com.android.tools:annotations:26.2.0:annotations-26.2.0.jar:af775f75168acbadb2ff61d6955e4fbeca9b1bd9f7a40b54ab7a489e30af45bb',
'com.android.tools:common:26.2.0:common-26.2.0.jar:3e60e91bc7c7a634bbfae3522c5e0b49e8a268a2d69904bed4bd55833f8bb0c9',
'com.android.tools:dvlib:26.2.0:dvlib-26.2.0.jar:c4d8fac5b8432e3676256ece4e25c3e55e8ef2da4298fb060cc6086d7e2235fc',
'com.android.tools:repository:26.2.0:repository-26.2.0.jar:9c9e320195e7e6d9bbdf9b5b0ed1d7e4da3625ad344902cb47b32c9ef8306d82',
'com.android.tools:sdk-common:26.2.0:sdk-common-26.2.0.jar:1e32745633177b28419041d9bd182bb99b51d1a7ad3f9b1c8b5292ed799656cd',
'com.android.tools:sdklib:26.2.0:sdklib-26.2.0.jar:08e2715f9abd3870df8e311f77ff6f3dae61193c1f6a2de78bf2e49c6ba4725a',
'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',
@@ -81,11 +83,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',
]
}

View File

@@ -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));
}
}

View File

@@ -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());

View File

@@ -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.

View File

@@ -0,0 +1,6 @@
package org.briarproject.bramble.api.db;
public interface DbCallable<R, E extends Exception> {
R call(Transaction txn) throws DbException, E;
}

View File

@@ -0,0 +1,6 @@
package org.briarproject.bramble.api.db;
public interface DbRunnable<E extends Exception> {
void run(Transaction txn) throws DbException, E;
}

View File

@@ -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();
}

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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'

View File

@@ -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());

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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();

View File

@@ -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();

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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',
]
}

View File

@@ -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'

View File

@@ -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',

View File

@@ -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'

View File

@@ -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

View File

@@ -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

View File

@@ -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;
@@ -42,6 +43,10 @@ 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 {

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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() {

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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() {

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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) {

View File

@@ -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);
}
}

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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));
}

View File

@@ -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());
}

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();
};

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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(" ");

View File

@@ -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);

View File

@@ -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));
}
}

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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">اللغة &amp; المنطقة</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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 dune 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 dune 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 à lappareil\u2026</string>
<string name="authenticating_with_device">Autentification avec lappareil\u2026</string>
@@ -370,6 +373,7 @@
<string name="panic_app_setting_none">Aucune</string>
<string name="dialog_title_connect_panic_app">Confirmer lapplication durgence</string>
<string name="dialog_message_connect_panic_app">Voulez-vous vraiment autoriser %1$s à déclencher les actions destructrices du bouton durgence?</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 lon appuie sur un bouton durgence</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 mavoir parlé de Briar!</string>
<!--This is a message to be used in screenshots.-->
<string name="screenshot_message_3">Pas de problème, jespère que tu aimeras lappli 😀</string>
</resources>

View File

@@ -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>

View File

@@ -1,50 +1,85 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<!--Setup-->
<string name="setup_title">ברוך הבא אל Briar</string>
<string name="setup_name_explanation">כינויך יוראה ליד תוכן כלשהו שתכתוב. אינך יכול לשנות אותו לאחר יצירת חשבונך.</string>
<string name="setup_next">הבא</string>
<string name="choose_nickname">בחרו כינוי</string>
<string name="choose_password">בחרו סיסמה</string>
<string name="confirm_password">אשרו את הסיסמה</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_doze_intro">כדי לקבל הודעות, Briar צריך להישאר מחובר ברקע.</string>
<string name="setup_doze_explanation">כדי לקבל הודעות, Briar צריך להישאר מחובר ברקע. אנא השבת מיטובי סוללה כך ש־Briar יוכל להישאר מחובר.</string>
<string name="setup_doze_button">התר חיבורים</string>
<string name="choose_nickname">בחר את כינויך</string>
<string name="choose_password">בחר את סיסמתך</string>
<string name="confirm_password">אשר את סיסמתך</string>
<string name="name_too_long">השם ארוך מדי</string>
<string name="password_too_weak">הסיסמה חלשה מדי</string>
<string name="passwords_do_not_match">הסיסמאות לא תואמות</string>
<string name="create_account_button">יצירת חשבון</string>
<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_button">הגן על Briar</string>
<string name="setup_huawei_help">אם Briar אינו מוסף אל רשימת היישומים המוגנים, הוא לא יוכל לרוץ ברקע.</string>
<string name="warning_dozed">%s לא היה יכול לרוץ ברקע</string>
<!--Login-->
<string name="enter_password">סיסמה</string>
<string name="try_again">הסיסמה שגוייה, נסו שוב</string>
<string name="sign_in_button">הכנסו</string>
<string name="try_again">סיסמה שגויה, נסה שוב</string>
<string name="sign_in_button">היכנס</string>
<string name="forgotten_password">שכחתי את הסיסמה שלי</string>
<string name="dialog_title_lost_password">הסיסמה נאבדה</string>
<string name="dialog_title_lost_password">סיסמה אבודה</string>
<string name="dialog_message_lost_password">חשבון הבריאר שלכם שמור ומוצפן על המכשיר, לא בענן, כך שלא נוכל לשחזר את הסיסמה שלכם. למחוק את החשבון שלכם ולהתחיל מחדש?
זהירות: הזהויות שלכם, אנשי הקשר וההודעות יאבדו לצמיתות.</string>
<string name="startup_failed_notification_title">אפליקציית בריאר נכשלה באיתחול</string>
<string name="startup_failed_activity_title">איתחול בריאר נכשל</string>
<string name="startup_failed_service_error">בריאר לא הצליח לאתחל תוסף הכרחי. התקנת בריאר מחדש לרוב פותרת בעייה זו. שימו לב שאז תאבדו את חשבונכם וכל המידע המשוייך אליו כיוון שבריאר לא משתמש בשרתים מרכזיים לשמירת המידע שלכם עליהם.</string>
<string name="startup_failed_notification_title">Briar לא היה ניתן להתחיל</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>
<plurals name="expiry_warning">
<item quantity="one">זאת גרסת בחינה של Briar. חשבונך יפוג תוך יום %d ואינו יכול להתחדש.</item>
<item quantity="two">זאת גרסת בחינה של Briar. חשבונך יפוג תוך %d ימים ואינו יכול להתחדש.</item>
<item quantity="many">זאת גרסת בחינה של Briar. חשבונך יפוג תוך %d ימים ואינו יכול להתחדש.</item>
<item quantity="other">זאת גרסת בחינה של Briar. חשבונך יפוג תוך %d ימים ואינו יכול להתחדש.</item>
</plurals>
<string name="expiry_update">תאריך תפוגת ההיבחנות הוארך. חשבונך יפוג תוך %d ימים.</string>
<string name="expiry_date_reached">פג תוקפה של תוכנה זו.
תודה לכם שבדקתם.</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>
<string name="blogs_button">בלוגים</string>
<!--This is part of the main menu. The app will be locked when this is tapped.-->
<string name="lock_button">נעל יישום</string>
<string name="settings_button">הגדרות</string>
<string name="sign_out_button">יציאה</string>
<string name="sign_out_button">התנתק</string>
<!--Transports-->
<string name="transport_tor">אינטרנט</string>
<string name="transport_bt">בלוטות\'</string>
<string name="transport_bt">שן כחולה</string>
<string name="transport_lan">Wi-Fi</string>
<!--Notifications-->
<string name="ongoing_notification_title">מחוברים לבריאר</string>
<string name="ongoing_notification_text">לחצו על מנת לפתוח את בריאר</string>
<string name="reminder_notification_title">נותקת מן Briar</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>
<plurals name="private_message_notification_text">
<item quantity="one">הודעה פרטית חדשה.</item>
<item quantity="two">%d הודעות פריטות חדשות.</item>
<item quantity="many">%d הודעות פריטות חדשות.</item>
<item quantity="other">%d הודעות פריטות חדשות.</item>
<item quantity="two">%d הודעות פרטיות חדשות.</item>
<item quantity="many">%d הודעות פרטיות חדשות.</item>
<item quantity="other">%d הודעות פרטיות חדשות.</item>
</plurals>
<plurals name="group_message_notification_text">
<item quantity="one">הודעה קבוצתית חדשה.</item>
@@ -53,66 +88,78 @@
<item quantity="other">%d הודעות קבוצתיות חדשות.</item>
</plurals>
<plurals name="forum_post_notification_text">
<item quantity="one">פוסט חדש בפורומים.</item>
<item quantity="two">%d פוסטים חדשים בפורומים.</item>
<item quantity="many">%d פוסטים חדשים בפורומים.</item>
<item quantity="other">%d פוסטים חדשים בפורומים.</item>
<item quantity="one">רשומת פורום חדשה.</item>
<item quantity="two">%d רשומות פורום חדשות.</item>
<item quantity="many">%d רשומות פורום חדשות.</item>
<item quantity="other">%d רשומות פורום חדשות.</item>
</plurals>
<plurals name="blog_post_notification_text">
<item quantity="one">פוסט חדש בבלוגים.</item>
<item quantity="two">%d פוסטים חדשים בבלוגים.</item>
<item quantity="many">%d פוסטים חדשים בבלוגים.</item>
<item quantity="other">%d פוסטים חדשים בבלוגים.</item>
<item quantity="one">רשומת בלוג חדשה.</item>
<item quantity="two">%d רשומות בלוג חדשות.</item>
<item quantity="many">%d רשומות בלוג חדשות.</item>
<item quantity="other">%d רשומות בלוג חדשות.</item>
</plurals>
<!--Misc-->
<string name="now">עכשיו</string>
<string name="show">הצג</string>
<string name="show">הראה</string>
<string name="hide">הסתר</string>
<string name="ok">אישור</string>
<string name="cancel">ביטול</string>
<string name="cancel">בטל</string>
<string name="got_it">הבנתי</string>
<string name="delete">מחק</string>
<string name="accept">קבל</string>
<string name="decline">סרב</string>
<string name="options">אפשרויות</string>
<string name="online">מחובר</string>
<string name="offline">לא מחובר</string>
<string name="online">מקוון</string>
<string name="offline">מנותק</string>
<string name="send">שלח</string>
<string name="allow">להתיר</string>
<string name="allow">התר</string>
<string name="open">פתח</string>
<string name="no_data">לא נמצא מידע</string>
<string name="no_data">אין נתונים</string>
<string name="ellipsis">...</string>
<string name="text_too_long">הטקסט שהזנתם ארוך מדי</string>
<string name="show_onboarding">הצג עזרה</string>
<string name="text_too_long">הטקסט המוכנס ארוך מדי</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="date_no_private_messages">אין הודעות.</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="contact_deleted_toast">איש הקשר נמחק</string>
<string name="no_private_messages">אין הודעות להראות</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="contact_deleted_toast">איש קשר נמחק</string>
<!--Adding Contacts-->
<string name="add_contact_title">הוספת איש קשר</string>
<string name="add_contact_title">הוסף איש קשר</string>
<string name="face_to_face">אתם חייבים להפגש עם האדם שאותו תרצו להוסיף כאיש קשר.
זאת על מנת למנוע מצב שבו מתחזים אליכם או קוראים הודעות שלכם בעתיד.</string>
<string name="continue_button">המשך</string>
<string name="try_again_button">נסו שוב</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_added_toast">איש קשר התווסף: %s</string>
<string name="contact_already_exists">איש קשר %s קיים כבר</string>
<string name="qr_code_invalid">קוד ה- QR אינו תקף</string>
<string name="connecting_to_device">מתחבר למכשיר\u2026</string>
<string name="qr_code_invalid">קוד ה־QR אינו תקף</string>
<string name="qr_code_too_old">קוד ה־QR שסרקת מגיע מגרסה ישנה יותר של %s.\n\nאנא בקש מאיש הקשר שלך לשדרג את הגרסה האחרונה ואז נסה שוב.</string>
<string name="qr_code_too_new">קוד ה־QR שסרקת מגיע מגרסה חדשה יותר של %s.\n\nאנא שדרג אל הגרסה האחרונה ואז נסה שוב.</string>
<string name="camera_error">שגיאת מצלמה</string>
<string name="connecting_to_device">מתחבר אל מכשיר\u2026</string>
<string name="authenticating_with_device">מזדהה מול המכשיר\u2026</string>
<string name="connection_error_title">לא היה ניתן להתחבר אל איש הקשר שלך</string>
<string name="connection_error_explanation">אנא בדוק ששניכם מחוברים אל אותה רשת Wi-Fi.</string>
<string name="connection_error_feedback">אם בעיה נמשכת, אנא <a href="feedback">שלח משוב</a> כדי לעזור לנו לשפר את היישום.</string>
<!--Introductions-->
<string name="introduction_onboarding_title">הציגו את אנשי קשר שלכם</string>
<string name="introduction_onboarding_text">אתם יכולים להציג את אנשי הקשר שלכם אחד לשני, כך שהם לא צריכים להפגש פנים-מול-פנים על מנת להתחבר בבריאר.</string>
<string name="introduction_menu_item">הציגו איש קשר</string>
<string name="introduction_activity_title">בחרו איש קשר</string>
<string name="introduction_activity_title">בחר איש קשר</string>
<string name="introduction_not_possible">יש לך כבר היכרות אחת בתהליך עם אנשי קשר אלו. אנא התר לה תחילה לסיים. אם אתה או אנשי הקשר שלך לעיתים נדירות מקוונים, זה עשוי לקחת זמן מה.</string>
<string name="introduction_message_title">הציגו אנשי קשר</string>
<string name="introduction_message_hint">הוסיפו הודעה (לא חובה)</string>
<string name="introduction_message_hint">הוסף הודעה (רשותי)</string>
<string name="introduction_button">הציגו איש קשר</string>
<string name="introduction_sent">הצגת איש קשר נשלחה.</string>
<string name="introduction_error">הייתה שגיאה בעת הצגת איש קשר.</string>
@@ -122,6 +169,7 @@
<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_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>
@@ -133,6 +181,8 @@
<item quantity="other">נוספו %d אנשי קשר חדשים.</item>
</plurals>
<!--Private Groups-->
<string name="groups_list_empty">אין קבוצות להראות</string>
<string name="groups_list_empty_action">הקש על הצלמית + כדי ליצור קבוצה, או בקש מאנשי הקשר שלך לשתף איתך קבוצות</string>
<string name="groups_created_by">נוצר על ידי %s</string>
<plurals name="messages">
<item quantity="one">הודעה %d</item>
@@ -140,26 +190,26 @@
<item quantity="many">%d הודעות</item>
<item quantity="other">%d הודעות</item>
</plurals>
<string name="groups_group_is_empty">קבוצה זו הינה ריקה</string>
<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_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_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_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_leave_dialog_title">אשרו את עזיבת הקבוצה</string>
<string name="groups_leave_dialog_message">האם אתם בטוחים שתרצו לעזוב את הקבוצה?</string>
<string name="groups_dissolve">פיזור הקבוצה</string>
<string name="groups_dissolve_dialog_title">אשרו פיזור הקבוצה</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_dissolve">פזר קבוצה</string>
<string name="groups_dissolve_dialog_title">אשר פיזור קבוצה</string>
<string name="groups_dissolve_dialog_message">האם אתם בטוחים שתרצו לפזר את הקבוצה?
כל המשתתפים לא יוכלו להמשיך את השיחה ועלולים לא לקבל את ההודעות האחרונות.</string>
@@ -169,24 +219,24 @@
לא תוכלו יותר לכתוב הודעות לקבוצה ואולי אף לא תקבלו את כל ההודעות שנכתבו.</string>
<!--Private Group Invitations-->
<string name="groups_invitations_title">הזמנות קבוצתיות</string>
<string name="groups_invitations_title">הזמנות לקבוצה</string>
<string name="groups_invitations_invitation_sent">הזמנתם את %1$s להצטרף לקבוצה \"%2$s\".</string>
<string name="groups_invitations_invitation_received">%1$s הזמינו אתכם להצטרף לקבוצה \"%2$s\".</string>
<string name="groups_invitations_joined">הצטרפתם לקבוצה</string>
<string name="groups_invitations_joined">הצטרף אל קבוצה</string>
<string name="groups_invitations_declined">הזמנה קבוצתית נדחתה</string>
<plurals name="groups_invitations_open">
<item quantity="one">הזמנה קבוצתית פתוחה %d</item>
<item quantity="two">%dהזמנות קבוצתית פתוחות </item>
<item quantity="many">%dהזמנות קבוצתית פתוחות </item>
<item quantity="other">%dהזמנות קבוצתית פתוחות </item>
<item quantity="two">%d הזמנות קבוצתיות פתוחות</item>
<item quantity="many">%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="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>
<!--Private Groups Revealing Contacts-->
<string name="groups_reveal_contacts">הצגת אנשי קשר</string>
<string name="groups_reveal_contacts">חשוף אנשי קשר</string>
<string name="groups_reveal_dialog_message">אתם יכולים לבחור אם לחשוף את אנשי הקשר שלכם למשתתפים הנוכחיים והעתידיים של קבוצה זו.
חשיפת אנשי הקשר תגרום לחיבור עם הקבוצה להיות מהיר ואמין יותר, כי תוכלו לתקשר עם אנשי הקשר שנחשפו גם אם יוצר הקבוצה אינו מחובר.</string>
@@ -195,68 +245,247 @@
<string name="groups_reveal_visible_revealed_by_contact">היחסים עם האיש קשר גלויים לקבוצה (נחשפו על %s)</string>
<string name="groups_reveal_invisible">היחסים עם האיש קשר אינם גלויים לקבוצה</string>
<!--Forums-->
<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_posts">אין פוסטים</string>
<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="forum_created_toast">פורום נוצר</string>
<string name="no_forum_posts">אין רשומות להראות</string>
<string name="no_posts">אין רשומות</string>
<plurals name="posts">
<item quantity="one">פוסט %d</item>
<item quantity="two">%d פוסטים</item>
<item quantity="many">%d פוסטים</item>
<item quantity="other">%d פוסטים</item>
<item quantity="one">רשומה %d</item>
<item quantity="two">%d רשומות</item>
<item quantity="many">%d רשומות</item>
<item quantity="other">%d רשומות</item>
</plurals>
<string name="forum_message_reply_hint">תגובה חדשה</string>
<string name="btn_reply">להשיב</string>
<string name="forum_leave">עזיבת הפורום</string>
<string name="dialog_title_leave_forum">אשרו את עזיבת הפורום</string>
<string name="dialog_button_leave">לעזוב</string>
<string name="forum_new_entry_posted">רשומת פורום פורסמה</string>
<string name="forum_new_message_hint">רשומה חדשה</string>
<string name="forum_message_reply_hint">תשובה חדשה</string>
<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_button_leave">עזוב</string>
<string name="forum_left_toast">עזב פורום</string>
<!--Forum Sharing-->
<string name="forum_share_button">שיתוף הפורום</string>
<string name="forum_share_button">שתף פורום</string>
<string name="contacts_selected">אנשי קשר נבחרו</string>
<string name="activity_share_toolbar_header">בחרו אנשי קשר</string>
<string name="forum_shared_snackbar">שיתפתם את הפורום עם אנשי הקשר שנבחרו</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="activity_share_toolbar_header">בחר אנשי קשר</string>
<string name="no_contacts_selector">אין אנשי קשר להראות</string>
<string name="no_contacts_selector_action">אנא חזור לכאן לאחר הוספת איש קשר</string>
<string name="forum_shared_snackbar">פורום שותף עם אנשי קשר נבחרים</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_invitations_title">הזמנות לפורום</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="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="sharing_status">מעמד שיתוף</string>
<string name="sharing_status_forum">כל חבר פורום יכול לשתף את הפורום עם אנשי הקשר שלו. אתה משתף פורום זה עם אנשי הקשר הבאים. יתכן שיש חברי פורום אחרים שאינך יכול לראות.</string>
<string name="shared_with">משותף עם %1$d (%2$d מקוונים)</string>
<plurals name="forums_shared">
<item quantity="one">פורום %d משותף ע\"י אנשי קשר</item>
<item quantity="two">%d פורומים משותפים ע\"י אנשי קשר</item>
<item quantity="many">%d פורומים משותפים ע\"י אנשי קשר</item>
<item quantity="other">%d פורומים משותפים ע\"י אנשי קשר</item>
</plurals>
<string name="nobody">אף אחד</string>
<!--Blogs-->
<string name="blogs_remove_blog_ok">להסיר</string>
<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="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_feed_empty_state">אין רשומות להראות</string>
<string name="blogs_feed_empty_state_action">רשומות מאנשי הקשר שלך ומבלוגים שאתה רשום כמנוי אליהם יופיעו כאן
הקש על צלמית העט כדי לכתוב רשומה</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>
<!--Blog Sharing-->
<string name="blogs_sharing_share">שתף בלוג</string>
<string name="blogs_sharing_error">הייתה שגיאה בשיתוף בלוג זה.</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_invitations_title">הזמנות לבלוג</string>
<string name="blogs_sharing_joined_toast">רשום כמנוי אל בלוג</string>
<string name="blogs_sharing_declined_toast">הזמנה סורבה</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_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_updated">עודכן לאחרונה:</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>
<!--Settings Display-->
<string name="display_settings_title">מציג</string>
<string name="pref_language_title">שפה ואזור</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>
<string name="pref_theme_light">בהיר</string>
<string name="pref_theme_dark">כהה</string>
<string name="pref_theme_auto">אוטומטי (שעות יום)</string>
<string name="pref_theme_system">ברירת מחדל של מערכת</string>
<!--Settings Network-->
<string name="network_settings_title">רשתות</string>
<string name="bluetooth_setting">התחבר באמצעות שן כחולה</string>
<string name="bluetooth_setting_enabled">כאשר אנשי קשר בקרבה</string>
<string name="bluetooth_setting_disabled">רק בעת הוספת אנשי קשר</string>
<string name="tor_network_setting">התחבר באמצעות אינטרנט (Tor)</string>
<string name="tor_network_setting_automatic">אוטומטי על סמך מיקום</string>
<string name="tor_network_setting_without_bridges">השתמש ב־Tor בלי גשרים</string>
<string name="tor_network_setting_with_bridges">השתמש ב־Tor עם גשרים</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>
<!--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"-->
<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"-->
<string name="pref_lock_timeout_5">5 דקות</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_15">15 דקות</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_30">30 דקות</string>
<!--Will be shown in a list of lock times. Should fit into the %s of "automatically lock it after %s"-->
<string name="choose_new_password">סיסמא חדשה</string>
<string name="pref_lock_timeout_60">שעה 1</string>
<string name="pref_lock_timeout_never">לעולם לא</string>
<string name="pref_lock_timeout_never_summary">לעולם אל תנעל את Briar באופן אוטומטי</string>
<string name="change_password">שנה סיסמה</string>
<string name="current_password">סיסמה נוכחית</string>
<string name="choose_new_password">סיסמה חדשה</string>
<string name="confirm_new_password">אשר סיסמה חדשה</string>
<string name="password_changed">סיסמה שונתה.</string>
<string name="panic_setting">הגדרה ראשונית של כפתור בהלה</string>
<string name="panic_setting_title">כפתור בהלה</string>
<string name="panic_setting_hint">קבע כיצד Briar יגיב כשתשתמש ביישום כפתור בהלה</string>
<string name="panic_app_setting_title">יישום כפתור בהלה</string>
<string name="unknown_app">יישום בלתי ידוע</string>
<string name="panic_app_setting_summary">לא הוגדר יישום</string>
<string name="panic_app_setting_none">כלום</string>
<string name="panic_setting_signout_title">יציאה</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>
<string name="purge_setting_summary">מחק את חשבון Briar שלך אם כפתור בהלה נלחץ. זהירות: זה ימחק לצמיתות את הזהויות, אנשי הקשר וההודעות שלך</string>
<string name="uninstall_setting_title">הסר את Briar</string>
<string name="uninstall_setting_summary">זה דורש אישור ידני באירוע בהלה</string>
<!--Settings Notifications-->
<string name="notification_settings_title">הודעות מערכת</string>
<string name="notification_settings_title">התראות</string>
<string name="notify_sign_in_title">הזכר לי להתחבר</string>
<string name="notify_sign_in_summary">הראה תזכורת כשהטלפון מתחיל או כשהיישום יעודכן</string>
<string name="notify_private_messages_setting_title">הודעות פרטיות</string>
<string name="notify_lock_screen_setting_title">נעל מסך</string>
<string name="notify_private_messages_setting_summary">הראה התרעות עבור הודעות פרטיות</string>
<string name="notify_private_messages_setting_summary_26">הגדר התרעות עבור הודעות פרטיות</string>
<string name="notify_group_messages_setting_title">הודעות קבוצתיות</string>
<string name="notify_group_messages_setting_summary">הראה התרעות עבור הודעות קבוצתיות</string>
<string name="notify_group_messages_setting_summary_26">הגדר התרעות עבור הודעות קבוצתיות</string>
<string name="notify_forum_posts_setting_title">רשומות פורום</string>
<string name="notify_forum_posts_setting_summary">הראה התראות עבור רשומות פורום</string>
<string name="notify_forum_posts_setting_summary_26">הגדר התראות עבור רשומות פורום</string>
<string name="notify_blog_posts_setting_title">רשומות בלוג</string>
<string name="notify_blog_posts_setting_summary">הראה התראות עבור רשומות בלוג</string>
<string name="notify_blog_posts_setting_summary_26">הגדר התראות עבור רשומות בלוג</string>
<string name="notify_vibration_setting">רטט</string>
<string name="notify_sound_setting">צליל</string>
<string name="notify_sound_setting_default">צלצול ברירת מחדל</string>
<string name="notify_sound_setting_disabled">כלום</string>
<string name="choose_ringtone_title">בחר צלצול</string>
<string name="cannot_load_ringtone">לא ניתן לטעון צלצול</string>
<!--Settings Feedback-->
<string name="feedback_settings_title">משוב</string>
<string name="send_feedback">שלח משוב</string>
<!--Link Warning-->
<string name="link_warning_title">אזהרת קישור</string>
<string name="link_warning_intro">אתה עומד לפתוח את הקישור הבא עם יישום חיצוני.</string>
<string name="link_warning_text">זה יכול לשמש כדי לזהות אותך. חשוב על האם אתה בוטח באיש ששלח לך קישור זה ושקול לפתוח את הקישור עם Orfox.</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="report_is_encrypted">אנו מבטיחים שהדוח הזה מוצפן ונשלח באופן מאובטח.</string>
<string name="feedback_title">משוב</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="could_not_load_report_data">לא היה ניתן לטעון נתוני דיווח.</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>
<!--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_denied_toast">הרשאת מצלמה לא הוענקה</string>
<string name="qr_code">קוד QR</string>
<string name="show_qr_code_fullscreen">הראה קוד QR במסך מלא</string>
<!--App Locking-->
<string name="lock_unlock">בטל נעילת Briar</string>
<string name="lock_unlock_verbose">אנא הכנס PIN, דפוס או סיסמה של מכשריך כדי לבטל נעילת 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>
<!--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>

Some files were not shown because too many files have changed in this diff Show More