Compare commits

..

63 Commits

Author SHA1 Message Date
akwizgran
934997ed91 Check for partial setup of clients due to feature flags reverting. 2023-01-31 11:23:19 +00:00
Torsten Grote
0b94814620 Merge branch 'remove-migration-code' into 'master'
Remove various bits of code whose migration periods have passed

See merge request briar/briar!1750
2023-01-30 13:59:02 +00:00
akwizgran
e82e11acfa Merge branch '2384-mailbox-problem-notification' into 'master'
Clear mailbox problem notification after unlinking

Closes #2384

See merge request briar/briar!1764
2023-01-27 15:09:27 +00:00
Torsten Grote
795461d9a8 Clear mailbox problem notification after unlinking 2023-01-27 11:49:01 -03:00
Torsten Grote
7b8d01cfe0 Merge branch '1822-rss-feeds-backend' into 'master'
Resolve "Import RSS feeds shared by other apps"

See merge request briar/briar!1763
2023-01-25 11:39:34 +00:00
akwizgran
abd04ee7f5 Add tests for feed serialisation/deserialisation. 2023-01-24 17:27:52 +00:00
akwizgran
cc5365eaf0 Remove redundant comparison from test. 2023-01-24 17:27:34 +00:00
akwizgran
6b20b03698 Bump version numbers for 1.4.20 release. 2023-01-24 15:51:48 +00:00
akwizgran
9da7fbf4f6 Update translations. 2023-01-24 15:51:26 +00:00
akwizgran
f64f442fcf Merge branch 'add-comment-for-is-connected' into 'master'
Add comment about NetworkInfo#isConnected()

See merge request briar/briar!1762
2023-01-24 15:25:52 +00:00
akwizgran
6eda2f6d13 AnimalSniffer doesn't allow StandardCharsets in tests. 2023-01-24 14:50:40 +00:00
akwizgran
6faa095dfb FeedMatcher interface doesn't need to be public. 2023-01-24 14:48:55 +00:00
akwizgran
4007fca668 Add integration tests for importing an RSS feed from a file. 2023-01-24 14:15:03 +00:00
akwizgran
28a747f7f3 Add method for adding an RSS feed from an input stream. 2023-01-24 13:57:44 +00:00
Sebastian Kürten
fd2d5c9173 Add comment about NetworkInfo#isConnected() 2023-01-24 14:48:03 +01:00
akwizgran
8f7bb9d26b Don't overwrite the list of feeds after fetching. 2023-01-24 13:28:22 +00:00
akwizgran
dc220200b6 Match newly added RSS feeds to existing feeds. 2023-01-24 12:43:14 +00:00
Torsten Grote
0cea137d75 Merge branch 'update-tor-bridges' into 'master'
Update Tor bridges

See merge request briar/briar!1761
2023-01-23 15:06:28 +00:00
akwizgran
2eef34f424 Use new transaction wrappers. 2023-01-23 13:02:16 +00:00
akwizgran
a68fff9dd2 Merge branch 'tor-0.4.7.13' into 'master'
Upgrade Tor to 0.4.7.13

See merge request briar/briar!1760
2023-01-23 12:11:32 +00:00
akwizgran
ddc8f4a7d7 Add three non-default obfs4 bridges. 2023-01-20 16:12:47 +00:00
akwizgran
f961b6a80b Remove three failing bridges. 2023-01-20 16:11:11 +00:00
akwizgran
93439d9c17 Update translations. 2023-01-20 15:50:28 +00:00
akwizgran
f3ee884816 Upgrade Tor to 0.4.7.13. 2023-01-20 15:34:23 +00:00
akwizgran
8ca22043cf Merge branch '1897-sharing-status' into 'master'
Introduce SharingStatus to report more fine-grained status

Closes #1897

See merge request briar/briar!1758
2023-01-20 14:33:32 +00:00
Torsten Grote
9353b78da8 Clarify sharing state docs 2023-01-20 11:13:35 -03:00
Torsten Grote
429bbe1275 Introduce more sharing states 2023-01-20 11:13:35 -03:00
Torsten Grote
c5fb1416bd Update JavaDoc for SharingState change 2023-01-20 11:13:34 -03:00
Torsten Grote
e52cbd896e Introduce SharingStatus to report more fine-grained status 2023-01-20 11:13:34 -03:00
akwizgran
ab1b8784b7 Merge branch '90-clickable-links' into 'master'
Resolve "Handle Hyperlinks (Clickable Links)"

Closes #90

See merge request briar/briar!1757
2023-01-20 13:47:08 +00:00
akwizgran
55a4daa92f Merge branch 'progressbar-remove-contact' into 'master'
Show progress bar while removing contact

See merge request briar/briar!1759
2023-01-20 13:44:29 +00:00
akwizgran
e52250f1e4 Don't sort list of RSS feeds in UI. 2023-01-18 15:04:38 +00:00
akwizgran
33d01aac8c Add matcher for matching an imported feed against existing feeds. 2023-01-18 15:04:38 +00:00
akwizgran
b920382e44 Store additional properties of RSS feed in metadata. 2023-01-18 15:04:38 +00:00
akwizgran
1a2f85f701 Small code cleanups for feed manager, don't fetch new feeds twice. 2023-01-18 15:04:33 +00:00
Airplane Mode
186bcc0b47 Show progress bar while removing contact 2023-01-16 23:56:26 +00:00
Torsten Grote
8b9140f477 Merge branch 'tor-0.4.7.12' into 'master'
Upgrade Tor to 0.4.7.12

See merge request briar/briar!1755
2023-01-13 16:25:42 +00:00
Katelyn Dickey
f959c32935 Remove autoLink attribute which was causing warnings to show twice, and highlight links in comments before a blog is expanded 2023-01-05 17:20:40 -05:00
akwizgran
1c060bc6db Upgrade Tor to 0.4.7.12. 2023-01-04 17:51:46 +00:00
Katelyn Dickey
5e44e4d308 Add clickable links to blog comments 2023-01-03 20:52:36 -05:00
Katelyn Dickey
75d5dec45f Add clickable links to notices/requests 2023-01-03 18:38:13 -05:00
Katelyn Dickey
d825227eb5 Add clickable links to threads (forums/groups) 2023-01-03 18:38:07 -05:00
Katelyn Dickey
967dd1f18d Add clickable links for conversations 2023-01-03 18:37:40 -05:00
akwizgran
4a4147b563 Bump version numbers for 1.4.19 release. 2022-12-30 11:15:32 +00:00
akwizgran
08b72af647 Update translations. 2022-12-30 11:07:38 +00:00
akwizgran
528e090c6f Merge branch '2409-require-obsolete-bluetooth-permission' into 'master'
Require obsolete BLUETOOTH permission on API 31

Closes #2409

See merge request briar/briar!1754
2022-12-30 10:56:29 +00:00
akwizgran
652f9e5705 Require obsolete BLUETOOTH permission on API 31.
This is a workaround for a platform bug on Xiaomi/Redmi/POCO devices that still checks for the obsolete permission.
2022-12-28 14:12:30 +00:00
akwizgran
6a91ec7a6b Merge branch '2407-bluetooth-permission' into 'master'
Always check Bluetooth permission when trying to get own address

Closes #2407

See merge request briar/briar!1753
2022-12-28 11:07:31 +00:00
akwizgran
c3a9eff96b Always check Bluetooth permission when trying to get own address. 2022-12-22 17:46:12 +00:00
akwizgran
bd05d893eb Merge branch '2397-wrong-type-of-qr-code' into 'master'
Tweak text for unknown QR code type

See merge request briar/briar!1752
2022-12-21 12:29:48 +00:00
akwizgran
6965bc0acd Tweak text for unknown QR code type. 2022-12-21 12:19:31 +00:00
akwizgran
c6e9554026 Merge branch '2397-wrong-type-of-qr-code' into 'master'
Show appropriate error message if user scans wrong kind of QR code

Closes #2397

See merge request briar/briar!1748
2022-12-19 15:43:16 +00:00
akwizgran
ab8734e373 Show relevant message when contact QR code has unknown format. 2022-12-19 10:24:58 +00:00
akwizgran
267956b36c Restore javadoc for qrCodeTooOld flag. 2022-12-19 10:04:55 +00:00
akwizgran
ec84ddb38b Merge branch '2403-show-progress-while-connecting-to-mailbox' into 'master'
Show progress while connecting to mailbox

Closes #2403

See merge request briar/briar!1747
2022-12-14 12:20:43 +00:00
akwizgran
ba2db48d8e Center text, add margin at bottom to center layout. 2022-12-14 12:03:06 +00:00
akwizgran
186f61f771 Set width of text views to 0dp so margins are applied. 2022-12-12 16:14:34 +00:00
akwizgran
7a3ffcbae6 Remove various bits of code whose migration periods have passed. 2022-12-07 17:47:02 +00:00
akwizgran
95d8783852 Show appropriate error message if contact QR code is scanned. 2022-12-02 14:27:42 +00:00
akwizgran
b4f3604584 Show appropriate error message if mailbox QR code is scanned. 2022-12-02 13:35:00 +00:00
akwizgran
badccac90c Factor out recognition of QR code format. 2022-12-02 13:35:00 +00:00
akwizgran
1b8d1a5a8d Update test expectations. 2022-11-30 17:30:33 +00:00
akwizgran
2fe57d2597 Show progress while connecting to mailbox. 2022-11-30 17:17:08 +00:00
144 changed files with 3386 additions and 978 deletions

View File

@@ -13,8 +13,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 31 targetSdkVersion 31
versionCode 10418 versionCode 10420
versionName "1.4.18" versionName "1.4.20"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.briarproject.bramble"> package="org.briarproject.bramble">
<uses-feature <uses-feature
@@ -7,15 +8,17 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- The BLUETOOTH permission was supposed to be removed in API 31 but is still needed on some Xiaomi/Redmi/POCO devices running API 31 -->
<uses-permission <uses-permission
android:name="android.permission.BLUETOOTH" android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" /> android:maxSdkVersion="31" />
<uses-permission <uses-permission
android:name="android.permission.BLUETOOTH_ADMIN" android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" /> android:maxSdkVersion="30" />
<uses-permission <uses-permission
android:name="android.permission.BLUETOOTH_SCAN" android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" /> android:usesPermissionFlags="neverForLocation"
tools:targetApi="31" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />

View File

@@ -118,6 +118,11 @@ class AndroidNetworkManager implements NetworkManager, Service {
try { try {
NetworkInfo net = connectivityManager.getActiveNetworkInfo(); NetworkInfo net = connectivityManager.getActiveNetworkInfo();
boolean connected = net != null && net.isConnected(); boolean connected = net != null && net.isConnected();
// Research into Android's behavior to check network connectivity
// (https://code.briarproject.org/briar/public-mesh-research/-/issues/19)
// has shown that NetworkInfo#isConnected() returns true if the device
// is connected to any Wifi, independent of whether any specific IP
// address can be reached using it or any domain names can be resolved.
boolean wifi = false, ipv6Only = false; boolean wifi = false, ipv6Only = false;
if (connected) { if (connected) {
wifi = net.getType() == TYPE_WIFI; wifi = net.getType() == TYPE_WIFI;

View File

@@ -65,6 +65,9 @@ public class AndroidUtils {
public static Pair<String, String> getBluetoothAddressAndMethod(Context ctx, public static Pair<String, String> getBluetoothAddressAndMethod(Context ctx,
BluetoothAdapter adapter) { BluetoothAdapter adapter) {
// If we don't have permission to access the adapter's address, let
// the caller know we can't find it
if (!hasBtConnectPermission(ctx)) return new Pair<>("", "");
// Return the adapter's address if it's valid and not fake // Return the adapter's address if it's valid and not fake
@SuppressLint("HardwareIds") @SuppressLint("HardwareIds")
String address = adapter.getAddress(); String address = adapter.getAddress();

View File

@@ -26,7 +26,7 @@ dependencyVerification {
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047', 'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
'org.briarproject:obfs4proxy-android:0.0.14-tor1:obfs4proxy-android-0.0.14-tor1.jar:8b08068778b133484b17956d8f7a7710739c33f671a26a68156f4d34e6f28c30', 'org.briarproject:obfs4proxy-android:0.0.14-tor1:obfs4proxy-android-0.0.14-tor1.jar:8b08068778b133484b17956d8f7a7710739c33f671a26a68156f4d34e6f28c30',
'org.briarproject:snowflake-android:2.3.1:snowflake-android-2.3.1.jar:1f83c9a070f87b7074af13627709a8b5aced5460104be7166af736b1bb73c293', 'org.briarproject:snowflake-android:2.3.1:snowflake-android-2.3.1.jar:1f83c9a070f87b7074af13627709a8b5aced5460104be7166af736b1bb73c293',
'org.briarproject:tor-android:0.4.5.14:tor-android-0.4.5.14.jar:7cf1beaa6c1db51fc8fac263aba9624ef289c3db29772509efcbc59f7057330a', 'org.briarproject:tor-android:0.4.7.13:tor-android-0.4.7.13.jar:7852aab7d2298b80878c7719f34ce665725b494d673ecf2e6f9e697564638cc6',
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a', 'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb', 'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9', 'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',

View File

@@ -1,18 +1,26 @@
package org.briarproject.bramble.api.keyagreement; package org.briarproject.bramble.api.keyagreement;
public interface KeyAgreementConstants { import org.briarproject.bramble.api.mailbox.MailboxConstants;
/** public interface KeyAgreementConstants {
* The version of the BQP protocol used in beta releases. This version
* number is reserved.
*/
byte BETA_PROTOCOL_VERSION = 89;
/** /**
* The current version of the BQP protocol. * The current version of the BQP protocol.
*/ */
byte PROTOCOL_VERSION = 4; byte PROTOCOL_VERSION = 4;
/**
* The QR code format identifier, used to distinguish BQP QR codes from
* QR codes used for other purposes. See
* {@link MailboxConstants#QR_FORMAT_ID}.
*/
byte QR_FORMAT_ID = 0;
/**
* The QR code format version.
*/
byte QR_FORMAT_VERSION = PROTOCOL_VERSION;
/** /**
* The length of the BQP key commitment in bytes. * The length of the BQP key commitment in bytes.
*/ */

View File

@@ -7,5 +7,5 @@ import java.io.IOException;
@NotNullByDefault @NotNullByDefault
public interface PayloadParser { public interface PayloadParser {
Payload parse(byte[] raw) throws IOException; Payload parse(String payload) throws IOException;
} }

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.api.mailbox; package org.briarproject.bramble.api.mailbox;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConstants;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import java.util.List; import java.util.List;
@@ -19,6 +20,18 @@ public interface MailboxConstants {
*/ */
TransportId ID = new TransportId("org.briarproject.bramble.mailbox"); TransportId ID = new TransportId("org.briarproject.bramble.mailbox");
/**
* The QR code format identifier, used to distinguish mailbox QR codes
* from QR codes used for other purposes. See
* {@link KeyAgreementConstants#QR_FORMAT_ID};
*/
byte QR_FORMAT_ID = 1;
/**
* The QR code format version.
*/
byte QR_FORMAT_VERSION = 0;
/** /**
* Mailbox API versions that we support as a client. This is reported to our * Mailbox API versions that we support as a client. This is reported to our
* contacts by {@link MailboxUpdateManager}. * contacts by {@link MailboxUpdateManager}.

View File

@@ -1,17 +1,44 @@
package org.briarproject.bramble.api.mailbox; package org.briarproject.bramble.api.mailbox;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
public abstract class MailboxPairingState { public abstract class MailboxPairingState {
public static class QrCodeReceived extends MailboxPairingState { public abstract static class Pending extends MailboxPairingState {
public final long timeStarted;
private Pending(long timeStarted) {
this.timeStarted = timeStarted;
}
} }
public static class Pairing extends MailboxPairingState { public static class QrCodeReceived extends Pending {
public QrCodeReceived(long timeStarted) {
super(timeStarted);
}
}
public static class Pairing extends Pending {
public Pairing(long timeStarted) {
super(timeStarted);
}
} }
public static class Paired extends MailboxPairingState { public static class Paired extends MailboxPairingState {
} }
public static class InvalidQrCode extends MailboxPairingState { public static class InvalidQrCode extends MailboxPairingState {
public final QrCodeType qrCodeType;
public final int formatVersion;
public InvalidQrCode(QrCodeType qrCodeType, int formatVersion) {
this.qrCodeType = qrCodeType;
this.formatVersion = formatVersion;
}
} }
public static class MailboxAlreadyPaired extends MailboxPairingState { public static class MailboxAlreadyPaired extends MailboxPairingState {

View File

@@ -27,8 +27,6 @@ public interface TorConstants {
int PREF_TOR_NETWORK_AUTOMATIC = 0; int PREF_TOR_NETWORK_AUTOMATIC = 0;
int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1; int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1;
int PREF_TOR_NETWORK_WITH_BRIDGES = 2; int PREF_TOR_NETWORK_WITH_BRIDGES = 2;
// TODO: Remove when settings migration code is removed
int PREF_TOR_NETWORK_NEVER = 3;
// Default values for local settings // Default values for local settings
boolean DEFAULT_PREF_PLUGIN_ENABLE = true; boolean DEFAULT_PREF_PLUGIN_ENABLE = true;

View File

@@ -0,0 +1,16 @@
package org.briarproject.bramble.api.qrcode;
import org.briarproject.bramble.api.Pair;
import org.briarproject.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface QrCodeClassifier {
enum QrCodeType {
BQP,
MAILBOX,
UNKNOWN
}
Pair<QrCodeType, Integer> classifyQrCode(String payload);
}

View File

@@ -0,0 +1,25 @@
package org.briarproject.bramble.api.qrcode;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
import org.briarproject.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* Thrown when a QR code that has been scanned does not have the expected type.
*/
@Immutable
@NotNullByDefault
public class WrongQrCodeTypeException extends FormatException {
private final QrCodeType qrCodeType;
public WrongQrCodeTypeException(QrCodeType qrCodeType) {
this.qrCodeType = qrCodeType;
}
public QrCodeType getQrCodeType() {
return qrCodeType;
}
}

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.util;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.nullsafety.NotNullByDefault;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException; import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
@@ -17,13 +16,18 @@ import javax.annotation.Nullable;
import static java.nio.charset.CodingErrorAction.IGNORE; import static java.nio.charset.CodingErrorAction.IGNORE;
import static java.util.regex.Pattern.CASE_INSENSITIVE; import static java.util.regex.Pattern.CASE_INSENSITIVE;
@SuppressWarnings("CharsetObjectCanBeUsed")
@NotNullByDefault @NotNullByDefault
public class StringUtils { public class StringUtils {
private static final Charset UTF_8 = Charset.forName("UTF-8"); public static final Charset UTF_8 = Charset.forName("UTF-8");
private static Pattern MAC = Pattern.compile("[0-9a-f]{2}:[0-9a-f]{2}:" + public static final Charset US_ASCII = Charset.forName("US-ASCII");
"[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}", public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
CASE_INSENSITIVE);
private static final Pattern MAC =
Pattern.compile("[0-9a-f]{2}:[0-9a-f]{2}:" +
"[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}",
CASE_INSENSITIVE);
private static final char[] HEX = new char[] { private static final char[] HEX = new char[] {
'0', '1', '2', '3', '4', '5', '6', '7', '0', '1', '2', '3', '4', '5', '6', '7',
@@ -45,11 +49,7 @@ public class StringUtils {
} }
public static byte[] toUtf8(String s) { public static byte[] toUtf8(String s) {
try { return s.getBytes(UTF_8);
return s.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
} }
public static String fromUtf8(byte[] bytes) { public static String fromUtf8(byte[] bytes) {

View File

@@ -33,7 +33,7 @@ dependencies {
testImplementation "org.jmock:jmock:$jmock_version" testImplementation "org.jmock:jmock:$jmock_version"
testImplementation "org.jmock:jmock-junit4:$jmock_version" testImplementation "org.jmock:jmock-junit4:$jmock_version"
testImplementation "org.jmock:jmock-imposters:$jmock_version" testImplementation "org.jmock:jmock-imposters:$jmock_version"
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.3" testImplementation "com.squareup.okhttp3:mockwebserver:$mockwebserver_version"
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version" testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"

View File

@@ -17,6 +17,7 @@ import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.mailbox.MailboxModule; import org.briarproject.bramble.mailbox.MailboxModule;
import org.briarproject.bramble.plugin.PluginModule; import org.briarproject.bramble.plugin.PluginModule;
import org.briarproject.bramble.properties.PropertiesModule; import org.briarproject.bramble.properties.PropertiesModule;
import org.briarproject.bramble.qrcode.QrCodeModule;
import org.briarproject.bramble.record.RecordModule; import org.briarproject.bramble.record.RecordModule;
import org.briarproject.bramble.reliability.ReliabilityModule; import org.briarproject.bramble.reliability.ReliabilityModule;
import org.briarproject.bramble.rendezvous.RendezvousModule; import org.briarproject.bramble.rendezvous.RendezvousModule;
@@ -47,6 +48,7 @@ import dagger.Module;
MailboxModule.class, MailboxModule.class,
PluginModule.class, PluginModule.class,
PropertiesModule.class, PropertiesModule.class,
QrCodeModule.class,
RecordModule.class, RecordModule.class,
ReliabilityModule.class, ReliabilityModule.class,
RendezvousModule.class, RendezvousModule.class,

View File

@@ -19,7 +19,6 @@ import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -29,6 +28,7 @@ import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT; import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.StringUtils.UTF_8;
import static org.briarproject.bramble.util.StringUtils.fromHexString; import static org.briarproject.bramble.util.StringUtils.fromHexString;
import static org.briarproject.bramble.util.StringUtils.toHexString; import static org.briarproject.bramble.util.StringUtils.toHexString;
@@ -99,7 +99,7 @@ class AccountManagerImpl implements AccountManager {
} }
try { try {
BufferedReader reader = new BufferedReader(new InputStreamReader( BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream(f), Charset.forName("UTF-8"))); new FileInputStream(f), UTF_8));
String key = reader.readLine(); String key = reader.readLine();
reader.close(); reader.close();
return key; return key;
@@ -151,7 +151,7 @@ class AccountManagerImpl implements AccountManager {
@GuardedBy("stateChangeLock") @GuardedBy("stateChangeLock")
private void writeDbKeyToFile(String key, File f) throws IOException { private void writeDbKeyToFile(String key, File f) throws IOException {
FileOutputStream out = new FileOutputStream(f); FileOutputStream out = new FileOutputStream(f);
out.write(key.getBytes(Charset.forName("UTF-8"))); out.write(key.getBytes(UTF_8));
out.flush(); out.flush();
out.close(); out.close();
} }

View File

@@ -29,7 +29,6 @@ import org.briarproject.nullsafety.NotNullByDefault;
import org.whispersystems.curve25519.Curve25519; import org.whispersystems.curve25519.Curve25519;
import org.whispersystems.curve25519.Curve25519KeyPair; import org.whispersystems.curve25519.Curve25519KeyPair;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.Provider; import java.security.Provider;
@@ -51,6 +50,7 @@ import static org.briarproject.bramble.api.crypto.DecryptionResult.KEY_STRENGTHE
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.bramble.util.StringUtils.US_ASCII;
@NotNullByDefault @NotNullByDefault
class CryptoComponentImpl implements CryptoComponent { class CryptoComponentImpl implements CryptoComponent {
@@ -460,7 +460,7 @@ class CryptoComponentImpl implements CryptoComponent {
@Override @Override
public String encodeOnion(byte[] publicKey) { public String encodeOnion(byte[] publicKey) {
Digest digest = new SHA3Digest(256); Digest digest = new SHA3Digest(256);
byte[] label = ".onion checksum".getBytes(Charset.forName("US-ASCII")); byte[] label = ".onion checksum".getBytes(US_ASCII);
digest.update(label, 0, label.length); digest.update(label, 0, label.length);
digest.update(publicKey, 0, publicKey.length); digest.update(publicKey, 0, publicKey.length);
digest.update(ONION_HS_PROTOCOL_VERSION); digest.update(ONION_HS_PROTOCOL_VERSION);

View File

@@ -39,12 +39,13 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.nio.charset.Charset;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Scanner; import java.util.Scanner;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.util.StringUtils.UTF_8;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class MessageEncrypter { public class MessageEncrypter {
@@ -228,7 +229,7 @@ public class MessageEncrypter {
PublicKey publicKey = PublicKey publicKey =
encrypter.getKeyParser().parsePublicKey(keyBytes); encrypter.getKeyParser().parsePublicKey(keyBytes);
String message = readFully(System.in); String message = readFully(System.in);
byte[] plaintext = message.getBytes(Charset.forName("UTF-8")); byte[] plaintext = message.getBytes(UTF_8);
byte[] ciphertext = encrypter.encrypt(publicKey, plaintext); byte[] ciphertext = encrypter.encrypt(publicKey, plaintext);
System.out.println(AsciiArmour.wrap(ciphertext, LINE_LENGTH)); System.out.println(AsciiArmour.wrap(ciphertext, LINE_LENGTH));
} }
@@ -242,7 +243,7 @@ public class MessageEncrypter {
encrypter.getKeyParser().parsePrivateKey(keyBytes); encrypter.getKeyParser().parsePrivateKey(keyBytes);
byte[] ciphertext = AsciiArmour.unwrap(readFully(System.in)); byte[] ciphertext = AsciiArmour.unwrap(readFully(System.in));
byte[] plaintext = encrypter.decrypt(privateKey, ciphertext); byte[] plaintext = encrypter.decrypt(privateKey, ciphertext);
System.out.println(new String(plaintext, Charset.forName("UTF-8"))); System.out.println(new String(plaintext, UTF_8));
} }
private static String readFully(InputStream in) throws IOException { private static String readFully(InputStream in) throws IOException {

View File

@@ -1,7 +1,5 @@
package org.briarproject.bramble.keyagreement; package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask; import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
import org.briarproject.bramble.api.keyagreement.PayloadEncoder; import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
import org.briarproject.bramble.api.keyagreement.PayloadParser; import org.briarproject.bramble.api.keyagreement.PayloadParser;
@@ -19,13 +17,13 @@ public class KeyAgreementModule {
} }
@Provides @Provides
PayloadEncoder providePayloadEncoder(BdfWriterFactory bdfWriterFactory) { PayloadEncoder providePayloadEncoder(PayloadEncoderImpl payloadEncoder) {
return new PayloadEncoderImpl(bdfWriterFactory); return payloadEncoder;
} }
@Provides @Provides
PayloadParser providePayloadParser(BdfReaderFactory bdfReaderFactory) { PayloadParser providePayloadParser(PayloadParserImpl payloadParser) {
return new PayloadParserImpl(bdfReaderFactory); return payloadParser;
} }
@Provides @Provides

View File

@@ -13,7 +13,8 @@ import java.io.IOException;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_ID;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_VERSION;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -29,7 +30,8 @@ class PayloadEncoderImpl implements PayloadEncoder {
@Override @Override
public byte[] encode(Payload p) { public byte[] encode(Payload p) {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(PROTOCOL_VERSION); int formatIdAndVersion = (QR_FORMAT_ID << 5) | QR_FORMAT_VERSION;
out.write(formatIdAndVersion);
BdfWriter w = bdfWriterFactory.createWriter(out); BdfWriter w = bdfWriterFactory.createWriter(out);
try { try {
w.writeListStart(); // Payload start w.writeListStart(); // Payload start

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.keyagreement; package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.UnsupportedVersionException; import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.BdfReader; import org.briarproject.bramble.api.data.BdfReader;
@@ -11,6 +12,9 @@ import org.briarproject.bramble.api.keyagreement.TransportDescriptor;
import org.briarproject.bramble.api.plugin.BluetoothConstants; import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.LanTcpConstants; import org.briarproject.bramble.api.plugin.LanTcpConstants;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
import org.briarproject.bramble.api.qrcode.WrongQrCodeTypeException;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.nullsafety.NotNullByDefault;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@@ -21,34 +25,42 @@ import java.util.List;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.BETA_PROTOCOL_VERSION;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_VERSION;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class PayloadParserImpl implements PayloadParser { class PayloadParserImpl implements PayloadParser {
private final BdfReaderFactory bdfReaderFactory; private final BdfReaderFactory bdfReaderFactory;
private final QrCodeClassifier qrCodeClassifier;
@Inject @Inject
PayloadParserImpl(BdfReaderFactory bdfReaderFactory) { PayloadParserImpl(BdfReaderFactory bdfReaderFactory,
QrCodeClassifier qrCodeClassifier) {
this.bdfReaderFactory = bdfReaderFactory; this.bdfReaderFactory = bdfReaderFactory;
this.qrCodeClassifier = qrCodeClassifier;
} }
@Override @Override
public Payload parse(byte[] raw) throws IOException { public Payload parse(String payloadString) throws IOException {
ByteArrayInputStream in = new ByteArrayInputStream(raw); Pair<QrCodeType, Integer> typeAndVersion =
// First byte: the protocol version qrCodeClassifier.classifyQrCode(payloadString);
int protocolVersion = in.read(); QrCodeType qrCodeType = typeAndVersion.getFirst();
if (protocolVersion == -1) throw new FormatException(); if (qrCodeType != BQP) throw new WrongQrCodeTypeException(qrCodeType);
if (protocolVersion != PROTOCOL_VERSION) { int formatVersion = typeAndVersion.getSecond();
boolean tooOld = protocolVersion < PROTOCOL_VERSION || if (formatVersion != QR_FORMAT_VERSION) {
protocolVersion == BETA_PROTOCOL_VERSION; boolean tooOld = formatVersion < QR_FORMAT_VERSION;
throw new UnsupportedVersionException(tooOld); throw new UnsupportedVersionException(tooOld);
} }
byte[] raw = payloadString.getBytes(ISO_8859_1);
ByteArrayInputStream in = new ByteArrayInputStream(raw);
// First byte: the format identifier and version (already parsed)
if (in.read() == -1) throw new AssertionError();
// The rest of the payload is a BDF list with one or more elements // The rest of the payload is a BDF list with one or more elements
BdfReader r = bdfReaderFactory.createReader(in); BdfReader r = bdfReaderFactory.createReader(in);
BdfList payload = r.readList(); BdfList payload = r.readList();

View File

@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.mailbox.MailboxPairingTask; import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager; import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager; import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.nullsafety.NotNullByDefault;
@@ -25,6 +26,7 @@ class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
private final MailboxApi api; private final MailboxApi api;
private final MailboxSettingsManager mailboxSettingsManager; private final MailboxSettingsManager mailboxSettingsManager;
private final MailboxUpdateManager mailboxUpdateManager; private final MailboxUpdateManager mailboxUpdateManager;
private final QrCodeClassifier qrCodeClassifier;
@Inject @Inject
MailboxPairingTaskFactoryImpl( MailboxPairingTaskFactoryImpl(
@@ -34,7 +36,8 @@ class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
Clock clock, Clock clock,
MailboxApi api, MailboxApi api,
MailboxSettingsManager mailboxSettingsManager, MailboxSettingsManager mailboxSettingsManager,
MailboxUpdateManager mailboxUpdateManager) { MailboxUpdateManager mailboxUpdateManager,
QrCodeClassifier qrCodeClassifier) {
this.eventExecutor = eventExecutor; this.eventExecutor = eventExecutor;
this.db = db; this.db = db;
this.crypto = crypto; this.crypto = crypto;
@@ -42,12 +45,13 @@ class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
this.api = api; this.api = api;
this.mailboxSettingsManager = mailboxSettingsManager; this.mailboxSettingsManager = mailboxSettingsManager;
this.mailboxUpdateManager = mailboxUpdateManager; this.mailboxUpdateManager = mailboxUpdateManager;
this.qrCodeClassifier = qrCodeClassifier;
} }
@Override @Override
public MailboxPairingTask createPairingTask(String qrCodePayload) { public MailboxPairingTask createPairingTask(String qrCodePayload) {
return new MailboxPairingTaskImpl(qrCodePayload, eventExecutor, db, return new MailboxPairingTaskImpl(qrCodePayload, eventExecutor, db,
crypto, clock, api, mailboxSettingsManager, crypto, clock, api, mailboxSettingsManager,
mailboxUpdateManager); mailboxUpdateManager, qrCodeClassifier);
} }
} }

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.Consumer; import org.briarproject.bramble.api.Consumer;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
@@ -9,18 +10,26 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.EventExecutor; import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.mailbox.MailboxAuthToken; import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
import org.briarproject.bramble.api.mailbox.MailboxPairingState; import org.briarproject.bramble.api.mailbox.MailboxPairingState;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.ConnectionError;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.InvalidQrCode;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.MailboxAlreadyPaired;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Paired;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Pairing;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.QrCodeReceived;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.UnexpectedError;
import org.briarproject.bramble.api.mailbox.MailboxPairingTask; import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
import org.briarproject.bramble.api.mailbox.MailboxProperties; import org.briarproject.bramble.api.mailbox.MailboxProperties;
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager; import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
import org.briarproject.bramble.api.mailbox.MailboxUpdate; import org.briarproject.bramble.api.mailbox.MailboxUpdate;
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager; import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.mailbox.MailboxApi.ApiException; import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
import org.briarproject.bramble.mailbox.MailboxApi.MailboxAlreadyPairedException; import org.briarproject.bramble.mailbox.MailboxApi.MailboxAlreadyPairedException;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.nullsafety.NotNullByDefault;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -32,7 +41,10 @@ import javax.annotation.concurrent.ThreadSafe;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
@@ -40,9 +52,6 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
private final static Logger LOG = private final static Logger LOG =
getLogger(MailboxPairingTaskImpl.class.getName()); getLogger(MailboxPairingTaskImpl.class.getName());
@SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
private static final int VERSION_REQUIRED = 32;
private final String payload; private final String payload;
private final Executor eventExecutor; private final Executor eventExecutor;
@@ -52,6 +61,8 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
private final MailboxApi api; private final MailboxApi api;
private final MailboxSettingsManager mailboxSettingsManager; private final MailboxSettingsManager mailboxSettingsManager;
private final MailboxUpdateManager mailboxUpdateManager; private final MailboxUpdateManager mailboxUpdateManager;
private final QrCodeClassifier qrCodeClassifier;
private final long timeStarted;
private final Object lock = new Object(); private final Object lock = new Object();
@GuardedBy("lock") @GuardedBy("lock")
@@ -68,7 +79,8 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
Clock clock, Clock clock,
MailboxApi api, MailboxApi api,
MailboxSettingsManager mailboxSettingsManager, MailboxSettingsManager mailboxSettingsManager,
MailboxUpdateManager mailboxUpdateManager) { MailboxUpdateManager mailboxUpdateManager,
QrCodeClassifier qrCodeClassifier) {
this.payload = payload; this.payload = payload;
this.eventExecutor = eventExecutor; this.eventExecutor = eventExecutor;
this.db = db; this.db = db;
@@ -77,7 +89,9 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
this.api = api; this.api = api;
this.mailboxSettingsManager = mailboxSettingsManager; this.mailboxSettingsManager = mailboxSettingsManager;
this.mailboxUpdateManager = mailboxUpdateManager; this.mailboxUpdateManager = mailboxUpdateManager;
state = new MailboxPairingState.QrCodeReceived(); this.qrCodeClassifier = qrCodeClassifier;
timeStarted = clock.currentTimeMillis();
state = new QrCodeReceived(timeStarted);
} }
@Override @Override
@@ -99,22 +113,30 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
@Override @Override
public void run() { public void run() {
Pair<QrCodeType, Integer> typeAndVersion =
qrCodeClassifier.classifyQrCode(payload);
QrCodeType qrCodeType = typeAndVersion.getFirst();
int formatVersion = typeAndVersion.getSecond();
if (qrCodeType != MAILBOX || formatVersion != QR_FORMAT_VERSION) {
setState(new InvalidQrCode(qrCodeType, formatVersion));
return;
}
try { try {
pairMailbox(); pairMailbox();
} catch (FormatException e) { } catch (FormatException e) {
onMailboxError(e, new MailboxPairingState.InvalidQrCode()); onMailboxError(e, new InvalidQrCode(qrCodeType, formatVersion));
} catch (MailboxAlreadyPairedException e) { } catch (MailboxAlreadyPairedException e) {
onMailboxError(e, new MailboxPairingState.MailboxAlreadyPaired()); onMailboxError(e, new MailboxAlreadyPaired());
} catch (IOException e) { } catch (IOException e) {
onMailboxError(e, new MailboxPairingState.ConnectionError()); onMailboxError(e, new ConnectionError());
} catch (ApiException | DbException e) { } catch (ApiException | DbException e) {
onMailboxError(e, new MailboxPairingState.UnexpectedError()); onMailboxError(e, new UnexpectedError());
} }
} }
private void pairMailbox() throws IOException, ApiException, DbException { private void pairMailbox() throws IOException, ApiException, DbException {
MailboxProperties mailboxProperties = decodeQrCodePayload(payload); MailboxProperties mailboxProperties = decodeQrCodePayload(payload);
setState(new MailboxPairingState.Pairing()); setState(new Pairing(timeStarted));
MailboxProperties ownerProperties = api.setup(mailboxProperties); MailboxProperties ownerProperties = api.setup(mailboxProperties);
long time = clock.currentTimeMillis(); long time = clock.currentTimeMillis();
db.transaction(false, txn -> { db.transaction(false, txn -> {
@@ -133,7 +155,7 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
} }
} }
}); });
setState(new MailboxPairingState.Paired()); setState(new Paired());
} }
private void onMailboxError(Exception e, MailboxPairingState state) { private void onMailboxError(Exception e, MailboxPairingState state) {
@@ -167,14 +189,6 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
} }
throw new FormatException(); throw new FormatException();
} }
int version = bytes[0] & 0xFF;
if (version != VERSION_REQUIRED) {
if (LOG.isLoggable(WARNING)) {
LOG.warning("QR code has not version " + VERSION_REQUIRED +
": " + version);
}
throw new FormatException();
}
LOG.info("QR code is valid"); LOG.info("QR code is valid");
byte[] onionPubKey = Arrays.copyOfRange(bytes, 1, 33); byte[] onionPubKey = Arrays.copyOfRange(bytes, 1, 33);
String onion = crypto.encodeOnion(onionPubKey); String onion = crypto.encodeOnion(onionPubKey);

View File

@@ -46,7 +46,6 @@ import java.io.OutputStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -100,6 +99,7 @@ import static org.briarproject.bramble.util.IoUtils.copyAndClose;
import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.IoUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion; import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
import static org.briarproject.bramble.util.StringUtils.UTF_8;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@@ -140,7 +140,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private final long maxLatency; private final long maxLatency;
private final int maxIdleTime; private final int maxIdleTime;
private final int socketTimeout; private final int socketTimeout;
private final File torDirectory, geoIpFile, configFile; private final File torDirectory;
private final File configFile;
private final int torSocksPort; private final int torSocksPort;
private final int torControlPort; private final int torControlPort;
private final File doneFile, cookieFile; private final File doneFile, cookieFile;
@@ -195,7 +196,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
this.torDirectory = torDirectory; this.torDirectory = torDirectory;
this.torSocksPort = torSocksPort; this.torSocksPort = torSocksPort;
this.torControlPort = torControlPort; this.torControlPort = torControlPort;
geoIpFile = new File(torDirectory, "geoip");
configFile = new File(torDirectory, "torrc"); configFile = new File(torDirectory, "torrc");
doneFile = new File(torDirectory, "done"); doneFile = new File(torDirectory, "done");
cookieFile = new File(torDirectory, ".tor/control_auth_cookie"); cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
@@ -332,12 +332,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
// The done file may already exist from a previous installation // The done file may already exist from a previous installation
//noinspection ResultOfMethodCallIgnored //noinspection ResultOfMethodCallIgnored
doneFile.delete(); doneFile.delete();
// The GeoIP file may exist from a previous installation - we can
// save some space by deleting it.
// TODO: Remove after a reasonable migration period
// (added 2022-03-29)
//noinspection ResultOfMethodCallIgnored
geoIpFile.delete();
installTorExecutable(); installTorExecutable();
installObfs4Executable(); installObfs4Executable();
installSnowflakeExecutable(); installSnowflakeExecutable();
@@ -419,9 +413,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
append(strb, "ClientTransportPlugin meek_lite exec", obfs4Path); append(strb, "ClientTransportPlugin meek_lite exec", obfs4Path);
String snowflakePath = getSnowflakeExecutableFile().getAbsolutePath(); String snowflakePath = getSnowflakeExecutableFile().getAbsolutePath();
append(strb, "ClientTransportPlugin snowflake exec", snowflakePath); append(strb, "ClientTransportPlugin snowflake exec", snowflakePath);
//noinspection CharsetObjectCanBeUsed return new ByteArrayInputStream(strb.toString().getBytes(UTF_8));
return new ByteArrayInputStream(
strb.toString().getBytes(Charset.forName("UTF-8")));
} }
private void listFiles(File f) { private void listFiles(File f) {

View File

@@ -7,7 +7,7 @@ import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Base64;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import java.nio.charset.Charset; import static org.briarproject.bramble.util.StringUtils.US_ASCII;
class TorRendezvousCryptoImpl implements TorRendezvousCrypto { class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
@@ -31,6 +31,6 @@ class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
EdDSAPrivateKeySpec spec = new EdDSAPrivateKeySpec(seed, CURVE_SPEC); EdDSAPrivateKeySpec spec = new EdDSAPrivateKeySpec(seed, CURVE_SPEC);
byte[] hash = spec.getH(); byte[] hash = spec.getH();
byte[] base64 = Base64.encode(hash); byte[] base64 = Base64.encode(hash);
return "ED25519-V3:" + new String(base64, Charset.forName("US-ASCII")); return "ED25519-V3:" + new String(base64, US_ASCII);
} }
} }

View File

@@ -0,0 +1,42 @@
package org.briarproject.bramble.qrcode;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConstants;
import org.briarproject.bramble.api.mailbox.MailboxConstants;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
import org.briarproject.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.UNKNOWN;
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
@Immutable
@NotNullByDefault
class QrCodeClassifierImpl implements QrCodeClassifier {
@Inject
QrCodeClassifierImpl() {
}
@Override
public Pair<QrCodeType, Integer> classifyQrCode(String payload) {
byte[] bytes = payload.getBytes(ISO_8859_1);
if (bytes.length == 0) return new Pair<>(UNKNOWN, 0);
// If this is a Bramble QR code then the first byte encodes the
// format ID (3 bits) and version (5 bits)
int formatIdAndVersion = bytes[0] & 0xFF;
int formatId = formatIdAndVersion >> 5;
int formatVersion = formatIdAndVersion & 0x1F;
if (formatId == KeyAgreementConstants.QR_FORMAT_ID) {
return new Pair<>(BQP, formatVersion);
}
if (formatId == MailboxConstants.QR_FORMAT_ID) {
return new Pair<>(MAILBOX, formatVersion);
}
return new Pair<>(UNKNOWN, 0);
}
}

View File

@@ -0,0 +1,16 @@
package org.briarproject.bramble.qrcode;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
import dagger.Module;
import dagger.Provides;
@Module
public class QrCodeModule {
@Provides
QrCodeClassifier provideQrCodeClassifier(
QrCodeClassifierImpl qrCodeClassifier) {
return qrCodeClassifier;
}
}

View File

@@ -21,11 +21,11 @@ n Bridge obfs4 185.103.252.72:443 75F15E9339FF572F88F5588D429FEA379744BC53 cert=
n Bridge obfs4 76.255.201.112:8888 96CF36C2ECCFB7376AB6BE905BECD2C2AE8AEFCD cert=+q0pjaiM0JMqHL/BKqCRD+pjflaw/S406eUDF7CnFgamvQW3l2HVLJhQ6uX9P8zff0PLGg iat-mode=0 n Bridge obfs4 76.255.201.112:8888 96CF36C2ECCFB7376AB6BE905BECD2C2AE8AEFCD cert=+q0pjaiM0JMqHL/BKqCRD+pjflaw/S406eUDF7CnFgamvQW3l2HVLJhQ6uX9P8zff0PLGg iat-mode=0
n Bridge obfs4 94.142.246.132:8088 135C158527AA9FE9A2F26EC515EB6999D813D347 cert=wTUz0/5FhAZRkitil5MprGbSF3JzjxjxI1kAmxAdSeDy98NgcLr11f/qUXWDC76Y97RiSg iat-mode=0 n Bridge obfs4 94.142.246.132:8088 135C158527AA9FE9A2F26EC515EB6999D813D347 cert=wTUz0/5FhAZRkitil5MprGbSF3JzjxjxI1kAmxAdSeDy98NgcLr11f/qUXWDC76Y97RiSg iat-mode=0
n Bridge obfs4 20.102.79.78:22022 B5705F7E616DAB0F477E3E1ADC23E40413F683FE cert=1Cc/hwPtPjzFKGHVOP0j/qmBgnvquRx8+im35/u5TIYjDQ3FlMfA5VvTrQ/hbX8BZZooLQ iat-mode=0 n Bridge obfs4 20.102.79.78:22022 B5705F7E616DAB0F477E3E1ADC23E40413F683FE cert=1Cc/hwPtPjzFKGHVOP0j/qmBgnvquRx8+im35/u5TIYjDQ3FlMfA5VvTrQ/hbX8BZZooLQ iat-mode=0
n Bridge obfs4 207.154.242.137:80 8E67A1B2A342652EE27376BD61BECF5806700E7F cert=qUrR9fan3XPNGNOwn9WGlXLJNZZx0grXH4AZXR+yoBbtbbj5Ak1n4a7TtjYgXcWcs/gHXw iat-mode=0
n Bridge obfs4 152.70.180.20:1993 3327C43587E66AD5F874C4234A1D72C938AD7318 cert=s7xLRUO2psaX7TMUP2YhXdxItR4U6K7D+E3gQaS/+yWUppevtazIibq4dN1g5Reu6dD2QQ iat-mode=0 n Bridge obfs4 152.70.180.20:1993 3327C43587E66AD5F874C4234A1D72C938AD7318 cert=s7xLRUO2psaX7TMUP2YhXdxItR4U6K7D+E3gQaS/+yWUppevtazIibq4dN1g5Reu6dD2QQ iat-mode=0
n Bridge obfs4 144.202.12.254:10002 4E220F45CD404C8A3082A36326A5ED19BB8D4404 cert=iLz5YYWO4pUw7U7MRNOSvE0qO+IVeE4kVfFVWPO3coH3FmZtrkvlaTklfXxHZaCcXWBgaA iat-mode=0 n Bridge obfs4 144.202.12.254:10002 4E220F45CD404C8A3082A36326A5ED19BB8D4404 cert=iLz5YYWO4pUw7U7MRNOSvE0qO+IVeE4kVfFVWPO3coH3FmZtrkvlaTklfXxHZaCcXWBgaA iat-mode=0
n Bridge obfs4 15.235.47.204:42058 869133925B3CD07683BDF01805C36448D090CE88 cert=PFwh4mzZlSTUdcEskpe20t998n5jbr81s+XoX7gmazqzUGHNhkendK5K1j2gOxesz9AkBw iat-mode=0 n Bridge obfs4 109.14.168.159:5082 BFE1416DEFFE969581F016A4A319A87FFB26BA91 cert=n3X1CDdKBPXPIzfKh83p3ydfMzb0AD9gKC+/gIpHb7+xjjAnYO9x3LT+T/MvOIfAXxYySg iat-mode=0
n Bridge obfs4 51.75.93.136:45532 8402B84833527BC249B21AC885134197E624FB5A cert=LwXEf/Dgo0tKdMJByXdlvWiJqyyPw4T284Cg5qygDuIJJNFuz3ED9UhGil6H4Of3gM7wSg iat-mode=0 n Bridge obfs4 45.150.172.16:80 82849E69CBB25EA7F479155F7DCD89D85717FF47 cert=+Krdu1jmVQOxWkMj0mqJHgwbQV49eyD6mZDS+mRExssWNHosa60g4P5Gp81sBJKzN8NrSw iat-mode=0
n Bridge obfs4 185.177.207.132:8443 4FB781F7A9DD39DA53A7996907817FC479874D19 cert=UL2gCAXWW5kEWY4TQ0lNeu6OAmzh40bXYVhMnTWVG8USnyy/zEKGSIPgmwTDMumWr9c1Pg iat-mode=0
v Bridge 92.243.15.235:9001 477EAD3C04036B48235F1F27FC91420A286A4B7F v Bridge 92.243.15.235:9001 477EAD3C04036B48235F1F27FC91420A286A4B7F
v Bridge 213.108.108.145:17674 A39C0FE47963B6E8CFE9815549864DE544935A31 v Bridge 213.108.108.145:17674 A39C0FE47963B6E8CFE9815549864DE544935A31
v Bridge 213.196.191.96:9060 05E222E2A8C234234FE0CEB58B08A93B8FC360DB v Bridge 213.196.191.96:9060 05E222E2A8C234234FE0CEB58B08A93B8FC360DB

View File

@@ -20,7 +20,6 @@ import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.Charset;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -34,6 +33,7 @@ import static org.briarproject.bramble.test.TestUtils.getIdentity;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory; import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
import static org.briarproject.bramble.util.StringUtils.UTF_8;
import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.bramble.util.StringUtils.toHexString; import static org.briarproject.bramble.util.StringUtils.toHexString;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
@@ -342,7 +342,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
private void storeDatabaseKey(File f, String hex) throws IOException { private void storeDatabaseKey(File f, String hex) throws IOException {
f.getParentFile().mkdirs(); f.getParentFile().mkdirs();
FileOutputStream out = new FileOutputStream(f); FileOutputStream out = new FileOutputStream(f);
out.write(hex.getBytes(Charset.forName("UTF-8"))); out.write(hex.getBytes(UTF_8));
out.flush(); out.flush();
out.close(); out.close();
} }
@@ -350,7 +350,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
@Nullable @Nullable
private String loadDatabaseKey(File f) throws IOException { private String loadDatabaseKey(File f) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader( BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream(f), Charset.forName("UTF-8"))); new FileInputStream(f), UTF_8));
String hex = reader.readLine(); String hex = reader.readLine();
reader.close(); reader.close();
return hex; return hex;

View File

@@ -2,21 +2,27 @@ package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.Bytes; import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.UnsupportedVersionException; import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.BdfReader; import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory; import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.keyagreement.Payload; import org.briarproject.bramble.api.keyagreement.Payload;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
import org.briarproject.bramble.api.qrcode.WrongQrCodeTypeException;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.junit.Test; import org.junit.Test;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.BETA_PROTOCOL_VERSION;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_VERSION;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -26,32 +32,29 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
private final BdfReaderFactory bdfReaderFactory = private final BdfReaderFactory bdfReaderFactory =
context.mock(BdfReaderFactory.class); context.mock(BdfReaderFactory.class);
private final QrCodeClassifier qrCodeClassifier =
context.mock(QrCodeClassifier.class);
private final BdfReader bdfReader = context.mock(BdfReader.class); private final BdfReader bdfReader = context.mock(BdfReader.class);
private final PayloadParserImpl payloadParser = private final String payload = getRandomString(123);
new PayloadParserImpl(bdfReaderFactory);
@Test(expected = FormatException.class) private final PayloadParserImpl payloadParser =
public void testThrowsFormatExceptionIfPayloadIsEmpty() throws Exception { new PayloadParserImpl(bdfReaderFactory, qrCodeClassifier);
payloadParser.parse(new byte[0]);
@Test(expected = WrongQrCodeTypeException.class)
public void testThrowsExceptionForWrongQrCodeType() throws Exception {
expectClassifyQrCode(payload, MAILBOX, QR_FORMAT_VERSION);
payloadParser.parse(payload);
} }
@Test @Test
public void testThrowsUnsupportedVersionExceptionForOldVersion() public void testThrowsUnsupportedVersionExceptionForOldVersion()
throws Exception { throws Exception {
try { expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION - 1);
payloadParser.parse(new byte[] {PROTOCOL_VERSION - 1});
fail();
} catch (UnsupportedVersionException e) {
assertTrue(e.isTooOld());
}
}
@Test
public void testThrowsUnsupportedVersionExceptionForBetaVersion()
throws Exception {
try { try {
payloadParser.parse(new byte[] {BETA_PROTOCOL_VERSION}); payloadParser.parse(payload);
fail(); fail();
} catch (UnsupportedVersionException e) { } catch (UnsupportedVersionException e) {
assertTrue(e.isTooOld()); assertTrue(e.isTooOld());
@@ -61,8 +64,10 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
@Test @Test
public void testThrowsUnsupportedVersionExceptionForNewVersion() public void testThrowsUnsupportedVersionExceptionForNewVersion()
throws Exception { throws Exception {
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION + 1);
try { try {
payloadParser.parse(new byte[] {PROTOCOL_VERSION + 1}); payloadParser.parse(payload);
fail(); fail();
} catch (UnsupportedVersionException e) { } catch (UnsupportedVersionException e) {
assertFalse(e.isTooOld()); assertFalse(e.isTooOld());
@@ -71,6 +76,8 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testThrowsFormatExceptionForEmptyList() throws Exception { public void testThrowsFormatExceptionForEmptyList() throws Exception {
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(bdfReaderFactory).createReader( oneOf(bdfReaderFactory).createReader(
with(any(ByteArrayInputStream.class))); with(any(ByteArrayInputStream.class)));
@@ -79,7 +86,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
will(returnValue(new BdfList())); will(returnValue(new BdfList()));
}}); }});
payloadParser.parse(new byte[] {PROTOCOL_VERSION}); payloadParser.parse(payload);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
@@ -87,6 +94,8 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
byte[] commitment = getRandomBytes(COMMIT_LENGTH); byte[] commitment = getRandomBytes(COMMIT_LENGTH);
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(bdfReaderFactory).createReader( oneOf(bdfReaderFactory).createReader(
with(any(ByteArrayInputStream.class))); with(any(ByteArrayInputStream.class)));
@@ -97,7 +106,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
will(returnValue(false)); will(returnValue(false));
}}); }});
payloadParser.parse(new byte[] {PROTOCOL_VERSION}); payloadParser.parse(payload);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
@@ -105,6 +114,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
byte[] commitment = getRandomBytes(COMMIT_LENGTH - 1); byte[] commitment = getRandomBytes(COMMIT_LENGTH - 1);
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(bdfReaderFactory).createReader( oneOf(bdfReaderFactory).createReader(
with(any(ByteArrayInputStream.class))); with(any(ByteArrayInputStream.class)));
@@ -115,7 +125,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
will(returnValue(true)); will(returnValue(true));
}}); }});
payloadParser.parse(new byte[] {PROTOCOL_VERSION}); payloadParser.parse(payload);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
@@ -123,6 +133,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
byte[] commitment = getRandomBytes(COMMIT_LENGTH + 1); byte[] commitment = getRandomBytes(COMMIT_LENGTH + 1);
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(bdfReaderFactory).createReader( oneOf(bdfReaderFactory).createReader(
with(any(ByteArrayInputStream.class))); with(any(ByteArrayInputStream.class)));
@@ -133,12 +144,14 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
will(returnValue(true)); will(returnValue(true));
}}); }});
payloadParser.parse(new byte[] {PROTOCOL_VERSION}); payloadParser.parse(payload);
} }
@Test @Test
public void testAcceptsPayloadWithNoDescriptors() throws Exception { public void testAcceptsPayloadWithNoDescriptors() throws Exception {
byte[] commitment = getRandomBytes(COMMIT_LENGTH); byte[] commitment = getRandomBytes(COMMIT_LENGTH);
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(bdfReaderFactory).createReader( oneOf(bdfReaderFactory).createReader(
with(any(ByteArrayInputStream.class))); with(any(ByteArrayInputStream.class)));
@@ -149,8 +162,16 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
will(returnValue(true)); will(returnValue(true));
}}); }});
Payload p = payloadParser.parse(new byte[] {PROTOCOL_VERSION}); Payload p = payloadParser.parse(payload);
assertArrayEquals(commitment, p.getCommitment()); assertArrayEquals(commitment, p.getCommitment());
assertTrue(p.getTransportDescriptors().isEmpty()); assertTrue(p.getTransportDescriptors().isEmpty());
} }
private void expectClassifyQrCode(String payload, QrCodeType qrCodeType,
int formatVersion) {
context.checking(new Expectations() {{
oneOf(qrCodeClassifier).classifyQrCode(payload);
will(returnValue(new Pair<>(qrCodeType, formatVersion)));
}});
}
} }

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.mailbox; package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
@@ -7,13 +8,24 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.mailbox.MailboxAuthToken; import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
import org.briarproject.bramble.api.mailbox.MailboxPairingState; import org.briarproject.bramble.api.mailbox.MailboxPairingState;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.ConnectionError;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.InvalidQrCode;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.MailboxAlreadyPaired;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Paired;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Pairing;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.QrCodeReceived;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.UnexpectedError;
import org.briarproject.bramble.api.mailbox.MailboxPairingTask; import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
import org.briarproject.bramble.api.mailbox.MailboxProperties; import org.briarproject.bramble.api.mailbox.MailboxProperties;
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager; import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
import org.briarproject.bramble.api.mailbox.MailboxUpdate; import org.briarproject.bramble.api.mailbox.MailboxUpdate;
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager; import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
import org.briarproject.bramble.api.mailbox.MailboxVersion; import org.briarproject.bramble.api.mailbox.MailboxVersion;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
import org.briarproject.bramble.mailbox.MailboxApi.MailboxAlreadyPairedException;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations; import org.briarproject.bramble.test.DbExpectations;
import org.briarproject.bramble.test.ImmediateExecutor; import org.briarproject.bramble.test.ImmediateExecutor;
@@ -27,6 +39,9 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
import static org.briarproject.bramble.mailbox.MailboxTestUtils.getQrCodePayload; import static org.briarproject.bramble.mailbox.MailboxTestUtils.getQrCodePayload;
import static org.briarproject.bramble.test.TestUtils.getContact; import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
@@ -48,9 +63,8 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
context.mock(MailboxSettingsManager.class); context.mock(MailboxSettingsManager.class);
private final MailboxUpdateManager mailboxUpdateManager = private final MailboxUpdateManager mailboxUpdateManager =
context.mock(MailboxUpdateManager.class); context.mock(MailboxUpdateManager.class);
private final MailboxPairingTaskFactory factory = private final QrCodeClassifier qrCodeClassifier =
new MailboxPairingTaskFactoryImpl(executor, db, crypto, clock, api, context.mock(QrCodeClassifier.class);
mailboxSettingsManager, mailboxUpdateManager);
private final String onion = getRandomString(56); private final String onion = getRandomString(56);
private final byte[] onionBytes = getRandomBytes(32); private final byte[] onionBytes = getRandomBytes(32);
@@ -68,32 +82,50 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
@Test @Test
public void testInitialQrCodeReceivedState() { public void testInitialQrCodeReceivedState() {
MailboxPairingTask task = MailboxPairingTask task = createPairingTask(getRandomString(42));
factory.createPairingTask(getRandomString(42));
task.addObserver(state -> task.addObserver(state ->
assertTrue(state instanceof MailboxPairingState.QrCodeReceived) assertTrue(state instanceof QrCodeReceived));
);
} }
@Test @Test
public void testInvalidQrCode() { public void testInvalidQrCodeType() {
MailboxPairingTask task1 = String payload = getRandomString(65);
factory.createPairingTask(getRandomString(42)); MailboxPairingTask task = createPairingTask(payload);
task1.run();
task1.addObserver(state ->
assertTrue(state instanceof MailboxPairingState.InvalidQrCode)
);
String goodLength = "00" + getRandomString(63); expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
MailboxPairingTask task2 = factory.createPairingTask(goodLength);
task2.run(); task.run();
task2.addObserver(state -> task.addObserver(state ->
assertTrue(state instanceof MailboxPairingState.InvalidQrCode) assertTrue(state instanceof InvalidQrCode));
); }
@Test
public void testInvalidQrCodeVersion() {
String payload = getRandomString(65);
MailboxPairingTask task = createPairingTask(payload);
expectClassifyQrCode(payload, MAILBOX, QR_FORMAT_VERSION + 1);
task.run();
task.addObserver(state ->
assertTrue(state instanceof InvalidQrCode));
}
@Test
public void testInvalidQrCodeLength() {
String payload = getRandomString(42);
MailboxPairingTask task = createPairingTask(payload);
expectClassifyQrCode(payload, MAILBOX, QR_FORMAT_VERSION);
task.run();
task.addObserver(state ->
assertTrue(state instanceof InvalidQrCode));
} }
@Test @Test
public void testSuccessfulPairing() throws Exception { public void testSuccessfulPairing() throws Exception {
expectClassifyQrCode(validPayload, MAILBOX, QR_FORMAT_VERSION);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).encodeOnion(onionBytes); oneOf(crypto).encodeOnion(onionBytes);
will(returnValue(onion)); will(returnValue(onion));
@@ -121,17 +153,14 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
}}); }});
AtomicInteger i = new AtomicInteger(0); AtomicInteger i = new AtomicInteger(0);
MailboxPairingTask task = factory.createPairingTask(validPayload); MailboxPairingTask task = createPairingTask(validPayload);
task.addObserver(state -> { task.addObserver(state -> {
if (i.get() == 0) { if (i.get() == 0) {
assertEquals(MailboxPairingState.QrCodeReceived.class, assertEquals(QrCodeReceived.class, state.getClass());
state.getClass());
} else if (i.get() == 1) { } else if (i.get() == 1) {
assertEquals(MailboxPairingState.Pairing.class, assertEquals(Pairing.class, state.getClass());
state.getClass());
} else if (i.get() == 2) { } else if (i.get() == 2) {
assertEquals(MailboxPairingState.Paired.class, assertEquals(Paired.class, state.getClass());
state.getClass());
} else fail("Unexpected change of state " + state.getClass()); } else fail("Unexpected change of state " + state.getClass());
i.getAndIncrement(); i.getAndIncrement();
}); });
@@ -140,24 +169,23 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
@Test @Test
public void testAlreadyPaired() throws Exception { public void testAlreadyPaired() throws Exception {
testApiException(new MailboxApi.MailboxAlreadyPairedException(), testApiException(new MailboxAlreadyPairedException(),
MailboxPairingState.MailboxAlreadyPaired.class); MailboxAlreadyPaired.class);
} }
@Test @Test
public void testMailboxApiException() throws Exception { public void testMailboxApiException() throws Exception {
testApiException(new MailboxApi.ApiException(), testApiException(new ApiException(), UnexpectedError.class);
MailboxPairingState.UnexpectedError.class);
} }
@Test @Test
public void testApiIOException() throws Exception { public void testApiIOException() throws Exception {
testApiException(new IOException(), testApiException(new IOException(), ConnectionError.class);
MailboxPairingState.ConnectionError.class);
} }
private void testApiException(Exception e, private void testApiException(Exception e,
Class<? extends MailboxPairingState> s) throws Exception { Class<? extends MailboxPairingState> s) throws Exception {
expectClassifyQrCode(validPayload, MAILBOX, QR_FORMAT_VERSION);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).encodeOnion(onionBytes); oneOf(crypto).encodeOnion(onionBytes);
will(returnValue(onion)); will(returnValue(onion));
@@ -165,13 +193,14 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
will(throwException(e)); will(throwException(e));
}}); }});
MailboxPairingTask task = factory.createPairingTask(validPayload); MailboxPairingTask task = createPairingTask(validPayload);
task.run(); task.run();
task.addObserver(state -> assertEquals(state.getClass(), s)); task.addObserver(state -> assertEquals(state.getClass(), s));
} }
@Test @Test
public void testDbException() throws Exception { public void testDbException() throws Exception {
expectClassifyQrCode(validPayload, MAILBOX, QR_FORMAT_VERSION);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(crypto).encodeOnion(onionBytes); oneOf(crypto).encodeOnion(onionBytes);
will(returnValue(onion)); will(returnValue(onion));
@@ -188,10 +217,10 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
will(throwException(new DbException())); will(throwException(new DbException()));
}}); }});
MailboxPairingTask task = factory.createPairingTask(validPayload); MailboxPairingTask task = createPairingTask(validPayload);
task.run(); task.run();
task.addObserver(state -> assertEquals(state.getClass(), task.addObserver(state ->
MailboxPairingState.UnexpectedError.class)); assertEquals(state.getClass(), UnexpectedError.class));
} }
private PredicateMatcher<MailboxProperties> matches(MailboxProperties p2) { private PredicateMatcher<MailboxProperties> matches(MailboxProperties p2) {
@@ -202,4 +231,22 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
p1.getServerSupports().equals(p2.getServerSupports())); p1.getServerSupports().equals(p2.getServerSupports()));
} }
private MailboxPairingTask createPairingTask(String qrCodePayload) {
context.checking(new Expectations() {{
oneOf(clock).currentTimeMillis();
will(returnValue(time));
}});
return new MailboxPairingTaskImpl(qrCodePayload, executor, db,
crypto, clock, api, mailboxSettingsManager,
mailboxUpdateManager, qrCodeClassifier);
}
private void expectClassifyQrCode(String payload, QrCodeType qrCodeType,
int formatVersion) {
context.checking(new Expectations() {{
oneOf(qrCodeClassifier).classifyQrCode(payload);
will(returnValue(new Pair<>(qrCodeType, formatVersion)));
}});
}
} }

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.mailbox;
import org.briarproject.bramble.api.WeakSingletonProvider; import org.briarproject.bramble.api.WeakSingletonProvider;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.net.SocketFactory; import javax.net.SocketFactory;
@@ -11,20 +10,24 @@ import javax.net.SocketFactory;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_ID;
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
class MailboxTestUtils { class MailboxTestUtils {
static String getQrCodePayload(byte[] onionBytes, byte[] setupToken) { static String getQrCodePayload(byte[] onionBytes, byte[] setupToken) {
int formatIdAndVersion = (QR_FORMAT_ID << 5) | QR_FORMAT_VERSION;
byte[] payloadBytes = ByteBuffer.allocate(65) byte[] payloadBytes = ByteBuffer.allocate(65)
.put((byte) 32) // 1 .put((byte) formatIdAndVersion) // 1
.put(onionBytes) // 32 .put(onionBytes) // 32
.put(setupToken) // 32 .put(setupToken) // 32
.array(); .array();
//noinspection CharsetObjectCanBeUsed return new String(payloadBytes, ISO_8859_1);
return new String(payloadBytes, Charset.forName("ISO-8859-1"));
} }
// Used by mailbox integration tests
static String getQrCodePayload(byte[] setupToken) { static String getQrCodePayload(byte[] setupToken) {
return getQrCodePayload(getRandomId(), setupToken); return getQrCodePayload(getRandomId(), setupToken);
} }

View File

@@ -0,0 +1,70 @@
package org.briarproject.bramble.qrcode;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConstants;
import org.briarproject.bramble.api.mailbox.MailboxConstants;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.UNKNOWN;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
import static org.junit.Assert.assertEquals;
public class QrCodeClassifierImplTest extends BrambleTestCase {
private final QrCodeClassifier classifier = new QrCodeClassifierImpl();
@Test
public void testClassifiesEmptyStringAsUnknown() {
Pair<QrCodeType, Integer> result = classifier.classifyQrCode("");
assertEquals(UNKNOWN, result.getFirst());
assertEquals(0, result.getSecond().intValue());
}
@Test
public void testClassifiesKeyAgreement() {
byte[] payloadBytes = getRandomBytes(123);
for (int version = 0; version < 32; version++) {
int typeAndVersion =
(KeyAgreementConstants.QR_FORMAT_ID << 5) | version;
payloadBytes[0] = (byte) typeAndVersion;
String payload = new String(payloadBytes, ISO_8859_1);
Pair<QrCodeType, Integer> result =
classifier.classifyQrCode(payload);
assertEquals(BQP, result.getFirst());
assertEquals(version, result.getSecond().intValue());
}
}
@Test
public void testClassifiesMailbox() {
byte[] payloadBytes = getRandomBytes(123);
for (int version = 0; version < 32; version++) {
int typeAndVersion =
(MailboxConstants.QR_FORMAT_ID << 5) | version;
payloadBytes[0] = (byte) typeAndVersion;
String payload = new String(payloadBytes, ISO_8859_1);
Pair<QrCodeType, Integer> result =
classifier.classifyQrCode(payload);
assertEquals(MAILBOX, result.getFirst());
assertEquals(version, result.getSecond().intValue());
}
}
@Test
public void testClassifiesUnknownFormatIdAsUnknown() {
byte[] payloadBytes = getRandomBytes(123);
int unknownFormatId = MailboxConstants.QR_FORMAT_ID + 1;
int typeAndVersion = unknownFormatId << 5;
payloadBytes[0] = (byte) typeAndVersion;
String payload = new String(payloadBytes, ISO_8859_1);
Pair<QrCodeType, Integer> result = classifier.classifyQrCode(payload);
assertEquals(UNKNOWN, result.getFirst());
assertEquals(0, result.getSecond().intValue());
}
}

View File

@@ -12,7 +12,6 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException; import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetDecoder;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
@@ -33,6 +32,7 @@ import static java.util.logging.Level.WARNING;
import static jssc.SerialPort.PURGE_RXCLEAR; import static jssc.SerialPort.PURGE_RXCLEAR;
import static jssc.SerialPort.PURGE_TXCLEAR; import static jssc.SerialPort.PURGE_TXCLEAR;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.StringUtils.US_ASCII;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -41,7 +41,6 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(ModemImpl.class.getName()); Logger.getLogger(ModemImpl.class.getName());
private static final Charset US_ASCII = Charset.forName("US-ASCII");
private static final int MAX_LINE_LENGTH = 256; private static final int MAX_LINE_LENGTH = 256;
private static final int[] BAUD_RATES = { private static final int[] BAUD_RATES = {
256000, 128000, 115200, 57600, 38400, 19200, 14400, 9600, 4800, 1200 256000, 128000, 115200, 57600, 38400, 19200, 14400, 9600, 4800, 1200

View File

@@ -30,8 +30,8 @@ dependencyVerification {
'org.briarproject:obfs4proxy-windows:0.0.14-tor1:obfs4proxy-windows-0.0.14-tor1.jar:9dd122b31b3cd1616f168091dcdb01de049d1e052fe5c089b7627618a8a2694b', 'org.briarproject:obfs4proxy-windows:0.0.14-tor1:obfs4proxy-windows-0.0.14-tor1.jar:9dd122b31b3cd1616f168091dcdb01de049d1e052fe5c089b7627618a8a2694b',
'org.briarproject:snowflake-linux:2.3.1:snowflake-linux-2.3.1.jar:99ecf4546d8f79eb8408168c09380fec596558ac934554bf7d4247ea7ef2c9f3', 'org.briarproject:snowflake-linux:2.3.1:snowflake-linux-2.3.1.jar:99ecf4546d8f79eb8408168c09380fec596558ac934554bf7d4247ea7ef2c9f3',
'org.briarproject:snowflake-windows:2.3.1:snowflake-windows-2.3.1.jar:d011f1a72c00a221f56380c19aad8ff11db8c2bb1adb0784125572d80b4d275a', 'org.briarproject:snowflake-windows:2.3.1:snowflake-windows-2.3.1.jar:d011f1a72c00a221f56380c19aad8ff11db8c2bb1adb0784125572d80b4d275a',
'org.briarproject:tor-linux:0.4.5.14:tor-linux-0.4.5.14.jar:1844e54cf6df0c85cec219381a3364c759ae444a6b63f7558b757becb7d41d08', 'org.briarproject:tor-linux:0.4.7.13:tor-linux-0.4.7.13.jar:9819ee973cbcdc133f7d04aef9d4b957a35087627a790e532142d15412a9636f',
'org.briarproject:tor-windows:0.4.5.14:tor-windows-0.4.5.14.jar:d337afa1043f0cfa7e6e8c2473d682a5663a2c8052bb97a770450893c78c9b4f', 'org.briarproject:tor-windows:0.4.7.13:tor-windows-0.4.7.13.jar:853d2769665614e26703cbe02e43b218b064c04a0bcd120fdc459cda45bd2606',
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a', 'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb', 'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9', 'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',

View File

@@ -26,8 +26,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 31 targetSdkVersion 31
versionCode 10418 versionCode 10420
versionName "1.4.18" versionName "1.4.20"
applicationId "org.briarproject.briar.android" applicationId "org.briarproject.briar.android"
buildConfigField "String", "TorVersion", "\"$tor_version\"" buildConfigField "String", "TorVersion", "\"$tor_version\""

View File

@@ -52,7 +52,6 @@ import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResul
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
import static org.briarproject.bramble.util.AndroidUtils.isUiThread; import static org.briarproject.bramble.util.AndroidUtils.isUiThread;
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY; import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_CHANNEL_ID;
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_ID;
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_OLD_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_OLD_ID;
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_NOTIFICATION_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_NOTIFICATION_ID;
@@ -141,11 +140,6 @@ public class BriarService extends Service {
ongoingChannel.setLockscreenVisibility(VISIBILITY_SECRET); ongoingChannel.setLockscreenVisibility(VISIBILITY_SECRET);
ongoingChannel.setShowBadge(false); ongoingChannel.setShowBadge(false);
nm.createNotificationChannel(ongoingChannel); nm.createNotificationChannel(ongoingChannel);
// Delete the unused channel previously used for startup
// failure notifications
// TODO: Remove this ID after a reasonable upgrade period
// (added 2021-07-12)
nm.deleteNotificationChannel(FAILURE_CHANNEL_ID);
} }
Notification foregroundNotification = Notification foregroundNotification =
notificationManager.getForegroundNotification(); notificationManager.getForegroundNotification();

View File

@@ -41,7 +41,6 @@ import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.util.HtmlUtils.ARTICLE;
@NotNullByDefault @NotNullByDefault
abstract class BaseViewModel extends DbViewModel implements EventListener { abstract class BaseViewModel extends DbViewModel implements EventListener {
@@ -115,7 +114,7 @@ abstract class BaseViewModel extends DbViewModel implements EventListener {
@DatabaseExecutor @DatabaseExecutor
private String getPostText(Transaction txn, MessageId m) private String getPostText(Transaction txn, MessageId m)
throws DbException { throws DbException {
return HtmlUtils.clean(blogManager.getPostText(txn, m), ARTICLE); return HtmlUtils.cleanArticle(blogManager.getPostText(txn, m));
} }
LiveData<LiveResult<BlogPostItem>> loadBlogPost(GroupId g, MessageId m) { LiveData<LiveResult<BlogPostItem>> loadBlogPost(GroupId g, MessageId m) {

View File

@@ -3,6 +3,7 @@ package org.briarproject.briar.android.blog;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.text.Spanned; import android.text.Spanned;
import android.text.util.Linkify;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -170,7 +171,12 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
// TODO make author clickable #624 // TODO make author clickable #624
text.setText(c.getComment()); text.setText(c.getComment());
if (fullText) text.setTextIsSelectable(true); Linkify.addLinks(text, Linkify.WEB_URLS);
text.setMovementMethod(null);
if (fullText) {
text.setTextIsSelectable(true);
makeLinksClickable(text, listener::onLinkClick);
}
commentContainer.addView(v); commentContainer.addView(v);
} }

View File

@@ -1,7 +1,6 @@
package org.briarproject.briar.android.blog; package org.briarproject.briar.android.blog;
import android.os.Bundle; import android.os.Bundle;
import android.widget.Toast;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
@@ -16,10 +15,6 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.EXISTS;
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.FAILED;
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.IMPORTED;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class RssFeedActivity extends BriarActivity public class RssFeedActivity extends BriarActivity
@@ -50,13 +45,13 @@ public class RssFeedActivity extends BriarActivity
viewModel.getImportResult().observeEvent(this, this::onImportResult); viewModel.getImportResult().observeEvent(this, this::onImportResult);
} }
private void onImportResult(RssFeedViewModel.ImportResult result) { private void onImportResult(boolean result) {
if (result == IMPORTED) { if (result) {
FragmentManager fm = getSupportFragmentManager(); FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentByTag(RssFeedImportFragment.TAG) != null) { if (fm.findFragmentByTag(RssFeedImportFragment.TAG) != null) {
onBackPressed(); onBackPressed();
} }
} else if (result == FAILED) { } else {
String url = viewModel.getUrlFailedImport(); String url = viewModel.getUrlFailedImport();
if (url == null) { if (url == null) {
throw new AssertionError(); throw new AssertionError();
@@ -65,9 +60,6 @@ public class RssFeedActivity extends BriarActivity
RssFeedImportFailedDialogFragment.newInstance(url); RssFeedImportFailedDialogFragment.newInstance(url);
dialog.show(getSupportFragmentManager(), dialog.show(getSupportFragmentManager(),
RssFeedImportFailedDialogFragment.TAG); RssFeedImportFailedDialogFragment.TAG);
} else if (result == EXISTS) {
Toast.makeText(this, R.string.blogs_rss_feeds_import_exists,
Toast.LENGTH_LONG).show();
} }
} }
} }

View File

@@ -28,8 +28,7 @@ class RssFeedAdapter extends ListAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
super(new DiffUtil.ItemCallback<Feed>() { super(new DiffUtil.ItemCallback<Feed>() {
@Override @Override
public boolean areItemsTheSame(Feed a, Feed b) { public boolean areItemsTheSame(Feed a, Feed b) {
return a.getUrl().equals(b.getUrl()) && return a.getBlogId().equals(b.getBlogId()) &&
a.getBlogId().equals(b.getBlogId()) &&
a.getAdded() == b.getAdded(); a.getAdded() == b.getAdded();
} }
@@ -86,8 +85,8 @@ class RssFeedAdapter extends ListAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
delete.setOnClickListener(v -> listener.onDeleteClick(item)); delete.setOnClickListener(v -> listener.onDeleteClick(item));
// Author // Author
if (item.getRssAuthor() != null) { if (item.getProperties().getAuthor() != null) {
author.setText(item.getRssAuthor()); author.setText(item.getProperties().getAuthor());
author.setVisibility(VISIBLE); author.setVisibility(VISIBLE);
authorLabel.setVisibility(VISIBLE); authorLabel.setVisibility(VISIBLE);
} else { } else {
@@ -100,8 +99,8 @@ class RssFeedAdapter extends ListAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
updated.setText(formatDate(ctx, item.getUpdated())); updated.setText(formatDate(ctx, item.getUpdated()));
// Description // Description
if (item.getDescription() != null) { if (item.getProperties().getDescription() != null) {
description.setText(item.getDescription()); description.setText(item.getProperties().getDescription());
description.setVisibility(VISIBLE); description.setVisibility(VISIBLE);
} else { } else {
description.setVisibility(GONE); description.setVisibility(GONE);

View File

@@ -22,7 +22,7 @@ import org.briarproject.nullsafety.NotNullByDefault;
import java.io.IOException; import java.io.IOException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.Collections; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -38,13 +38,9 @@ import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.EXISTS;
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.FAILED;
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.IMPORTED;
@NotNullByDefault @NotNullByDefault
class RssFeedViewModel extends DbViewModel { class RssFeedViewModel extends DbViewModel {
enum ImportResult {IMPORTED, FAILED, EXISTS}
private static final Logger LOG = private static final Logger LOG =
getLogger(RssFeedViewModel.class.getName()); getLogger(RssFeedViewModel.class.getName());
@@ -60,7 +56,7 @@ class RssFeedViewModel extends DbViewModel {
private volatile String urlFailedImport = null; private volatile String urlFailedImport = null;
private final MutableLiveData<Boolean> isImporting = private final MutableLiveData<Boolean> isImporting =
new MutableLiveData<>(false); new MutableLiveData<>(false);
private final MutableLiveEvent<ImportResult> importResult = private final MutableLiveEvent<Boolean> importResult =
new MutableLiveEvent<>(); new MutableLiveEvent<>();
@Inject @Inject
@@ -101,7 +97,6 @@ class RssFeedViewModel extends DbViewModel {
private List<Feed> loadFeeds(Transaction txn) throws DbException { private List<Feed> loadFeeds(Transaction txn) throws DbException {
long start = now(); long start = now();
List<Feed> feeds = feedManager.getFeeds(txn); List<Feed> feeds = feedManager.getFeeds(txn);
Collections.sort(feeds);
logDuration(LOG, "Loading feeds", start); logDuration(LOG, "Loading feeds", start);
return feeds; return feeds;
} }
@@ -125,7 +120,7 @@ class RssFeedViewModel extends DbViewModel {
}); });
} }
LiveEvent<ImportResult> getImportResult() { LiveEvent<Boolean> getImportResult() {
return importResult; return importResult;
} }
@@ -138,21 +133,23 @@ class RssFeedViewModel extends DbViewModel {
urlFailedImport = null; urlFailedImport = null;
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
try { try {
if (exists(url)) {
importResult.postEvent(EXISTS);
return;
}
Feed feed = feedManager.addFeed(url); Feed feed = feedManager.addFeed(url);
List<Feed> updated = addListItem(getList(feeds), feed); // Update the feed if it was already present
if (updated != null) { List<Feed> feedList = getList(feeds);
Collections.sort(updated); if (feedList == null) feedList = new ArrayList<>();
feeds.postValue(new LiveResult<>(updated)); List<Feed> updated = updateListItems(feedList,
f -> f.equals(feed), f -> feed);
// Add the feed if it wasn't already present
if (updated == null) {
feedList.add(feed);
updated = feedList;
} }
importResult.postEvent(IMPORTED); feeds.postValue(new LiveResult<>(updated));
importResult.postEvent(true);
} catch (DbException | IOException e) { } catch (DbException | IOException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
urlFailedImport = url; urlFailedImport = url;
importResult.postEvent(FAILED); importResult.postEvent(false);
} finally { } finally {
isImporting.postValue(false); isImporting.postValue(false);
} }
@@ -163,16 +160,4 @@ class RssFeedViewModel extends DbViewModel {
String getUrlFailedImport() { String getUrlFailedImport() {
return urlFailedImport; return urlFailedImport;
} }
private boolean exists(String url) {
List<Feed> list = getList(feeds);
if (list != null) {
for (Feed feed : list) {
if (url.equals(feed.getUrl())) {
return true;
}
}
}
return false;
}
} }

View File

@@ -3,12 +3,14 @@ package org.briarproject.briar.android.contact.add.nearby;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
abstract class AddContactState { abstract class AddContactState {
static class KeyAgreementListening extends AddContactState { static class KeyAgreementListening extends AddContactState {
final Bitmap qrCode; final Bitmap qrCode;
KeyAgreementListening(Bitmap qrCode) { KeyAgreementListening(Bitmap qrCode) {
@@ -29,6 +31,7 @@ abstract class AddContactState {
} }
static class ContactExchangeFinished extends AddContactState { static class ContactExchangeFinished extends AddContactState {
final ContactExchangeResult result; final ContactExchangeResult result;
ContactExchangeFinished(ContactExchangeResult result) { ContactExchangeFinished(ContactExchangeResult result) {
@@ -37,25 +40,34 @@ abstract class AddContactState {
} }
static class Failed extends AddContactState { static class Failed extends AddContactState {
/**
* Non-null if failed due to the scanned QR code version.
* True if the app producing the code is too old.
* False if the scanning app is too old.
*/
@Nullable
final Boolean qrCodeTooOld;
Failed(@Nullable Boolean qrCodeTooOld) { static class WrongQrCodeType extends Failed {
this.qrCodeTooOld = qrCodeTooOld;
final QrCodeType qrCodeType;
WrongQrCodeType(QrCodeType qrCodeType) {
this.qrCodeType = qrCodeType;
}
} }
Failed() { static class WrongQrCodeVersion extends Failed {
this(null);
/**
* True if the app producing the code is too old.
* False if the scanning app is too old.
*/
final boolean qrCodeTooOld;
WrongQrCodeVersion(boolean qrCodeTooOld) {
this.qrCodeTooOld = qrCodeTooOld;
}
} }
} }
abstract static class ContactExchangeResult { abstract static class ContactExchangeResult {
static class Success extends ContactExchangeResult { static class Success extends ContactExchangeResult {
final Author remoteAuthor; final Author remoteAuthor;
Success(Author remoteAuthor) { Success(Author remoteAuthor) {
@@ -64,6 +76,7 @@ abstract class AddContactState {
} }
static class Error extends ContactExchangeResult { static class Error extends ContactExchangeResult {
@Nullable @Nullable
final Author duplicateAuthor; final Author duplicateAuthor;

View File

@@ -6,12 +6,15 @@ import android.view.MenuItem;
import android.widget.Toast; import android.widget.Toast;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity; import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeFinished; import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeFinished;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult; import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed; import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeType;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeVersion;
import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision; import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision;
import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener; import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
@@ -34,6 +37,7 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.widget.Toast.LENGTH_LONG; import static android.widget.Toast.LENGTH_LONG;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.ACCEPTED; import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.ACCEPTED;
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED; import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED;
@@ -141,9 +145,15 @@ public class AddNearbyContactActivity extends BriarActivity
ContactExchangeResult result = ContactExchangeResult result =
((ContactExchangeFinished) state).result; ((ContactExchangeFinished) state).result;
onContactExchangeResult(result); onContactExchangeResult(result);
} else if (state instanceof WrongQrCodeType) {
QrCodeType qrCodeType = ((WrongQrCodeType) state).qrCodeType;
if (qrCodeType == MAILBOX) onMailboxQrCodeScanned();
else onWrongQrCodeType();
} else if (state instanceof WrongQrCodeVersion) {
boolean qrCodeTooOld = ((WrongQrCodeVersion) state).qrCodeTooOld;
onWrongQrCodeVersion(qrCodeTooOld);
} else if (state instanceof Failed) { } else if (state instanceof Failed) {
Boolean qrCodeTooOld = ((Failed) state).qrCodeTooOld; showErrorFragment();
onAddingContactFailed(qrCodeTooOld);
} }
} }
@@ -170,15 +180,27 @@ public class AddNearbyContactActivity extends BriarActivity
} else throw new AssertionError(); } else throw new AssertionError();
} }
private void onAddingContactFailed(@Nullable Boolean qrCodeTooOld) { private void onMailboxQrCodeScanned() {
if (qrCodeTooOld == null) { String title = getString(R.string.qr_code_invalid);
showErrorFragment(); String msg = getString(R.string.mailbox_qr_code_for_contact);
} else { showNextFragment(
String msg; AddNearbyContactErrorFragment.newInstance(title, msg, false));
if (qrCodeTooOld) msg = getString(R.string.qr_code_too_old_1); }
else msg = getString(R.string.qr_code_too_new_1);
showNextFragment(AddNearbyContactErrorFragment.newInstance(msg)); private void onWrongQrCodeType() {
} String title = getString(R.string.qr_code_invalid);
String msg = getString(R.string.qr_code_format_unknown);
showNextFragment(
AddNearbyContactErrorFragment.newInstance(title, msg, false));
}
private void onWrongQrCodeVersion(boolean qrCodeTooOld) {
String title = getString(R.string.qr_code_invalid);
String msg;
if (qrCodeTooOld) msg = getString(R.string.qr_code_too_old_1);
else msg = getString(R.string.qr_code_too_new_1);
showNextFragment(
AddNearbyContactErrorFragment.newInstance(title, msg, false));
} }
private void showErrorFragment() { private void showErrorFragment() {

View File

@@ -22,6 +22,7 @@ import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.view.View.GONE;
import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen; import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen;
import static org.briarproject.briar.android.util.UiUtils.onSingleLinkClick; import static org.briarproject.briar.android.util.UiUtils.onSingleLinkClick;
@@ -31,17 +32,22 @@ public class AddNearbyContactErrorFragment extends BaseFragment {
public static final String TAG = public static final String TAG =
AddNearbyContactErrorFragment.class.getName(); AddNearbyContactErrorFragment.class.getName();
private static final String ERROR_MSG = "errorMessage"; private static final String ARG_TITLE = "title";
private static final String ARG_ERROR_MSG = "message";
private static final String ARG_FEEDBACK = "feedback";
@Inject @Inject
ViewModelProvider.Factory viewModelFactory; ViewModelProvider.Factory viewModelFactory;
private AddNearbyContactViewModel viewModel; private AddNearbyContactViewModel viewModel;
public static AddNearbyContactErrorFragment newInstance(String errorMsg) { public static AddNearbyContactErrorFragment newInstance(String title,
String errorMessage, boolean feedback) {
AddNearbyContactErrorFragment f = new AddNearbyContactErrorFragment(); AddNearbyContactErrorFragment f = new AddNearbyContactErrorFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putString(ERROR_MSG, errorMsg); args.putString(ARG_TITLE, title);
args.putString(ARG_ERROR_MSG, errorMessage);
args.putBoolean(ARG_FEEDBACK, feedback);
f.setArguments(args); f.setArguments(args);
return f; return f;
} }
@@ -66,19 +72,32 @@ public class AddNearbyContactErrorFragment extends BaseFragment {
View v = inflater.inflate(R.layout.fragment_error_contact_exchange, View v = inflater.inflate(R.layout.fragment_error_contact_exchange,
container, false); container, false);
// set optional error message String title = null, errorMessage = null;
TextView explanation = v.findViewById(R.id.errorMessage); boolean feedback = true;
Bundle args = getArguments(); Bundle args = getArguments();
String errorMessage = args == null ? null : args.getString(ERROR_MSG); if (args != null) {
if (errorMessage == null) { title = args.getString(ARG_TITLE);
explanation.setText(getString(R.string.add_contact_error_two_way)); errorMessage = args.getString(ARG_ERROR_MSG);
} else { feedback = args.getBoolean(ARG_FEEDBACK, true);
explanation.setText(args.getString(ERROR_MSG)); }
if (title != null) {
TextView titleView = v.findViewById(R.id.errorTitle);
titleView.setText(title);
}
if (errorMessage != null) {
TextView messageView = v.findViewById(R.id.errorMessage);
messageView.setText(errorMessage);
} }
// make feedback link clickable
TextView sendFeedback = v.findViewById(R.id.sendFeedback); TextView sendFeedback = v.findViewById(R.id.sendFeedback);
onSingleLinkClick(sendFeedback, this::triggerFeedback); if (feedback) {
// make feedback link clickable
onSingleLinkClick(sendFeedback, this::triggerFeedback);
} else {
sendFeedback.setVisibility(GONE);
}
// buttons // buttons
Button tryAgain = v.findViewById(R.id.tryAgainButton); Button tryAgain = v.findViewById(R.id.tryAgainButton);

View File

@@ -43,6 +43,7 @@ import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.TransportStateEvent; import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
import org.briarproject.bramble.api.qrcode.WrongQrCodeTypeException;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.plugin.bluetooth.BluetoothPlugin; import org.briarproject.bramble.plugin.bluetooth.BluetoothPlugin;
import org.briarproject.briar.R; import org.briarproject.briar.R;
@@ -50,9 +51,13 @@ import org.briarproject.briar.android.contact.add.nearby.AddContactState.Contact
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult.Error; import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult.Error;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult.Success; import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult.Success;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeStarted; import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeStarted;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeType;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeVersion;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementListening; import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementListening;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementStarted; import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementStarted;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementWaiting; import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementWaiting;
import org.briarproject.briar.android.contact.add.nearby.AddContactState.QrCodeScanned;
import org.briarproject.briar.android.qrcode.QrCodeDecoder; import org.briarproject.briar.android.qrcode.QrCodeDecoder;
import org.briarproject.briar.android.qrcode.QrCodeUtils; import org.briarproject.briar.android.qrcode.QrCodeUtils;
import org.briarproject.briar.android.viewmodel.LiveEvent; import org.briarproject.briar.android.viewmodel.LiveEvent;
@@ -60,7 +65,6 @@ import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.nullsafety.NotNullByDefault;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -85,6 +89,7 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE; import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING; import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactPermissionManager.areEssentialPermissionsGranted; import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactPermissionManager.areEssentialPermissionsGranted;
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.NO_ADAPTER; import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.NO_ADAPTER;
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED; import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED;
@@ -126,9 +131,6 @@ class AddNearbyContactViewModel extends AndroidViewModel
REFUSED REFUSED
} }
@SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
private final EventBus eventBus; private final EventBus eventBus;
private final AndroidExecutor androidExecutor; private final AndroidExecutor androidExecutor;
private final Executor ioExecutor; private final Executor ioExecutor;
@@ -376,11 +378,11 @@ class AddNearbyContactViewModel extends AndroidViewModel
} else if (e instanceof KeyAgreementAbortedEvent) { } else if (e instanceof KeyAgreementAbortedEvent) {
LOG.info("KeyAgreementAbortedEvent received"); LOG.info("KeyAgreementAbortedEvent received");
resetPayloadFlags(); resetPayloadFlags();
state.setValue(new AddContactState.Failed()); state.setValue(new Failed());
} else if (e instanceof KeyAgreementFailedEvent) { } else if (e instanceof KeyAgreementFailedEvent) {
LOG.info("KeyAgreementFailedEvent received"); LOG.info("KeyAgreementFailedEvent received");
resetPayloadFlags(); resetPayloadFlags();
state.setValue(new AddContactState.Failed()); state.setValue(new Failed());
} }
} }
@@ -446,22 +448,22 @@ class AddNearbyContactViewModel extends AndroidViewModel
// Ignore results until the KeyAgreementTask is ready // Ignore results until the KeyAgreementTask is ready
if (!gotLocalPayload || gotRemotePayload || currentTask == null) return; if (!gotLocalPayload || gotRemotePayload || currentTask == null) return;
try { try {
byte[] payloadBytes = result.getText().getBytes(ISO_8859_1); Payload remotePayload = payloadParser.parse(result.getText());
if (LOG.isLoggable(INFO))
LOG.info("Remote payload is " + payloadBytes.length + " bytes");
Payload remotePayload = payloadParser.parse(payloadBytes);
gotRemotePayload = true; gotRemotePayload = true;
currentTask.connectAndRunProtocol(remotePayload); currentTask.connectAndRunProtocol(remotePayload);
state.postValue(new AddContactState.QrCodeScanned()); state.postValue(new QrCodeScanned());
} catch (WrongQrCodeTypeException e) {
resetPayloadFlags();
state.postValue(new WrongQrCodeType(e.getQrCodeType()));
} catch (UnsupportedVersionException e) { } catch (UnsupportedVersionException e) {
resetPayloadFlags(); resetPayloadFlags();
state.postValue(new AddContactState.Failed(e.isTooOld())); state.postValue(new WrongQrCodeVersion(e.isTooOld()));
} catch (IOException | IllegalArgumentException e) { } catch (IOException | IllegalArgumentException e) {
LOG.log(WARNING, "QR Code Invalid", e); LOG.log(WARNING, "QR Code Invalid", e);
androidExecutor.runOnUiThread(() -> Toast.makeText(getApplication(), androidExecutor.runOnUiThread(() -> Toast.makeText(getApplication(),
R.string.qr_code_invalid, LENGTH_LONG).show()); R.string.qr_code_invalid, LENGTH_LONG).show());
resetPayloadFlags(); resetPayloadFlags();
state.postValue(new AddContactState.Failed()); state.postValue(new Failed());
} }
} }

View File

@@ -12,7 +12,7 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@NotNullByDefault @NotNullByDefault
public abstract class BaseContactSelectorAdapter<I extends SelectableContactItem, H extends ContactItemViewHolder<I>> public abstract class BaseContactSelectorAdapter<I extends BaseSelectableContactItem, H extends ContactItemViewHolder<I>>
extends BaseContactListAdapter<I, H> { extends BaseContactListAdapter<I, H> {
public BaseContactSelectorAdapter(Context context, Class<I> c, public BaseContactSelectorAdapter(Context context, Class<I> c,
@@ -24,7 +24,7 @@ public abstract class BaseContactSelectorAdapter<I extends SelectableContactItem
Collection<ContactId> selected = new ArrayList<>(); Collection<ContactId> selected = new ArrayList<>();
for (int i = 0; i < items.size(); i++) { for (int i = 0; i < items.size(); i++) {
SelectableContactItem item = items.get(i); BaseSelectableContactItem item = items.get(i);
if (item.isSelected()) selected.add(item.getContact().getId()); if (item.isSelected()) selected.add(item.getContact().getId());
} }
return selected; return selected;

View File

@@ -33,7 +33,7 @@ import static org.briarproject.briar.android.contactselection.ContactSelectorAct
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public abstract class BaseContactSelectorFragment<I extends SelectableContactItem, A extends BaseContactSelectorAdapter<I, ? extends ContactItemViewHolder<I>>> public abstract class BaseContactSelectorFragment<I extends BaseSelectableContactItem, A extends BaseContactSelectorAdapter<I, ? extends ContactItemViewHolder<I>>>
extends BaseFragment extends BaseFragment
implements OnContactClickListener<I> { implements OnContactClickListener<I> {

View File

@@ -17,7 +17,7 @@ import static org.briarproject.briar.android.util.UiUtils.GREY_OUT;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
public class BaseSelectableContactHolder<I extends SelectableContactItem> public abstract class BaseSelectableContactHolder<I extends BaseSelectableContactItem>
extends ContactItemViewHolder<I> { extends ContactItemViewHolder<I> {
private final CheckBox checkBox; private final CheckBox checkBox;

View File

@@ -0,0 +1,32 @@
package org.briarproject.briar.android.contactselection;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.briar.android.contact.ContactItem;
import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
@NotNullByDefault
public abstract class BaseSelectableContactItem extends ContactItem {
private boolean selected;
public BaseSelectableContactItem(Contact contact, AuthorInfo authorInfo,
boolean selected) {
super(contact, authorInfo);
this.selected = selected;
}
boolean isSelected() {
return selected;
}
void toggleSelected() {
selected = !selected;
}
public abstract boolean isDisabled();
}

View File

@@ -10,7 +10,7 @@ import org.briarproject.nullsafety.NotNullByDefault;
import java.util.Collection; import java.util.Collection;
@NotNullByDefault @NotNullByDefault
public interface ContactSelectorController<I extends SelectableContactItem> public interface ContactSelectorController<I extends BaseSelectableContactItem>
extends DbController { extends DbController {
void loadContacts(GroupId g, Collection<ContactId> selection, void loadContacts(GroupId g, Collection<ContactId> selection,

View File

@@ -11,6 +11,7 @@ import org.briarproject.briar.android.controller.DbControllerImpl;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler; import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.api.identity.AuthorInfo; import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.briar.api.identity.AuthorManager; import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.sharing.SharingManager.SharingStatus;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.nullsafety.NotNullByDefault;
import java.util.ArrayList; import java.util.ArrayList;
@@ -53,10 +54,8 @@ public abstract class ContactSelectorControllerImpl
AuthorInfo authorInfo = authorManager.getAuthorInfo(c); AuthorInfo authorInfo = authorManager.getAuthorInfo(c);
// was this contact already selected? // was this contact already selected?
boolean selected = selection.contains(c.getId()); boolean selected = selection.contains(c.getId());
// can this contact be selected?
boolean disabled = isDisabled(g, c);
contacts.add(new SelectableContactItem(c, authorInfo, contacts.add(new SelectableContactItem(c, authorInfo,
selected, disabled)); selected, getSharingStatus(g, c)));
} }
handler.onResult(contacts); handler.onResult(contacts);
} catch (DbException e) { } catch (DbException e) {
@@ -67,7 +66,7 @@ public abstract class ContactSelectorControllerImpl
} }
@DatabaseExecutor @DatabaseExecutor
protected abstract boolean isDisabled(GroupId g, Contact c) protected abstract SharingStatus getSharingStatus(GroupId g, Contact c)
throws DbException; throws DbException;
} }

View File

@@ -2,15 +2,22 @@ package org.briarproject.briar.android.contactselection;
import android.view.View; import android.view.View;
import org.briarproject.briar.R;
import org.briarproject.briar.android.contact.OnContactClickListener; import org.briarproject.briar.android.contact.OnContactClickListener;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.nullsafety.NotNullByDefault;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import static android.view.View.GONE; import static android.view.View.GONE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.ERROR;
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.INVITE_RECEIVED;
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.INVITE_SENT;
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.NOT_SUPPORTED;
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.SHARING;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
@@ -27,6 +34,19 @@ class SelectableContactHolder
super.bind(item, listener); super.bind(item, listener);
if (item.isDisabled()) { if (item.isDisabled()) {
@StringRes int strRes;
if (item.getSharingStatus() == SHARING) {
strRes = R.string.forum_invitation_already_sharing;
} else if (item.getSharingStatus() == INVITE_SENT) {
strRes = R.string.forum_invitation_already_invited;
} else if (item.getSharingStatus() == INVITE_RECEIVED) {
strRes = R.string.forum_invitation_invite_received;
} else if (item.getSharingStatus() == NOT_SUPPORTED) {
strRes = R.string.forum_invitation_not_supported;
} else if (item.getSharingStatus() == ERROR) {
strRes = R.string.forum_invitation_error;
} else throw new AssertionError("Unhandled SharingStatus");
info.setText(strRes);
info.setVisibility(VISIBLE); info.setVisibility(VISIBLE);
} else { } else {
info.setVisibility(GONE); info.setVisibility(GONE);

View File

@@ -1,36 +1,33 @@
package org.briarproject.briar.android.contactselection; package org.briarproject.briar.android.contactselection;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.briar.android.contact.ContactItem;
import org.briarproject.briar.api.identity.AuthorInfo; import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.briar.api.sharing.SharingManager.SharingStatus;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.SHAREABLE;
@NotThreadSafe @NotThreadSafe
@NotNullByDefault @NotNullByDefault
public class SelectableContactItem extends ContactItem { public class SelectableContactItem extends BaseSelectableContactItem {
private boolean selected; private final SharingStatus sharingStatus;
private final boolean disabled;
public SelectableContactItem(Contact contact, AuthorInfo authorInfo, public SelectableContactItem(Contact contact, AuthorInfo authorInfo,
boolean selected, boolean disabled) { boolean selected, SharingStatus sharingStatus) {
super(contact, authorInfo); super(contact, authorInfo, selected);
this.selected = selected; this.sharingStatus = sharingStatus;
this.disabled = disabled;
} }
boolean isSelected() { public SharingStatus getSharingStatus() {
return selected; return sharingStatus;
}
void toggleSelected() {
selected = !selected;
} }
@Override
public boolean isDisabled() { public boolean isDisabled() {
return disabled; return sharingStatus != SHAREABLE;
} }
} }

View File

@@ -66,6 +66,7 @@ import org.briarproject.briar.android.view.TextAttachmentController.AttachmentLi
import org.briarproject.briar.android.view.TextInputView; import org.briarproject.briar.android.view.TextInputView;
import org.briarproject.briar.android.view.TextSendController; import org.briarproject.briar.android.view.TextSendController;
import org.briarproject.briar.android.view.TextSendController.SendState; import org.briarproject.briar.android.view.TextSendController.SendState;
import org.briarproject.briar.android.widget.LinkDialogFragment;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.attachment.AttachmentHeader; import org.briarproject.briar.api.attachment.AttachmentHeader;
import org.briarproject.briar.api.autodelete.event.ConversationMessagesDeletedEvent; import org.briarproject.briar.api.autodelete.event.ConversationMessagesDeletedEvent;
@@ -476,6 +477,12 @@ public class ConversationActivity extends BriarActivity
actionMode = null; actionMode = null;
} }
@Override
public void onLinkClick(String url) {
LinkDialogFragment f = LinkDialogFragment.newInstance(url);
f.show(getSupportFragmentManager(), f.getUniqueTag());
}
private void addSelectionTracker() { private void addSelectionTracker() {
RecyclerView recyclerView = list.getRecyclerView(); RecyclerView recyclerView = list.getRecyclerView();
if (recyclerView.getAdapter() != adapter) if (recyclerView.getAdapter() != adapter)
@@ -925,6 +932,7 @@ public class ConversationActivity extends BriarActivity
} }
private void removeContact() { private void removeContact() {
list.showProgressBar();
runOnDbThread(() -> { runOnDbThread(() -> {
try { try {
contactManager.removeContact(contactId); contactManager.removeContact(contactId);

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android.conversation; package org.briarproject.briar.android.conversation;
import android.content.Context; import android.content.Context;
import android.text.util.Linkify;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
@@ -19,6 +20,7 @@ import static android.view.View.VISIBLE;
import static org.briarproject.bramble.util.StringUtils.trim; import static org.briarproject.bramble.util.StringUtils.trim;
import static org.briarproject.briar.android.util.UiUtils.formatDate; import static org.briarproject.briar.android.util.UiUtils.formatDate;
import static org.briarproject.briar.android.util.UiUtils.formatDuration; import static org.briarproject.briar.android.util.UiUtils.formatDuration;
import static org.briarproject.briar.android.util.UiUtils.makeLinksClickable;
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER; import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
@UiThread @UiThread
@@ -58,6 +60,8 @@ abstract class ConversationItemViewHolder extends ViewHolder {
if (item.getText() != null) { if (item.getText() != null) {
text.setText(trim(item.getText())); text.setText(trim(item.getText()));
Linkify.addLinks(text, Linkify.WEB_URLS);
makeLinksClickable(text, listener::onLinkClick);
} }
long timestamp = item.getTime(); long timestamp = item.getTime();

View File

@@ -20,4 +20,6 @@ interface ConversationListener {
void onAutoDeleteTimerNoticeClicked(); void onAutoDeleteTimerNoticeClicked();
void onLinkClick(String url);
} }

View File

@@ -1,5 +1,6 @@
package org.briarproject.briar.android.conversation; package org.briarproject.briar.android.conversation;
import android.text.util.Linkify;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
@@ -13,6 +14,7 @@ import static android.view.View.GONE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
import static org.briarproject.bramble.util.StringUtils.trim; import static org.briarproject.bramble.util.StringUtils.trim;
import static org.briarproject.briar.android.util.UiUtils.makeLinksClickable;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
@@ -40,6 +42,8 @@ class ConversationNoticeViewHolder extends ConversationItemViewHolder {
} else { } else {
msgText.setVisibility(VISIBLE); msgText.setVisibility(VISIBLE);
msgText.setText(trim(text)); msgText.setText(trim(text));
Linkify.addLinks(msgText, Linkify.WEB_URLS);
makeLinksClickable(msgText, listener::onLinkClick);
layout.setBackgroundResource(isIncoming() ? layout.setBackgroundResource(isIncoming() ?
R.drawable.notice_in_bottom : R.drawable.notice_out_bottom); R.drawable.notice_in_bottom : R.drawable.notice_out_bottom);
} }

View File

@@ -436,7 +436,7 @@ class HotspotManager {
} }
private static String createWifiLoginString(String ssid, String password) { private static String createWifiLoginString(String ssid, String password) {
// https://en.wikipedia.org/wiki/QR_code#WiFi_network_login // https://en.wikipedia.org/wiki/QR_code#Joining_a_Wi%E2%80%91Fi_network
// do not remove the dangling ';', it can cause problems to omit it // do not remove the dangling ';', it can cause problems to omit it
return "WIFI:S:" + ssid + ";T:WPA;P:" + password + ";;"; return "WIFI:S:" + ssid + ";T:WPA;P:" + password + ";;";
} }

View File

@@ -6,10 +6,24 @@ import android.widget.ProgressBar;
import android.widget.Toast; import android.widget.Toast;
import org.briarproject.bramble.api.mailbox.MailboxPairingState; import org.briarproject.bramble.api.mailbox.MailboxPairingState;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.ConnectionError;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.InvalidQrCode;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.MailboxAlreadyPaired;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Paired;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Pending;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.UnexpectedError;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity; import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.fragment.FinalFragment; import org.briarproject.briar.android.fragment.FinalFragment;
import org.briarproject.briar.android.mailbox.MailboxState.CameraError;
import org.briarproject.briar.android.mailbox.MailboxState.IsPaired;
import org.briarproject.briar.android.mailbox.MailboxState.NotSetup;
import org.briarproject.briar.android.mailbox.MailboxState.OfflineWhenPairing;
import org.briarproject.briar.android.mailbox.MailboxState.Pairing;
import org.briarproject.briar.android.mailbox.MailboxState.ScanningQrCode;
import org.briarproject.briar.android.mailbox.MailboxState.ShowDownload;
import org.briarproject.briar.android.mailbox.MailboxState.WasUnpaired;
import org.briarproject.briar.android.view.BlankFragment; import org.briarproject.briar.android.view.BlankFragment;
import org.briarproject.nullsafety.MethodsNotNullByDefault; import org.briarproject.nullsafety.MethodsNotNullByDefault;
import org.briarproject.nullsafety.ParametersNotNullByDefault; import org.briarproject.nullsafety.ParametersNotNullByDefault;
@@ -25,6 +39,9 @@ import androidx.lifecycle.ViewModelProvider;
import static android.view.View.INVISIBLE; import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static android.widget.Toast.LENGTH_LONG; import static android.widget.Toast.LENGTH_LONG;
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
import static org.briarproject.briar.android.util.UiUtils.showFragment; import static org.briarproject.briar.android.util.UiUtils.showFragment;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@@ -56,24 +73,23 @@ public class MailboxActivity extends BriarActivity {
} }
viewModel.getPairingState().observeEvent(this, state -> { viewModel.getPairingState().observeEvent(this, state -> {
if (state instanceof MailboxState.NotSetup) { if (state instanceof NotSetup) {
onNotSetup(); onNotSetup();
} else if (state instanceof MailboxState.ShowDownload) { } else if (state instanceof ShowDownload) {
onShowDownload(); onShowDownload();
} else if (state instanceof MailboxState.ScanningQrCode) { } else if (state instanceof ScanningQrCode) {
onScanningQrCode(); onScanningQrCode();
} else if (state instanceof MailboxState.Pairing) { } else if (state instanceof Pairing) {
MailboxPairingState s = MailboxPairingState s = ((Pairing) state).pairingState;
((MailboxState.Pairing) state).pairingState;
onMailboxPairingStateChanged(s); onMailboxPairingStateChanged(s);
} else if (state instanceof MailboxState.OfflineWhenPairing) { } else if (state instanceof OfflineWhenPairing) {
onOffline(); onOffline();
} else if (state instanceof MailboxState.CameraError) { } else if (state instanceof CameraError) {
onCameraError(); onCameraError();
} else if (state instanceof MailboxState.IsPaired) { } else if (state instanceof IsPaired) {
onIsPaired(((MailboxState.IsPaired) state).isOnline); onIsPaired(((IsPaired) state).isOnline);
} else if (state instanceof MailboxState.WasUnpaired) { } else if (state instanceof WasUnpaired) {
MailboxState.WasUnpaired s = (MailboxState.WasUnpaired) state; WasUnpaired s = (WasUnpaired) state;
onUnPaired(s.tellUserToWipeMailbox); onUnPaired(s.tellUserToWipeMailbox);
} else { } else {
throw new AssertionError("Unknown state: " + state); throw new AssertionError("Unknown state: " + state);
@@ -104,7 +120,7 @@ public class MailboxActivity extends BriarActivity {
@Override @Override
public void onBackPressed() { public void onBackPressed() {
MailboxState s = viewModel.getPairingState().getLastValue(); MailboxState s = viewModel.getPairingState().getLastValue();
if (s instanceof MailboxState.Pairing) { if (s instanceof Pairing) {
// don't go back in the flow if we are already pairing // don't go back in the flow if we are already pairing
// with the mailbox. We provide a try-again button instead. // with the mailbox. We provide a try-again button instead.
supportFinishAfterTransition(); supportFinishAfterTransition();
@@ -158,33 +174,44 @@ public class MailboxActivity extends BriarActivity {
} }
Fragment f; Fragment f;
String tag; String tag;
if (s instanceof MailboxPairingState.QrCodeReceived) { if (s instanceof Pending) {
f = new MailboxConnectingFragment(); long timeStarted = ((Pending) s).timeStarted;
f = MailboxConnectingFragment.newInstance(timeStarted);
tag = MailboxConnectingFragment.TAG; tag = MailboxConnectingFragment.TAG;
} else if (s instanceof MailboxPairingState.Pairing) { } else if (s instanceof InvalidQrCode) {
f = new MailboxConnectingFragment(); InvalidQrCode i = (InvalidQrCode) s;
tag = MailboxConnectingFragment.TAG; int errorRes;
} else if (s instanceof MailboxPairingState.InvalidQrCode) { if (i.qrCodeType == MAILBOX) {
f = ErrorFragment.newInstance( if (i.formatVersion < QR_FORMAT_VERSION) {
R.string.mailbox_setup_qr_code_wrong_title, errorRes = R.string.mailbox_qr_code_too_old;
R.string.mailbox_setup_qr_code_wrong_description); } else if (i.formatVersion > QR_FORMAT_VERSION) {
errorRes = R.string.mailbox_qr_code_too_new;
} else {
errorRes = R.string.mailbox_setup_qr_code_wrong_description;
}
} else if (i.qrCodeType == BQP) {
errorRes = R.string.contact_qr_code_for_mailbox;
} else {
errorRes = R.string.mailbox_setup_qr_code_wrong_description;
}
f = ErrorFragment.newInstance(R.string.qr_code_invalid, errorRes);
tag = ErrorFragment.TAG; tag = ErrorFragment.TAG;
} else if (s instanceof MailboxPairingState.MailboxAlreadyPaired) { } else if (s instanceof MailboxAlreadyPaired) {
f = ErrorFragment.newInstance( f = ErrorFragment.newInstance(
R.string.mailbox_setup_already_paired_title, R.string.mailbox_setup_already_paired_title,
R.string.mailbox_setup_already_paired_description); R.string.mailbox_setup_already_paired_description);
tag = ErrorFragment.TAG; tag = ErrorFragment.TAG;
} else if (s instanceof MailboxPairingState.ConnectionError) { } else if (s instanceof ConnectionError) {
f = ErrorFragment.newInstance( f = ErrorFragment.newInstance(
R.string.mailbox_setup_io_error_title, R.string.mailbox_setup_io_error_title,
R.string.mailbox_setup_io_error_description); R.string.mailbox_setup_io_error_description);
tag = ErrorFragment.TAG; tag = ErrorFragment.TAG;
} else if (s instanceof MailboxPairingState.UnexpectedError) { } else if (s instanceof UnexpectedError) {
f = ErrorFragment.newInstance( f = ErrorFragment.newInstance(
R.string.mailbox_setup_assertion_error_title, R.string.mailbox_setup_assertion_error_title,
R.string.mailbox_setup_assertion_error_description); R.string.mailbox_setup_assertion_error_description);
tag = ErrorFragment.TAG; tag = ErrorFragment.TAG;
} else if (s instanceof MailboxPairingState.Paired) { } else if (s instanceof Paired) {
f = FinalFragment.newInstance(R.string.mailbox_setup_paired_title, f = FinalFragment.newInstance(R.string.mailbox_setup_paired_title,
R.drawable.ic_check_circle_outline, R.drawable.ic_check_circle_outline,
R.color.briar_brand_green, R.color.briar_brand_green,
@@ -218,6 +245,7 @@ public class MailboxActivity extends BriarActivity {
} }
private void onUnPaired(boolean tellUserToWipeMailbox) { private void onUnPaired(boolean tellUserToWipeMailbox) {
viewModel.clearProblemNotification();
if (tellUserToWipeMailbox) { if (tellUserToWipeMailbox) {
showFragment(getSupportFragmentManager(), new BlankFragment(), showFragment(getSupportFragmentManager(), new BlankFragment(),
BlankFragment.TAG); BlankFragment.TAG);

View File

@@ -1,10 +1,15 @@
package org.briarproject.briar.android.mailbox; package org.briarproject.briar.android.mailbox;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.nullsafety.MethodsNotNullByDefault; import org.briarproject.nullsafety.MethodsNotNullByDefault;
import org.briarproject.nullsafety.ParametersNotNullByDefault; import org.briarproject.nullsafety.ParametersNotNullByDefault;
@@ -12,25 +17,71 @@ import org.briarproject.nullsafety.ParametersNotNullByDefault;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import static org.briarproject.briar.android.util.UiUtils.formatDuration;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class MailboxConnectingFragment extends Fragment { public class MailboxConnectingFragment extends Fragment {
static final String TAG = MailboxConnectingFragment.class.getName(); static final String TAG = MailboxConnectingFragment.class.getName();
private static final String ARG_STARTED = "started";
private static final long TIMEOUT_MS = TorConstants.EXTRA_CONNECT_TIMEOUT;
private static final long REFRESH_INTERVAL_MS = 1_000;
private final Handler handler = new Handler(Looper.getMainLooper());
// Capture a method reference so we use the same reference for posting
// and removing
private final Runnable refresher = this::updateProgressBar;
private ProgressBar progressBar;
private long timeStarted;
public static MailboxConnectingFragment newInstance(long timeStarted) {
MailboxConnectingFragment f = new MailboxConnectingFragment();
Bundle args = new Bundle();
args.putLong(ARG_STARTED, timeStarted);
f.setArguments(args);
return f;
}
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_mailbox_connecting, View v = inflater.inflate(R.layout.fragment_mailbox_connecting,
container, false); container, false);
progressBar = v.findViewById(R.id.progressBar);
TextView info = v.findViewById(R.id.info);
String duration = formatDuration(requireContext(), TIMEOUT_MS);
info.setText(getString(R.string.mailbox_setup_connecting_info,
duration));
timeStarted = requireArguments().getLong(ARG_STARTED);
return v;
} }
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
requireActivity().setTitle(R.string.mailbox_setup_title); requireActivity().setTitle(R.string.mailbox_setup_title);
updateProgressBar();
} }
@Override
public void onStop() {
super.onStop();
handler.removeCallbacks(refresher);
}
private void updateProgressBar() {
long elapsedMs = System.currentTimeMillis() - timeStarted;
int percent = (int) (elapsedMs * 100 / TIMEOUT_MS);
percent = Math.min(Math.max(percent, 0), 100);
progressBar.setProgress(percent);
handler.postDelayed(refresher, REFRESH_INTERVAL_MS);
}
} }

View File

@@ -15,6 +15,7 @@ import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.mailbox.MailboxManager; import org.briarproject.bramble.api.mailbox.MailboxManager;
import org.briarproject.bramble.api.mailbox.MailboxPairingState; import org.briarproject.bramble.api.mailbox.MailboxPairingState;
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Paired;
import org.briarproject.bramble.api.mailbox.MailboxPairingTask; import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
import org.briarproject.bramble.api.mailbox.MailboxStatus; import org.briarproject.bramble.api.mailbox.MailboxStatus;
import org.briarproject.bramble.api.mailbox.event.OwnMailboxConnectionStatusEvent; import org.briarproject.bramble.api.mailbox.event.OwnMailboxConnectionStatusEvent;
@@ -24,7 +25,14 @@ import org.briarproject.bramble.api.plugin.TorConstants;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent; import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.android.mailbox.MailboxState.CameraError;
import org.briarproject.briar.android.mailbox.MailboxState.IsPaired;
import org.briarproject.briar.android.mailbox.MailboxState.NotSetup; import org.briarproject.briar.android.mailbox.MailboxState.NotSetup;
import org.briarproject.briar.android.mailbox.MailboxState.OfflineWhenPairing;
import org.briarproject.briar.android.mailbox.MailboxState.Pairing;
import org.briarproject.briar.android.mailbox.MailboxState.ScanningQrCode;
import org.briarproject.briar.android.mailbox.MailboxState.ShowDownload;
import org.briarproject.briar.android.mailbox.MailboxState.WasUnpaired;
import org.briarproject.briar.android.qrcode.QrCodeDecoder; import org.briarproject.briar.android.qrcode.QrCodeDecoder;
import org.briarproject.briar.android.viewmodel.DbViewModel; import org.briarproject.briar.android.viewmodel.DbViewModel;
import org.briarproject.briar.android.viewmodel.LiveEvent; import org.briarproject.briar.android.viewmodel.LiveEvent;
@@ -113,7 +121,7 @@ class MailboxViewModel extends DbViewModel
MailboxStatus mailboxStatus = MailboxStatus mailboxStatus =
mailboxManager.getMailboxStatus(txn); mailboxManager.getMailboxStatus(txn);
boolean isOnline = isTorActive(); boolean isOnline = isTorActive();
pairingState.postEvent(new MailboxState.IsPaired(isOnline)); pairingState.postEvent(new IsPaired(isOnline));
status.postValue(mailboxStatus); status.postValue(mailboxStatus);
} else { } else {
pairingState.postEvent(new NotSetup()); pairingState.postEvent(new NotSetup());
@@ -142,14 +150,14 @@ class MailboxViewModel extends DbViewModel
@UiThread @UiThread
private void onTorInactive() { private void onTorInactive() {
MailboxState lastState = pairingState.getLastValue(); MailboxState lastState = pairingState.getLastValue();
if (lastState instanceof MailboxState.IsPaired) { if (lastState instanceof IsPaired) {
// we are already paired, so use IsPaired state // we are already paired, so use IsPaired state
pairingState.setEvent(new MailboxState.IsPaired(false)); pairingState.setEvent(new IsPaired(false));
} else if (lastState instanceof MailboxState.Pairing) { } else if (lastState instanceof Pairing) {
MailboxState.Pairing p = (MailboxState.Pairing) lastState; Pairing p = (Pairing) lastState;
// check that we not just finished pairing (showing success screen) // check that we not just finished pairing (showing success screen)
if (!(p.pairingState instanceof MailboxPairingState.Paired)) { if (!(p.pairingState instanceof Paired)) {
pairingState.setEvent(new MailboxState.OfflineWhenPairing()); pairingState.setEvent(new OfflineWhenPairing());
} }
// else ignore offline event as user will be leaving UI flow anyway // else ignore offline event as user will be leaving UI flow anyway
} }
@@ -158,15 +166,15 @@ class MailboxViewModel extends DbViewModel
@UiThread @UiThread
void onScanButtonClicked() { void onScanButtonClicked() {
if (isTorActive()) { if (isTorActive()) {
pairingState.setEvent(new MailboxState.ScanningQrCode()); pairingState.setEvent(new ScanningQrCode());
} else { } else {
pairingState.setEvent(new MailboxState.OfflineWhenPairing()); pairingState.setEvent(new OfflineWhenPairing());
} }
} }
@UiThread @UiThread
void onCameraError() { void onCameraError() {
pairingState.setEvent(new MailboxState.CameraError()); pairingState.setEvent(new CameraError());
} }
@Override @Override
@@ -182,7 +190,7 @@ class MailboxViewModel extends DbViewModel
pairingTask = mailboxManager.startPairingTask(qrCodePayload); pairingTask = mailboxManager.startPairingTask(qrCodePayload);
pairingTask.addObserver(this); pairingTask.addObserver(this);
} else { } else {
pairingState.postEvent(new MailboxState.OfflineWhenPairing()); pairingState.postEvent(new OfflineWhenPairing());
} }
} }
@@ -193,7 +201,7 @@ class MailboxViewModel extends DbViewModel
LOG.info("New pairing state: " + LOG.info("New pairing state: " +
mailboxPairingState.getClass().getSimpleName()); mailboxPairingState.getClass().getSimpleName());
} }
pairingState.setEvent(new MailboxState.Pairing(mailboxPairingState)); pairingState.setEvent(new Pairing(mailboxPairingState));
} }
private boolean isTorActive() { private boolean isTorActive() {
@@ -203,7 +211,7 @@ class MailboxViewModel extends DbViewModel
@UiThread @UiThread
void showDownloadFragment() { void showDownloadFragment() {
pairingState.setEvent(new MailboxState.ShowDownload()); pairingState.setEvent(new ShowDownload());
} }
@UiThread @UiThread
@@ -214,7 +222,7 @@ class MailboxViewModel extends DbViewModel
@UiThread @UiThread
void checkIfOnlineWhenPaired() { void checkIfOnlineWhenPaired() {
boolean isOnline = isTorActive(); boolean isOnline = isTorActive();
pairingState.setEvent(new MailboxState.IsPaired(isOnline)); pairingState.setEvent(new IsPaired(isOnline));
} }
LiveData<Boolean> checkConnection() { LiveData<Boolean> checkConnection() {
@@ -227,7 +235,7 @@ class MailboxViewModel extends DbViewModel
checkConnection(success -> { checkConnection(success -> {
boolean isOnline = isTorActive(); boolean isOnline = isTorActive();
// make UI move back to status fragment by changing pairingState // make UI move back to status fragment by changing pairingState
pairingState.postEvent(new MailboxState.IsPaired(isOnline)); pairingState.postEvent(new IsPaired(isOnline));
}); });
} }
@@ -246,7 +254,7 @@ class MailboxViewModel extends DbViewModel
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
try { try {
boolean wasWiped = mailboxManager.unPair(); boolean wasWiped = mailboxManager.unPair();
pairingState.postEvent(new MailboxState.WasUnpaired(!wasWiped)); pairingState.postEvent(new WasUnpaired(!wasWiped));
} catch (DbException e) { } catch (DbException e) {
handleException(e); handleException(e);
} }

View File

@@ -13,6 +13,7 @@ import org.briarproject.briar.android.privategroup.memberlist.GroupMemberListAct
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity; import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
import org.briarproject.briar.android.threaded.ThreadListActivity; import org.briarproject.briar.android.threaded.ThreadListActivity;
import org.briarproject.briar.android.threaded.ThreadListViewModel; import org.briarproject.briar.android.threaded.ThreadListViewModel;
import org.briarproject.briar.android.widget.LinkDialogFragment;
import org.briarproject.nullsafety.MethodsNotNullByDefault; import org.briarproject.nullsafety.MethodsNotNullByDefault;
import org.briarproject.nullsafety.ParametersNotNullByDefault; import org.briarproject.nullsafety.ParametersNotNullByDefault;
@@ -158,6 +159,12 @@ public class GroupActivity extends
if (isDissolved != null && !isDissolved) super.onReplyClick(item); if (isDissolved != null && !isDissolved) super.onReplyClick(item);
} }
@Override
public void onLinkClick(String url){
LinkDialogFragment f = LinkDialogFragment.newInstance(url);
f.show(getSupportFragmentManager(), f.getUniqueTag());
}
private void setGroupEnabled(boolean enabled) { private void setGroupEnabled(boolean enabled) {
sendController.setReady(enabled); sendController.setReady(enabled);
list.getRecyclerView().setAlpha(enabled ? 1f : 0.5f); list.getRecyclerView().setAlpha(enabled ? 1f : 0.5f);

View File

@@ -26,6 +26,7 @@ import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupManager; import org.briarproject.briar.api.privategroup.PrivateGroupManager;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory; import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager; import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
import org.briarproject.briar.api.sharing.SharingManager.SharingStatus;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.nullsafety.NotNullByDefault;
import java.util.ArrayList; import java.util.ArrayList;
@@ -140,8 +141,8 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
} }
@Override @Override
protected boolean isDisabled(GroupId g, Contact c) throws DbException { protected SharingStatus getSharingStatus(GroupId g, Contact c) throws DbException {
return !groupInvitationManager.isInvitationAllowed(c, g); return groupInvitationManager.getSharingStatus(c, g);
} }
@Override @Override

View File

@@ -92,7 +92,7 @@ class RevealContactsControllerImpl extends DbControllerImpl
boolean selected = boolean selected =
disabled || selection.contains(c.getId()); disabled || selection.contains(c.getId());
items.add(new RevealableContactItem(c, authorInfo, selected, items.add(new RevealableContactItem(c, authorInfo, selected,
disabled, m.getVisibility())); m.getVisibility()));
} }
} }

View File

@@ -1,22 +1,24 @@
package org.briarproject.briar.android.privategroup.reveal; package org.briarproject.briar.android.privategroup.reveal;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.briar.android.contactselection.SelectableContactItem; import org.briarproject.briar.android.contactselection.BaseSelectableContactItem;
import org.briarproject.briar.api.identity.AuthorInfo; import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.briar.api.privategroup.Visibility; import org.briarproject.briar.api.privategroup.Visibility;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.briar.api.privategroup.Visibility.INVISIBLE;
@NotThreadSafe @NotThreadSafe
@NotNullByDefault @NotNullByDefault
class RevealableContactItem extends SelectableContactItem { class RevealableContactItem extends BaseSelectableContactItem {
private final Visibility visibility; private final Visibility visibility;
RevealableContactItem(Contact contact, AuthorInfo authorInfo, RevealableContactItem(Contact contact, AuthorInfo authorInfo,
boolean selected, boolean disabled, Visibility visibility) { boolean selected, Visibility visibility) {
super(contact, authorInfo, selected, disabled); super(contact, authorInfo, selected);
this.visibility = visibility; this.visibility = visibility;
} }
@@ -24,4 +26,8 @@ class RevealableContactItem extends SelectableContactItem {
return visibility; return visibility;
} }
@Override
public boolean isDisabled() {
return visibility != INVISIBLE;
}
} }

View File

@@ -41,7 +41,6 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
private final ResultCallback callback; private final ResultCallback callback;
private Camera camera = null; private Camera camera = null;
private int cameraIndex = 0;
public QrCodeDecoder(AndroidExecutor androidExecutor, public QrCodeDecoder(AndroidExecutor androidExecutor,
@IoExecutor Executor ioExecutor, ResultCallback callback) { @IoExecutor Executor ioExecutor, ResultCallback callback) {
@@ -53,14 +52,12 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
@Override @Override
public void start(Camera camera, int cameraIndex) { public void start(Camera camera, int cameraIndex) {
this.camera = camera; this.camera = camera;
this.cameraIndex = cameraIndex;
askForPreviewFrame(); askForPreviewFrame();
} }
@Override @Override
public void stop() { public void stop() {
camera = null; camera = null;
cameraIndex = 0;
} }
@UiThread @UiThread

View File

@@ -310,14 +310,12 @@ class BriarReportCollector {
btLeAdvertise); btLeAdvertise);
} }
if (hasBtConnectPermission(ctx)) { Pair<String, String> p = getBluetoothAddressAndMethod(ctx, bt);
Pair<String, String> p = getBluetoothAddressAndMethod(ctx, bt); String address = p.getFirst();
String address = p.getFirst(); String method = p.getSecond();
String method = p.getSecond(); connectivityInfo.add("BluetoothAddress",
connectivityInfo.add("BluetoothAddress", scrubMacAddress(address));
scrubMacAddress(address)); connectivityInfo.add("BluetoothAddressMethod", method);
connectivityInfo.add("BluetoothAddressMethod", method);
}
} }
return new ReportItem("Connectivity", R.string.dev_report_connectivity, return new ReportItem("Connectivity", R.string.dev_report_connectivity,
connectivityInfo); connectivityInfo);

View File

@@ -18,7 +18,6 @@ import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING; import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK; 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_ONLY_WHEN_CHARGING; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHEN_CHARGING;
import static org.briarproject.briar.android.settings.SettingsViewModel.BT_NAMESPACE; import static org.briarproject.briar.android.settings.SettingsViewModel.BT_NAMESPACE;
import static org.briarproject.briar.android.settings.SettingsViewModel.TOR_NAMESPACE; import static org.briarproject.briar.android.settings.SettingsViewModel.TOR_NAMESPACE;
@@ -61,32 +60,18 @@ class ConnectionsManager {
} }
void updateTorSettings(Settings settings) { void updateTorSettings(Settings settings) {
Settings torSettings = migrateTorSettings(settings); torEnabled.postValue(settings.getBoolean(PREF_PLUGIN_ENABLE,
torEnabled.postValue(torSettings.getBoolean(PREF_PLUGIN_ENABLE,
TorConstants.DEFAULT_PREF_PLUGIN_ENABLE)); TorConstants.DEFAULT_PREF_PLUGIN_ENABLE));
int torNetworkSetting = torSettings.getInt(PREF_TOR_NETWORK, int torNetworkSetting = settings.getInt(PREF_TOR_NETWORK,
DEFAULT_PREF_TOR_NETWORK); DEFAULT_PREF_TOR_NETWORK);
torNetwork.postValue(Integer.toString(torNetworkSetting)); torNetwork.postValue(Integer.toString(torNetworkSetting));
torMobile.postValue(torSettings.getBoolean(PREF_TOR_MOBILE, torMobile.postValue(settings.getBoolean(PREF_TOR_MOBILE,
DEFAULT_PREF_TOR_MOBILE)); DEFAULT_PREF_TOR_MOBILE));
torCharging
.postValue(torSettings.getBoolean(PREF_TOR_ONLY_WHEN_CHARGING,
DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING));
}
// TODO: Remove after a reasonable migration period (added 2020-06-25) torCharging.postValue(settings.getBoolean(PREF_TOR_ONLY_WHEN_CHARGING,
private Settings migrateTorSettings(Settings s) { DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING));
int network = s.getInt(PREF_TOR_NETWORK, DEFAULT_PREF_TOR_NETWORK);
if (network == PREF_TOR_NETWORK_NEVER) {
s.putInt(PREF_TOR_NETWORK, DEFAULT_PREF_TOR_NETWORK);
s.putBoolean(PREF_PLUGIN_ENABLE, false);
// We don't need to save the migrated settings - the Tor plugin is
// responsible for that. This code just handles the case where the
// settings are loaded before the plugin migrates them.
}
return s;
} }
LiveData<Boolean> btEnabled() { LiveData<Boolean> btEnabled() {

View File

@@ -13,6 +13,7 @@ import org.briarproject.briar.android.contactselection.ContactSelectorController
import org.briarproject.briar.android.controller.handler.ExceptionHandler; import org.briarproject.briar.android.controller.handler.ExceptionHandler;
import org.briarproject.briar.api.blog.BlogSharingManager; import org.briarproject.briar.api.blog.BlogSharingManager;
import org.briarproject.briar.api.identity.AuthorManager; import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.sharing.SharingManager.SharingStatus;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.nullsafety.NotNullByDefault;
import java.util.Collection; import java.util.Collection;
@@ -47,8 +48,9 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
} }
@Override @Override
protected boolean isDisabled(GroupId g, Contact c) throws DbException { protected SharingStatus getSharingStatus(GroupId g, Contact c)
return !blogSharingManager.canBeShared(g, c); throws DbException {
return blogSharingManager.getSharingStatus(g, c);
} }
@Override @Override

View File

@@ -13,6 +13,7 @@ import org.briarproject.briar.android.contactselection.ContactSelectorController
import org.briarproject.briar.android.controller.handler.ExceptionHandler; import org.briarproject.briar.android.controller.handler.ExceptionHandler;
import org.briarproject.briar.api.forum.ForumSharingManager; import org.briarproject.briar.api.forum.ForumSharingManager;
import org.briarproject.briar.api.identity.AuthorManager; import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.sharing.SharingManager.SharingStatus;
import org.briarproject.nullsafety.NotNullByDefault; import org.briarproject.nullsafety.NotNullByDefault;
import java.util.Collection; import java.util.Collection;
@@ -47,8 +48,9 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
} }
@Override @Override
protected boolean isDisabled(GroupId g, Contact c) throws DbException { protected SharingStatus getSharingStatus(GroupId g, Contact c)
return !forumSharingManager.canBeShared(g, c); throws DbException {
return forumSharingManager.getSharingStatus(g, c);
} }
@Override @Override

View File

@@ -4,6 +4,7 @@ import android.animation.Animator;
import android.animation.ArgbEvaluator; import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.content.Context; import android.content.Context;
import android.text.util.Linkify;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator; import android.view.animation.AccelerateInterpolator;
@@ -20,6 +21,7 @@ import androidx.annotation.UiThread;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import static androidx.core.content.ContextCompat.getColor; import static androidx.core.content.ContextCompat.getColor;
import static org.briarproject.briar.android.util.UiUtils.makeLinksClickable;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
@@ -43,6 +45,8 @@ public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
@CallSuper @CallSuper
public void bind(I item, ThreadItemListener<I> listener) { public void bind(I item, ThreadItemListener<I> listener) {
textView.setText(StringUtils.trim(item.getText())); textView.setText(StringUtils.trim(item.getText()));
Linkify.addLinks(textView, Linkify.WEB_URLS);
makeLinksClickable(textView, listener::onLinkClick);
author.setAuthor(item.getAuthor(), item.getAuthorInfo()); author.setAuthor(item.getAuthor(), item.getAuthorInfo());
author.setDate(item.getTimestamp()); author.setDate(item.getTimestamp());

View File

@@ -137,6 +137,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
public interface ThreadItemListener<I> { public interface ThreadItemListener<I> {
void onReplyClick(I item); void onReplyClick(I item);
void onLinkClick(String url);
} }
} }

View File

@@ -19,6 +19,7 @@ import org.briarproject.briar.android.view.TextSendController;
import org.briarproject.briar.android.view.TextSendController.SendListener; import org.briarproject.briar.android.view.TextSendController.SendListener;
import org.briarproject.briar.android.view.TextSendController.SendState; import org.briarproject.briar.android.view.TextSendController.SendState;
import org.briarproject.briar.android.view.UnreadMessageButton; import org.briarproject.briar.android.view.UnreadMessageButton;
import org.briarproject.briar.android.widget.LinkDialogFragment;
import org.briarproject.briar.api.attachment.AttachmentHeader; import org.briarproject.briar.api.attachment.AttachmentHeader;
import org.briarproject.nullsafety.MethodsNotNullByDefault; import org.briarproject.nullsafety.MethodsNotNullByDefault;
import org.briarproject.nullsafety.ParametersNotNullByDefault; import org.briarproject.nullsafety.ParametersNotNullByDefault;
@@ -202,6 +203,12 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
} }
} }
@Override
public void onLinkClick(String url) {
LinkDialogFragment f = LinkDialogFragment.newInstance(url);
f.show(getSupportFragmentManager(), f.getUniqueTag());
}
protected void setToolbarSubTitle(SharingInfo sharingInfo) { protected void setToolbarSubTitle(SharingInfo sharingInfo) {
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
if (actionBar != null) { if (actionBar != null) {

View File

@@ -24,7 +24,6 @@ public interface AndroidNotificationManager {
// Notification IDs // Notification IDs
int ONGOING_NOTIFICATION_ID = 1; int ONGOING_NOTIFICATION_ID = 1;
int FAILURE_NOTIFICATION_ID = 2;
int REMINDER_NOTIFICATION_ID = 3; int REMINDER_NOTIFICATION_ID = 3;
int PRIVATE_MESSAGE_NOTIFICATION_ID = 4; int PRIVATE_MESSAGE_NOTIFICATION_ID = 4;
int GROUP_MESSAGE_NOTIFICATION_ID = 5; int GROUP_MESSAGE_NOTIFICATION_ID = 5;
@@ -47,10 +46,6 @@ public interface AndroidNotificationManager {
String HOTSPOT_CHANNEL_ID = "zHotspot"; String HOTSPOT_CHANNEL_ID = "zHotspot";
String MAILBOX_PROBLEM_CHANNEL_ID = "zMailboxProblem"; String MAILBOX_PROBLEM_CHANNEL_ID = "zMailboxProblem";
// This channel is no longer used - keep the ID so we can remove the
// channel from existing installations
String FAILURE_CHANNEL_ID = "zStartupFailure";
// Actions for pending intents // Actions for pending intents
String ACTION_DISMISS_REMINDER = "dismissReminder"; String ACTION_DISMISS_REMINDER = "dismissReminder";
String ACTION_STOP_HOTSPOT = "stopHotspot"; String ACTION_STOP_HOTSPOT = "stopHotspot";

View File

@@ -51,12 +51,12 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_xlarge" android:layout_marginTop="@dimen/margin_xlarge"
android:text="@string/add_contact_error_two_way"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
app:layout_constraintBottom_toTopOf="@+id/sendFeedback" app:layout_constraintBottom_toTopOf="@+id/sendFeedback"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/errorTitle" app:layout_constraintTop_toBottomOf="@+id/errorTitle" />
tools:text="error explanation" />
<TextView <TextView
android:id="@+id/sendFeedback" android:id="@+id/sendFeedback"

View File

@@ -1,31 +1,51 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<ProgressBar <TextView
android:id="@+id/progressBar" android:id="@+id/title"
style="?android:attr/progressBarStyleLarge" style="@style/TextAppearance.AppCompat.Large"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="16dp" android:layout_marginHorizontal="@dimen/margin_xlarge"
app:layout_constraintBottom_toTopOf="@+id/textView" android:layout_marginTop="@dimen/margin_xlarge"
android:gravity="center"
android:text="@string/mailbox_setup_connecting"
app:layout_constraintBottom_toTopOf="@+id/progressBar"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" /> app:layout_constraintVertical_chainStyle="packed" />
<TextView <ProgressBar
android:id="@+id/textView" android:id="@+id/progressBar"
style="@style/TextAppearance.AppCompat.Large" style="?android:attr/progressBarStyleHorizontal"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="16dp" android:layout_marginHorizontal="@dimen/margin_xlarge"
android:text="@string/mailbox_setup_connecting" android:layout_marginTop="@dimen/margin_xlarge"
android:max="100"
app:layout_constraintBottom_toTopOf="@+id/info"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title" />
<TextView
android:id="@+id/info"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_xlarge"
android:layout_marginTop="@dimen/margin_xlarge"
android:layout_marginBottom="@dimen/margin_xlarge"
android:gravity="center"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progressBar" /> app:layout_constraintTop_toBottomOf="@+id/progressBar"
tools:text="This may take up to 2 minutes" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -33,6 +33,7 @@
android:paddingBottom="@dimen/listitem_vertical_margin" android:paddingBottom="@dimen/listitem_vertical_margin"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_small" android:textSize="@dimen/text_size_small"
android:textColorLink="@color/briar_text_link"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/authorView" app:layout_constraintTop_toBottomOf="@+id/authorView"

View File

@@ -41,6 +41,7 @@
android:layout_marginRight="@dimen/message_bubble_padding_sides_inner" android:layout_marginRight="@dimen/message_bubble_padding_sides_inner"
android:layout_marginBottom="@dimen/message_bubble_padding_bottom_inner" android:layout_marginBottom="@dimen/message_bubble_padding_bottom_inner"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:textColorLink="@color/briar_text_link"
app:layout_constrainedWidth="true" app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@+id/statusLayout" app:layout_constraintBottom_toTopOf="@+id/statusLayout"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

View File

@@ -49,6 +49,7 @@
android:layout_marginRight="@dimen/message_bubble_padding_sides_inner" android:layout_marginRight="@dimen/message_bubble_padding_sides_inner"
android:layout_marginBottom="@dimen/message_bubble_padding_bottom_inner" android:layout_marginBottom="@dimen/message_bubble_padding_bottom_inner"
android:textColor="@color/briar_text_primary_inverse" android:textColor="@color/briar_text_primary_inverse"
android:textColorLink="@color/briar_text_link_inverse"
app:layout_constrainedWidth="true" app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@+id/statusLayout" app:layout_constraintBottom_toTopOf="@+id/statusLayout"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

View File

@@ -21,6 +21,7 @@
android:layout_marginRight="@dimen/message_bubble_margin_non_tail" android:layout_marginRight="@dimen/message_bubble_margin_non_tail"
android:background="@drawable/msg_in_top" android:background="@drawable/msg_in_top"
android:elevation="@dimen/message_bubble_elevation" android:elevation="@dimen/message_bubble_elevation"
android:textColorLink="@color/briar_text_link"
tools:text="Short message" tools:text="Short message"
tools:visibility="visible" /> tools:visibility="visible" />

View File

@@ -23,6 +23,7 @@
android:background="@drawable/msg_out_top" android:background="@drawable/msg_out_top"
android:elevation="@dimen/message_bubble_elevation" android:elevation="@dimen/message_bubble_elevation"
android:textColor="@color/briar_text_primary_inverse" android:textColor="@color/briar_text_primary_inverse"
android:textColorLink="@color/briar_text_link_inverse"
tools:text="This is a long long long message that spans over several lines.\n\nIt ends here." tools:text="This is a long long long message that spans over several lines.\n\nIt ends here."
tools:visibility="visible" /> tools:visibility="visible" />

View File

@@ -22,6 +22,7 @@
android:background="@drawable/msg_in_top" android:background="@drawable/msg_in_top"
android:elevation="@dimen/message_bubble_elevation" android:elevation="@dimen/message_bubble_elevation"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:textColorLink="@color/briar_text_link"
tools:text="Short message" tools:text="Short message"
tools:visibility="visible" /> tools:visibility="visible" />

View File

@@ -114,6 +114,7 @@
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:textIsSelectable="true" android:textIsSelectable="true"
android:textSize="@dimen/text_size_medium" android:textSize="@dimen/text_size_medium"
android:textColorLink="@color/briar_text_link"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"

View File

@@ -224,6 +224,7 @@
<string name="menu_contact">Контакт</string> <string name="menu_contact">Контакт</string>
<!--Adding Contacts--> <!--Adding Contacts-->
<string name="add_contact_title">Добавяне на контакт на живо</string> <string name="add_contact_title">Добавяне на контакт на живо</string>
<string name="add_contact_error_two_way">Сканирахте ли взаимно кодовете си за QR?</string>
<string name="face_to_face">Трябва да се срещнете лично с човека, чиито контакт искате да добавите.\n\nТака никой не може да се представи за вас или да чете съобщенията ви в бъдеще.</string> <string name="face_to_face">Трябва да се срещнете лично с човека, чиито контакт искате да добавите.\n\nТака никой не може да се представи за вас или да чете съобщенията ви в бъдеще.</string>
<string name="continue_button">Напред</string> <string name="continue_button">Напред</string>
<string name="try_again_button">Нов опит</string> <string name="try_again_button">Нов опит</string>
@@ -239,6 +240,7 @@
<string name="authenticating_with_device">Удостоверяване с устройство\u2026</string> <string name="authenticating_with_device">Удостоверяване с устройство\u2026</string>
<string name="connection_error_title">Не може да бъде установена връзка с контакта</string> <string name="connection_error_title">Не може да бъде установена връзка с контакта</string>
<string name="connection_error_feedback">Ако проблемът продължава, <a href="feedback">изпратете обратна връзка</a>, за да ни помогнете да подобрим приложението.</string> <string name="connection_error_feedback">Ако проблемът продължава, <a href="feedback">изпратете обратна връзка</a>, за да ни помогнете да подобрим приложението.</string>
<string name="info_both_must_scan">Трябва взаимно да сканирате кодовете си за QR</string>
<!--Adding Contacts Remotely--> <!--Adding Contacts Remotely-->
<string name="add_contact_remotely_title_case">Добавяне на контакт отдалечено</string> <string name="add_contact_remotely_title_case">Добавяне на контакт отдалечено</string>
<string name="add_contact_nearby_title">Добавяне на контакт на живо</string> <string name="add_contact_nearby_title">Добавяне на контакт на живо</string>
@@ -296,6 +298,7 @@
<string name="different_person_button">Не</string> <string name="different_person_button">Не</string>
<string name="duplicate_link_dialog_text_3">%1$s и %2$s са изпратили еднакви препратки.\n\nЕдиният от двамата вероятно се опитва да разбере кои са контактите ви.\n\nНе им споделяйте, че сте получили същата препратка от друг човек.</string> <string name="duplicate_link_dialog_text_3">%1$s и %2$s са изпратили еднакви препратки.\n\nЕдиният от двамата вероятно се опитва да разбере кои са контактите ви.\n\nНе им споделяйте, че сте получили същата препратка от друг човек.</string>
<string name="pending_contact_updated_toast">Обновена чакаща заявка за контакт</string> <string name="pending_contact_updated_toast">Обновена чакаща заявка за контакт</string>
<string name="info_both_must_enter_links">Трябва взаимно да добавите препратките си</string>
<!--Peer trust levels--> <!--Peer trust levels-->
<string name="peer_trust_level_unverified">Непроверен контакт</string> <string name="peer_trust_level_unverified">Непроверен контакт</string>
<string name="peer_trust_level_verified">Проверен контакт</string> <string name="peer_trust_level_verified">Проверен контакт</string>
@@ -329,6 +332,7 @@
<string name="connect_via_bluetooth_intro">В случай, че връзка чрез Bluetooth не се установява автоматично можете да използвате този екран, за да се свържете ръчно.\n\nВие и контакта трябва да бъдете близо един до друг.\n\nДвамата трябва да докоснете бутона „Старт“ едновременно.</string> <string name="connect_via_bluetooth_intro">В случай, че връзка чрез Bluetooth не се установява автоматично можете да използвате този екран, за да се свържете ръчно.\n\nВие и контакта трябва да бъдете близо един до друг.\n\nДвамата трябва да докоснете бутона „Старт“ едновременно.</string>
<string name="connect_via_bluetooth_already_discovering">Осъществява св връзка през Bluetooth. По-късно опитайте отново.</string> <string name="connect_via_bluetooth_already_discovering">Осъществява св връзка през Bluetooth. По-късно опитайте отново.</string>
<string name="connect_via_bluetooth_no_location_permission">Не може да продължи без разрешение за местоположение</string> <string name="connect_via_bluetooth_no_location_permission">Не може да продължи без разрешение за местоположение</string>
<string name="connect_via_bluetooth_no_bluetooth_permission">Не може да продължи без разрешение за устройства наблизо</string>
<string name="connect_via_bluetooth_start">Свързване чрез Bluetooth…</string> <string name="connect_via_bluetooth_start">Свързване чрез Bluetooth…</string>
<string name="connect_via_bluetooth_success">Успешно е създaдена връзка чрез Bluetooth</string> <string name="connect_via_bluetooth_success">Успешно е създaдена връзка чрез Bluetooth</string>
<string name="connect_via_bluetooth_error">Не може да бъде установена връзка чрез Bluetooth.</string> <string name="connect_via_bluetooth_error">Не може да бъде установена връзка чрез Bluetooth.</string>
@@ -586,10 +590,8 @@
\nСлед това свържете Mailbox с Briar чрез сканиране на кода за QR от приложението Mailbox.</string> \nСлед това свържете Mailbox с Briar чрез сканиране на кода за QR от приложението Mailbox.</string>
<string name="mailbox_setup_download_link">Споделяне на препратка за изтегляне</string> <string name="mailbox_setup_download_link">Споделяне на препратка за изтегляне</string>
<string name="mailbox_setup_button_scan">Сканиране на кода за QR на пощенска кутия</string> <string name="mailbox_setup_button_scan">Сканиране на кода за QR на пощенска кутия</string>
<string name="permission_camera_qr_denied_body">Отказахте достъп до камерата, но тя е необходима за добавянето на контакти.\n\nОбмислете дали да не дадете разрешение.</string> <string name="permission_camera_qr_denied_body">Отказахте достъп до камерата, но достъп е необходим за сканиране на кодове за QR.\n\nОбмислете дали да не дадете разрешение.</string>
<string name="mailbox_setup_connecting">Свързване…</string> <!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_qr_code_wrong_title">Грешен код на QR</string>
<string name="mailbox_setup_qr_code_wrong_description">Сканираният код е недействителен. Отворете приложението Briar Malibox на устройството, на което е инсталирано и сканирайте кода, който то предостави.</string>
<string name="mailbox_setup_already_paired_title">Пощенската кутия е вече свързана</string> <string name="mailbox_setup_already_paired_title">Пощенската кутия е вече свързана</string>
<string name="mailbox_setup_already_paired_description">Прекъснете връзката с пощенската кутия от другото устройство и опитайте отново.</string> <string name="mailbox_setup_already_paired_description">Прекъснете връзката с пощенската кутия от другото устройство и опитайте отново.</string>
<string name="mailbox_setup_io_error_title">Грешка при свързване</string> <string name="mailbox_setup_io_error_title">Грешка при свързване</string>
@@ -718,14 +720,20 @@
<string name="permission_camera_title">Разрешение за камера</string> <string name="permission_camera_title">Разрешение за камера</string>
<string name="permission_camera_request_body">За да сканира кода за QR, Briar трябва да използва камерата.</string> <string name="permission_camera_request_body">За да сканира кода за QR, Briar трябва да използва камерата.</string>
<string name="permission_location_title">Разрешение за местоположение</string> <string name="permission_location_title">Разрешение за местоположение</string>
<string name="permission_location_request_body">За да открива устройства чрез Bluetooth, на Briar му е необходимо разрешение за достъп до местоположението.\n\nBriar не го пази и не го споделя с никого.</string> <string name="permission_location_request_body">За да открива устройства чрез Bluetooth, Briar се нуждае от разрешение за достъп до местоположението.\n\nBriar не го пази и не го споделя с никого.</string>
<string name="permission_camera_location_title">Камера и местоположение</string> <string name="permission_camera_location_title">Камера и местоположение</string>
<string name="permission_camera_location_request_body">За да сканира кодове за QR, на Briar му е необходимо разрешение за достъп до камерата.\n\nЗа да открива устройства чрез Bluetooth, на Briar му е необходимо разрешение за достъп до местоположението.\n\nBriar не го пази и не го споделя с никого.</string> <string name="permission_camera_location_request_body">За да сканира кода за QR, Briar трябва да използва камерата.\n\nЗа да открива устройства чрез Bluetooth, Briar трябва да има достъп до местоположението.\n\nBriar не го пази и не го споделя с никого.</string>
<string name="permission_camera_denied_body">Отказахте достъп до камерата, но тя е необходима за добавянето на контакти.\n\nОбмислете дали да не дадете разрешение.</string> <string name="permission_camera_bluetooth_title">Камера и устройства наблизо</string>
<string name="permission_location_denied_body">Отказахте достъп до местоположението, но то е необходимо за откриване на устройства чрез Bluetooth.\n\nОбмислете дали да не дадете разрешение.</string> <string name="permission_camera_bluetooth_request_body">За да сканира кода за QR, Briar трябва да използва камерата.\n\nЗа да открива устройства чрез Bluetooth, Briar се нуждае от достъп до местоположението. Briar се нуждае от разрешение да намира и да се свързва с устройства наблизо.</string>
<string name="permission_camera_denied_body">Отказахте достъп до камерата, но достъп е необходим за добавяне на контакти.\n\nОбмислете дали да не дадете разрешение.</string>
<string name="permission_location_denied_body">Отказахте достъп до местоположението, но достъп е необходим за откриване на устройства чрез Bluetooth.\n\nОбмислете дали да не дадете разрешение.</string>
<string name="permission_location_setting_title">Настройки на местоположението</string> <string name="permission_location_setting_title">Настройки на местоположението</string>
<string name="permission_location_setting_body">Местоположението на устройството ви трябва да е включено, за да бъдат откривани устройства чрез Bluetooth. За да продължите включете местоположението. След това можете да го изключите.</string> <string name="permission_location_setting_body">За да бъдат откривани устройства чрез Bluetooth местоположението на устройството ви трябва да е включено. За да продължите включете местоположението. След това можете да го изключите.</string>
<string name="permission_location_setting_hotspot_body">За да създаде безжична точка за достъп местоположението на устройството ви трябва да е включено. За да продължите включете местоположението. След това можете да го изключите.</string>
<string name="permission_location_setting_button">Включване на местеположение</string> <string name="permission_location_setting_button">Включване на местеположение</string>
<string name="permission_bluetooth_title">Разрешение за устройства наблизо</string>
<string name="permission_bluetooth_body">За да извършва разговори чрез Bluetooth, Briar се нуждае от разрешение да намира и да се свързва с устройства наблизо.</string>
<string name="permission_bluetooth_denied_body">Отказахте достъп до устройства наблизо, но достъп е необходим за използване на Bluetooth.\n\nОбмислете дали да не дадете разрешение.</string>
<string name="qr_code">Код за QR</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--> <!--App Locking-->
@@ -747,8 +755,10 @@
<string name="hotspot_notification_channel_title">Безжична точка за достъп</string> <string name="hotspot_notification_channel_title">Безжична точка за достъп</string>
<string name="hotspot_notification_title">Споделяне на Briar извън мрежа</string> <string name="hotspot_notification_title">Споделяне на Briar извън мрежа</string>
<string name="hotspot_button_connected">Напред</string> <string name="hotspot_button_connected">Напред</string>
<string name="permission_hotspot_location_request_body">За да създаде безжична точка за достъп, на Briar му е необходимо разрешение за достъп до местоположението.\n\nBriar не го пази и не го споделя с никого.</string> <string name="permission_hotspot_location_request_body">За да създаде безжична точка за достъп, Briar се нуждае от разрешение за достъп до местоположението.\n\nBriar не го пази и не го споделя с никого.</string>
<string name="permission_hotspot_location_denied_body">Отказахте достъп до местоположението, но то е необходимо за създаване на безжична точка за достъп.\n\nОбмислете дали да не дадете разрешение.</string> <string name="permission_hotspot_location_request_precise_body">За да създаде безжична точка за достъп, Briar се нуждае от разрешение за достъп до точното местоположение.\n\nBriar не го пази и не го споделя с никого.</string>
<string name="permission_hotspot_location_denied_body">Отказахте достъп до местоположението, но достъп е необходим за създаване на безжична точка за достъп.\n\nОбмислете дали да не дадете разрешение.</string>
<string name="permission_hotspot_location_denied_precise_body">Отказахте достъп до точното местоположение, но достъп е необходим за създаване на безжична точка за достъп.\n\nОбмислете дали да не дадете разрешение.</string>
<string name="wifi_settings_title">Настройки на Wi-Fi</string> <string name="wifi_settings_title">Настройки на Wi-Fi</string>
<string name="wifi_settings_request_enable_body">За да създаде безжична точка за достъп, Briar се нуждае от безжична мрежа. Включете Wi-Fi.</string> <string name="wifi_settings_request_enable_body">За да създаде безжична точка за достъп, Briar се нуждае от безжична мрежа. Включете Wi-Fi.</string>
<string name="hotspot_tab_manual">Ръчно</string> <string name="hotspot_tab_manual">Ръчно</string>

View File

@@ -1,36 +1,35 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<!--Setup--> <!--Setup-->
<string name="setup_title">Benvingut a Briar</string> <string name="setup_title">Benvingut a Briar</string>
<string name="setup_name_explanation">El vostre sobrenom etiquetarà tot el que publiqueu. Després de crear el compte ja no podreu canviar el sobrenom.</string> <string name="setup_name_explanation">El vostre sobrenom etiquetarà tot el que publiqueu. Després de crear el compte ja no podreu canviar el sobrenom.</string>
<string name="setup_next">Següent</string> <string name="setup_next">Següent</string>
<string name="setup_password_intro">Establiu una contrasenya</string> <string name="setup_password_intro">Establiu una contrasenya</string>
<string name="setup_password_explanation">El compte de Briar s\'emmagatzema xifrat en el vostre dispositiu, no en el núvol. Si oblideu la contrasenya o desinstal·leu Briar no podreu recuperar el vostre compte ni les dades associades.\n\nTrieu una contrasenya llarga que sigui difícil d\'endevinar, com ara quatre paraules aleatòries o deu lletres, números i símbols aleatoris.</string> <string name="setup_password_explanation">El compte de Briar s\'emmagatzema xifrat en el vostre dispositiu, no en el núvol. Si oblideu la contrasenya o desinstal·leu Briar no podreu recuperar el vostre compte ni les dades associades.\n\nTrieu una contrasenya llarga que sigui difícil d\'endevinar, com ara quatre paraules aleatòries o deu lletres, números i símbols aleatoris.</string>
<string name="dnkm_doze_title">Connexions en segon pla</string> <string name="dnkm_doze_intro">Per rebre missatges Briar necessita estar connectat en segon pla.</string>
<string name="dnkm_doze_intro">Per rebre missatges Briar necessita estar connectat en segon pla.</string> <string name="dnkm_doze_explanation">Per rebre missatges Briar necessita estar connectat en segon pla. Desactiveu les optimitzacions de la bateria per permetre que Briar resti sempre connectat.</string>
<string name="dnkm_doze_explanation">Per rebre missatges Briar necessita estar connectat en segon pla. Desactiveu les optimitzacions de la bateria per permetre que Briar resti sempre connectat.</string> <string name="choose_nickname">Trieu el sobrenom</string>
<string name="dnkm_doze_button">Permet connexions</string> <string name="choose_password">Trieu la contrasenya</string>
<string name="choose_nickname">Trieu el sobrenom</string> <string name="confirm_password">Confirmeu la contrasenya</string>
<string name="choose_password">Trieu la contrasenya</string> <string name="name_too_long">El nom és massa llarg</string>
<string name="confirm_password">Confirmeu la contrasenya</string> <string name="password_too_weak">La contrasenya és massa feble</string>
<string name="name_too_long">El nom és massa llarg</string> <string name="passwords_do_not_match">Les contrasenyes no coincideixen</string>
<string name="password_too_weak">La contrasenya és massa feble</string> <string name="create_account_button">Crea el compte</string>
<string name="passwords_do_not_match">Les contrasenyes no coincideixen</string> <string name="more_info">Més informació</string>
<string name="create_account_button">Crea el compte</string> <string name="don_t_ask_again">No tornis a preguntar-ho</string>
<string name="more_info">Més informació</string> <string name="dnkm_huawei_protected_text">Feu un toc sobre el botó següent i assegureu-vos de que Briar consta com a protegit a la pantalla «Aplicacions protegides».</string>
<string name="don_t_ask_again">No tornis a preguntar-ho</string> <string name="dnkm_huawei_protected_button">Protegeix Briar</string>
<string name="dnkm_huawei_protected_text">Feu un toc sobre el botó següent i assegureu-vos de que Briar consta com a protegit a la pantalla «Aplicacions protegides».</string> <string name="dnkm_huawei_protected_help">Si no afegiu Briar a la llista d\'aplicacions protegides, s\'evitarà que Briar s\'executi en segon pla.</string>
<string name="dnkm_huawei_protected_button">Protegeix Briar</string> <string name="dnkm_huawei_app_launch_text">Premeu el botó, obriu la pantalla «Llença app» i assegureu-vos que Briar està configurat com «Gestiona a ma».</string>
<string name="dnkm_huawei_protected_help">Si no afegiu Briar a la llista d\'aplicacions protegides, s\'evitarà que Briar s\'executi en segon pla.</string> <string name="dnkm_huawei_app_launch_help">Si Briar no està configurat com a «Gestiona manualment» a la pantalla «Llença app», no podrà executar-se en segon pla.</string>
<string name="dnkm_huawei_app_launch_text">Premeu el botó, obriu la pantalla «Llença app» i assegureu-vos que Briar està configurat com «Gestiona a ma».</string> <string name="dnkm_xiaomi_text">Per executar-se en segon pla, Briar necessita ser blocat en la llista d\'aplicacions recents.</string>
<string name="dnkm_huawei_app_launch_button">Obriu la configuració de la bateria</string> <string name="dnkm_xiaomi_button">Protegeix Briar</string>
<string name="dnkm_huawei_app_launch_help">Si Briar no està configurat com a «Gestiona manualment» a la pantalla «Llença app», no podrà executar-se en segon pla.</string> <string name="dnkm_xiaomi_help">Si Briar no està blocat a la llista d\'aplicacions recents, no podrà executar-se en segon pla.</string>
<string name="dnkm_xiaomi_text">Per executar-se en segon pla, Briar necessita ser blocat en la llista d\'aplicacions recents.</string> <string name="dnkm_xiaomi_dialog_body_old">1. Obriu la llista d\'aplicacions recents (de canvi d\'aplicació)\n\n2. Desplaceu avall la icona de Briar per mostrar la icona del cadenat\n\n3. Si el cadenat és obert, premeu per tancar-lo.</string>
<string name="dnkm_xiaomi_button">Protegeix Briar</string> <string name="dnkm_xiaomi_dialog_body_new">1. Obriu la llista d\'aplicacions recents (també anomenada commutador d\'aplicacions)\n\n2. Si el Briar té una petita imatge d\'un cadenat al costat del seu nom, no cal que feu res\n\n3. Si no hi ha cadenat, manteniu premuda la imatge de Briar fins que aparegui el botó del cadenat i, a continuació, toqueu-lo</string>
<string name="dnkm_xiaomi_help">Si Briar no està blocat a la llista d\'aplicacions recents, no podrà executar-se en segon pla.</string> <string name="dnkm_xiaomi_lock_apps_text">Toqueu el botó següent per obrir la configuració de seguretat. Toqueu «Augmenta la velocitat» i, a continuació, toqueu «Bloca aplicacions» i assegureu-vos que el Briar estigui configurat a \"Blocat\".</string>
<string name="dnkm_xiaomi_dialog_body_old">1. Obriu la llista d\'aplicacions recents (de canvi d\'aplicació)\n\n2. Desplaceu avall la icona de Briar per mostrar la icona del cadenat\n\n3. Si el cadenat és obert, premeu per tancar-lo.</string> <string name="dnkm_xiaomi_lock_apps_help">Si el Briar no està configurat com a «Blocat» a la pantalla «Bloca aplicacions», no es podrà executar en segon pla.</string>
<string name="dnkm_xiaomi_dialog_body_new">1. Obriu la llista d\'aplicacions recents (canvi d\'aplicació)\n\n2. Premeu la icona de Briar fins que es mostra la icona del cadenat\n\n3. Si el cadenat és obert, premeu per tancar-lo.</string> <string name="dnkm_warning_dozed_1">El Briar no va poder executar-se en segon pla</string>
<string name="dnkm_warning_dozed">%s no s\'ha pogut executar en segon pla</string>
<!--Login--> <!--Login-->
<string name="enter_password">Contrasenya</string> <string name="enter_password">Contrasenya</string>
<string name="try_again">La contrasenya és incorrecta, torneu a escriure-la</string> <string name="try_again">La contrasenya és incorrecta, torneu a escriure-la</string>
@@ -41,14 +40,25 @@
<string name="dialog_title_lost_password">Contrasenya perduda</string> <string name="dialog_title_lost_password">Contrasenya perduda</string>
<string name="dialog_message_lost_password">El vostre compte de Briar s\'emmagatzema només en el vostre dispositiu i xifrat. La contrasenya, doncs, no es pot restablir. Voleu esborrar el compte i crear-ne un de nou?\n\nAtenció! Si esborreu el compte la vostra identitat, els contactes i els missatges antics es perdran per sempre.</string> <string name="dialog_message_lost_password">El vostre compte de Briar s\'emmagatzema només en el vostre dispositiu i xifrat. La contrasenya, doncs, no es pot restablir. Voleu esborrar el compte i crear-ne un de nou?\n\nAtenció! Si esborreu el compte la vostra identitat, els contactes i els missatges antics es perdran per sempre.</string>
<string name="startup_failed_activity_title">Error iniciant Briar</string> <string name="startup_failed_activity_title">Error iniciant Briar</string>
<string name="startup_failed_clock_error">El Briar no s\'ha pogut iniciar perquè l\'hora del dispositiu és incorrecte.\n\nConfigureu l\'hora del dispositiu correctament i torneu-ho a provar.</string>
<string name="startup_failed_db_error">El Briar no ha pogut obrir la base de dades que conté el vostre compte, els vostres contactes i els vostres missatges.\n\nActualitzeu a la darrera versió de l\'aplicació i torneu-ho a provar, o configureu un compte nou seleccionant «He oblidat la meva contrasenya» a la sol·licitud de contrasenya.</string>
<string name="startup_failed_data_too_old_error">El vostre compte s\'ha creat amb una versió antiga d\'aquesta aplicació i no es pot obrir amb aquesta versió.\n\nHeu de tornar a instal·lar la versió antiga o configurar un compte nou escollint «He oblidat la meva contrasenya» a la sol·licitud de contrasenya.</string>
<string name="startup_failed_data_too_new_error">El vostre compte s\'ha creat amb una versió més recent d\'aquesta aplicació i no es pot obrir amb aquesta versió.\n\nActualitzeu a la versió més recent i torneu-ho a provar.</string>
<string name="startup_failed_service_error">El Briar no ha pogut iniciar un component necessari.\n\nActualitzeu l\'aplicació a la darrera versió i torna-ho a provar.</string>
<plurals name="expiry_warning"> <plurals name="expiry_warning">
<item quantity="one">Aquesta és una versió de prova de Briar. El vostre compte expira en %d dia i no es pot renovar.</item> <item quantity="one">Aquesta és una versió de prova de Briar. El vostre compte expira en %d dia i no es pot renovar.</item>
<item quantity="other">Aquesta és una versió de prova de Briar. El vostre compte caducarà en %d dies i no es podrà renovar.</item> <item quantity="other">Aquesta és una versió de prova de Briar. El vostre compte caducarà en %d dies i no es podrà renovar.</item>
</plurals> </plurals>
<plurals name="old_android_expiry_warning">
<item quantity="one">L\'Android 4 ja no és compatible. El Briar deixarà de funcionar a %s (d\'aquí a %d dia). Instal·leu el Briar en un dispositiu més nou i creeu un compte nou.</item>
<item quantity="other">L\'Android 4 ja no és compatible. El Briar deixarà de funcionar a %s (d\'aquí a %d dies). Instal·leu el Briar en un dispositiu més nou i creeu un compte nou.</item>
</plurals>
<string name="expiry_date_reached">Aquesta versió de Briar ha caducat.\nGràcies per haver-lo provat!</string> <string name="expiry_date_reached">Aquesta versió de Briar ha caducat.\nGràcies per haver-lo provat!</string>
<string name="download_briar">Per continuar utilitzant Briar, baixeu la darrera versió.</string> <string name="download_briar">Per continuar utilitzant Briar, baixeu la darrera versió.</string>
<string name="create_new_account">Haureu de crear un compte nou, però podeu utilitzar el mateix sobrenom.</string> <string name="create_new_account">Haureu de crear un compte nou, però podeu utilitzar el mateix sobrenom.</string>
<string name="download_briar_button">Descarrega la darrera versió</string> <string name="download_briar_button">Descarrega la darrera versió</string>
<string name="old_android_expiry_date_reached">El Briar ja no funciona amb l\'Android 4.\nInstal·leu el Briar en un dispositiu més nou.</string>
<string name="old_android_delete_account">Podeu tocar el botó següent per suprimir el vostre compte d\'aquest dispositiu.</string>
<string name="delete_account_button">Esborreu el compte</string> <string name="delete_account_button">Esborreu el compte</string>
<string name="startup_open_database">S\'està desxifrant la base de dades...</string> <string name="startup_open_database">S\'està desxifrant la base de dades...</string>
<string name="startup_migrate_database">S\'està actualitzant la base de dades...</string> <string name="startup_migrate_database">S\'està actualitzant la base de dades...</string>
@@ -145,6 +155,7 @@
<string name="error_start_activity">No està disponible en el vostre sistema</string> <string name="error_start_activity">No està disponible en el vostre sistema</string>
<string name="status_heading">Estat</string> <string name="status_heading">Estat</string>
<string name="error">Error</string> <string name="error">Error</string>
<string name="info">Informació</string>
<!--Contacts and Private Conversations--> <!--Contacts and Private Conversations-->
<string name="no_contacts">No hi ha cap contacte per mostrar</string> <string name="no_contacts">No hi ha cap contacte per mostrar</string>
<string name="no_contacts_action">Toqueu la icona + per afegir un contacte</string> <string name="no_contacts_action">Toqueu la icona + per afegir un contacte</string>
@@ -216,6 +227,7 @@ Així que l\'actualitzi li veureu una icona diferent .</string>
<string name="menu_contact">Contacte</string> <string name="menu_contact">Contacte</string>
<!--Adding Contacts--> <!--Adding Contacts-->
<string name="add_contact_title">Afegeix un contacte proper</string> <string name="add_contact_title">Afegeix un contacte proper</string>
<string name="add_contact_error_two_way">Tots dos heu escanejat el codi QR de l\'altre?</string>
<string name="face_to_face">Heu de coincidir en el mateix lloc amb la persona que voleu afegir com a contacte.\n\nD\'aquesta manera evitareu que algú suplanti les vostres identitats o pugui llegir els vostres missatges en el futur.</string> <string name="face_to_face">Heu de coincidir en el mateix lloc amb la persona que voleu afegir com a contacte.\n\nD\'aquesta manera evitareu que algú suplanti les vostres identitats o pugui llegir els vostres missatges en el futur.</string>
<string name="continue_button">Continua</string> <string name="continue_button">Continua</string>
<string name="try_again_button">Torna-ho a provar</string> <string name="try_again_button">Torna-ho a provar</string>
@@ -224,13 +236,16 @@ Així que l\'actualitzi li veureu una icona diferent .</string>
<string name="contact_added_toast">S\'ha afegit el contacte %s</string> <string name="contact_added_toast">S\'ha afegit el contacte %s</string>
<string name="contact_already_exists">El contacte %s ja existia</string> <string name="contact_already_exists">El contacte %s ja existia</string>
<string name="qr_code_invalid">El codi QR és invàlid</string> <string name="qr_code_invalid">El codi QR és invàlid</string>
<string name="qr_code_too_old">El codi QR que heu escanejat prové d\'una versió antiga de %s.\n\nDemaneu al vostre contacte que l\'actualitzi i torneu a provar-ho.</string> <string name="qr_code_too_old_1">El codi QR que heu escanejat prové d\'una versió anterior del Briar.\n\nDemaneu al vostre contacte que actualitzi a la versió més recent i, a continuació, torneu-ho a provar.</string>
<string name="qr_code_too_new">El codi QR que heu escanejat prové d\'una versió de %s més nova que la vostra.\n\nActualitzeu la vostra aplicació i torneu a provar-ho.</string> <string name="qr_code_too_new_1">El codi QR que heu escanejat prové d\'una versió més recent del Briar.\n\nActualitzeu a la darrera versió i torneu-ho a provar.</string>
<string name="mailbox_qr_code_for_contact">El codi QR que heu escanejat prové del Briar Mailbox.\n\nSi voleu enllaçar una bústia de correu, seleccioneu Configuració &gt; Bústia del menú Briar.</string>
<string name="qr_code_format_unknown">El codi QR que heu escanejat no serveix per afegir un contacte del Briar.\n\nEscanejeu el codi QR que es mostra a la pantalla del vostre contacte.</string>
<string name="camera_error">Error de la càmera</string> <string name="camera_error">Error de la càmera</string>
<string name="connecting_to_device">Connectant-se al dispositiu\u2026</string> <string name="connecting_to_device">Connectant-se al dispositiu\u2026</string>
<string name="authenticating_with_device">Autenticant-se amb el dispositiu\u2026</string> <string name="authenticating_with_device">Autenticant-se amb el dispositiu\u2026</string>
<string name="connection_error_title">No ha pogut connectar-se al vostre contacte</string> <string name="connection_error_title">No ha pogut connectar-se al vostre contacte</string>
<string name="connection_error_feedback">Si aquest problema persisteix, <a href="feedback">envieu-nos un comentari</a> per ajudar-nos a millorar l\'aplicació.</string> <string name="connection_error_feedback">Si aquest problema persisteix, <a href="feedback">envieu-nos un comentari</a> per ajudar-nos a millorar l\'aplicació.</string>
<string name="info_both_must_scan">Tots dos heu d\'escanejar el codi QR de l\'altre</string>
<!--Adding Contacts Remotely--> <!--Adding Contacts Remotely-->
<string name="add_contact_remotely_title_case">Afegeix un contacte llunyà</string> <string name="add_contact_remotely_title_case">Afegeix un contacte llunyà</string>
<string name="add_contact_nearby_title">Afegeix un contacte proper</string> <string name="add_contact_nearby_title">Afegeix un contacte proper</string>
@@ -277,6 +292,7 @@ Així que l\'actualitzi li veureu una icona diferent .</string>
<string name="duplicate_link_dialog_text_1">Teniu una sol·licitud de contacte pendent amb l\'enllaç %s</string> <string name="duplicate_link_dialog_text_1">Teniu una sol·licitud de contacte pendent amb l\'enllaç %s</string>
<string name="duplicate_link_dialog_text_1_contact">Ja teniu un contacte amb aquest enllaç: %s</string> <string name="duplicate_link_dialog_text_1_contact">Ja teniu un contacte amb aquest enllaç: %s</string>
<!--This is a question asking whether two nicknames refer to the same person--> <!--This is a question asking whether two nicknames refer to the same person-->
<string name="duplicate_link_dialog_text_2">%1$s i %2$s són la mateixa persona?</string>
<!--This is a button for answering that two nicknames do indeed refer to the same person. This <!--This is a button for answering that two nicknames do indeed refer to the same person. This
string will be used in a dialog button, so if the translation of this string is longer than 20 string will be used in a dialog button, so if the translation of this string is longer than 20
characters, please use "Yes" instead, and use "No" for the "Different Person" button--> characters, please use "Yes" instead, and use "No" for the "Different Person" button-->
@@ -285,10 +301,17 @@ Així que l\'actualitzi li veureu una icona diferent .</string>
will be used in a dialog button, so if the translation of this string longer than 20 characters, will be used in a dialog button, so if the translation of this string longer than 20 characters,
please use "No" instead, and use "Yes" for the "Same Person" button--> please use "No" instead, and use "Yes" for the "Same Person" button-->
<string name="different_person_button">Persones diferents</string> <string name="different_person_button">Persones diferents</string>
<string name="duplicate_link_dialog_text_3">%1$s i %2$s us han enviat el mateix enllaç.\n\nÉs possible que un d\'ells estigui intentant descobrir qui són els teus contactes.\n\nNo els diguis que has rebut el mateix enllaç d\'una altra persona.</string>
<string name="pending_contact_updated_toast">S\'ha actualitzat la sol·licitud de contacte pendent</string> <string name="pending_contact_updated_toast">S\'ha actualitzat la sol·licitud de contacte pendent</string>
<string name="info_both_must_enter_links">Tots dos heu d\'afegir l\'enllaç de l\'altre</string>
<!--Peer trust levels-->
<string name="peer_trust_level_unverified">Contacte no verificat</string>
<string name="peer_trust_level_verified">Contacte verificat</string>
<string name="peer_trust_level_ourselves">Jo</string>
<string name="peer_trust_level_stranger">Estrany</string>
<!--Introductions--> <!--Introductions-->
<string name="introduction_onboarding_title">Presenteu als vostres contactes</string> <string name="introduction_onboarding_title">Presenteu als vostres contactes</string>
<string name="introduction_onboarding_text">Podeu presentar als vostres contactes entre si, de manera que no necessitin trobar-se en persona per a relacionar-se a través de Briar.</string> <string name="introduction_onboarding_text">Presenteu els vostres contactes entre ells perquè es puguin connectar al Briar.</string>
<string name="introduction_menu_item">Presenta-li un altre contacte</string> <string name="introduction_menu_item">Presenta-li un altre contacte</string>
<string name="introduction_activity_title">Trieu un contacte</string> <string name="introduction_activity_title">Trieu un contacte</string>
<string name="introduction_not_possible">Ja teniu una presentació en marxa entre aquests contactes. Si us plau, primer deixeu que aquesta presentació acabi. Si vós o els contactes presentats sovint esteu desconnectats, la presentació pot trigar temps.</string> <string name="introduction_not_possible">Ja teniu una presentació en marxa entre aquests contactes. Si us plau, primer deixeu que aquesta presentació acabi. Si vós o els contactes presentats sovint esteu desconnectats, la presentació pot trigar temps.</string>
@@ -311,9 +334,14 @@ Així que l\'actualitzi li veureu una icona diferent .</string>
<!--Connect via Bluetooth--> <!--Connect via Bluetooth-->
<string name="menu_item_connect_via_bluetooth">Connecta via bluetooth</string> <string name="menu_item_connect_via_bluetooth">Connecta via bluetooth</string>
<string name="connect_via_bluetooth_title">Connecta via bluetooth</string> <string name="connect_via_bluetooth_title">Connecta via bluetooth</string>
<string name="connect_via_bluetooth_intro">En cas que les connexions Bluetooth no funcionin automàticament, podeu utilitzar aquesta pantalla per connectar-vos manualment.\n\nEl vostre contacte ha d\'estar a prop perquè això funcioni.\n\nVós i el vostre contacte hauríeu de prémer «Inicia» alhora.</string>
<string name="connect_via_bluetooth_already_discovering">Ja s\'està provant de connectar-se mitjançant Bluetooth. Torneu-ho a provar més tard.</string>
<string name="connect_via_bluetooth_no_location_permission">No es pot continuar sense permís per obtenir la posició.</string> <string name="connect_via_bluetooth_no_location_permission">No es pot continuar sense permís per obtenir la posició.</string>
<string name="connect_via_bluetooth_no_bluetooth_permission">No es pot continuar sense el permís de dispositius propers</string>
<string name="connect_via_bluetooth_start">Connectant-se via Bluetooth...</string> <string name="connect_via_bluetooth_start">Connectant-se via Bluetooth...</string>
<string name="connect_via_bluetooth_success">S\'ha connectat via Bluetooth.</string> <string name="connect_via_bluetooth_success">S\'ha connectat via Bluetooth.</string>
<string name="connect_via_bluetooth_error">No s\'ha pogut connectar mitjançant Bluetooth.</string>
<string name="connect_via_bluetooth_error_not_supported">El Bluetooth no és compatible amb el dispositiu.</string>
<!--Private Groups--> <!--Private Groups-->
<string name="groups_list_empty">No hi ha cap grup per mostrar</string> <string name="groups_list_empty">No hi ha cap grup per mostrar</string>
<string name="groups_list_empty_action">Toqueu la icona + per crear un grup o demaneu als vostres contactes que us afegeixin als seus grups</string> <string name="groups_list_empty_action">Toqueu la icona + per crear un grup o demaneu als vostres contactes que us afegeixin als seus grups</string>
@@ -406,6 +434,10 @@ Així que l\'actualitzi li veureu una icona diferent .</string>
<string name="forum_declined_toast">Heu refusat la invitació</string> <string name="forum_declined_toast">Heu refusat la invitació</string>
<string name="shared_by_format">Compartit per %s</string> <string name="shared_by_format">Compartit per %s</string>
<string name="forum_invitation_already_sharing">Ja l\'esteu compartint</string> <string name="forum_invitation_already_sharing">Ja l\'esteu compartint</string>
<string name="forum_invitation_already_invited">La invitació ja s\'ha enviat</string>
<string name="forum_invitation_invite_received">La invitació ja s\'ha rebut</string>
<string name="forum_invitation_not_supported">Aquest contacte no és compatible</string>
<string name="forum_invitation_error">Això és un error i no és culpa vostre</string>
<string name="forum_invitation_response_accepted_sent">Heu acceptat la invitació al fòrum enviada per %s.</string> <string name="forum_invitation_response_accepted_sent">Heu acceptat la invitació al fòrum enviada per %s.</string>
<string name="forum_invitation_response_declined_sent">Heu refusat la invitació al fòrum enviada per %s.</string> <string name="forum_invitation_response_declined_sent">Heu refusat la invitació al fòrum enviada per %s.</string>
<string name="forum_invitation_response_declined_auto">La invitació al blog de %s s\'ha declinat automàticament.</string> <string name="forum_invitation_response_declined_auto">La invitació al blog de %s s\'ha declinat automàticament.</string>
@@ -560,11 +592,104 @@ Així que l\'actualitzi li veureu una icona diferent .</string>
<string name="choose_ringtone_title">Trieu el so d\'avís</string> <string name="choose_ringtone_title">Trieu el so d\'avís</string>
<string name="cannot_load_ringtone">No s\'ha pogut carregar el so d\'avís</string> <string name="cannot_load_ringtone">No s\'ha pogut carregar el so d\'avís</string>
<!--Mailbox--> <!--Mailbox-->
<string name="mailbox_setup_connecting">S\'està connectant...</string> <string name="mailbox_settings_title">Bústia</string>
<string name="mailbox_setup_title">Configuració de la bústia</string>
<string name="mailbox_setup_intro">Una bústia permet als vostres contactes enviar-vos missatges mentre esteu fora de línia. La bústia rebrà els vostres missatges i els emmagatzemarà fins que us connecteu.\n
\nPodeu instal·lar l\'aplicació Briar Mailbox en un dispositiu de recanvi. Mantingueu-lo connectat a l\'alimentació i a la Wi-Fi perquè estigui sempre en línia.</string>
<string name="mailbox_setup_download">Primer, instal·leu l\'aplicació Mailbox en un altre dispositiu cercant «Briar Mailbox» a Google Play o allà on hageu baixat el Briar.\n
\nA continuació, enllaceu la vostra bústia amb Briar escanejant el codi QR que mostra l\'aplicació Mailbox.</string>
<string name="mailbox_setup_download_link">Comparteix l\'enllaç de baixada</string>
<string name="mailbox_setup_button_scan">Escaneja el codi QR de la bústia</string>
<string name="permission_camera_qr_denied_body">Heu denegat l\'accés a la càmera, però per escanejar un codi QR cal utilitzar-la.\n\nConsidereu concedir l\'accés.</string>
<string name="mailbox_setup_connecting">S\'està connectant a la bústia...</string>
<!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_connecting_info">Això pot trigar fins a %1s</string>
<string name="mailbox_qr_code_too_old">El codi QR que heu escanejat prové d\'una versió anterior del Briar Mailbox.\n\nActualitzeu el Briar Mailbox a la darrera versió i torneu-ho a provar.</string>
<string name="mailbox_qr_code_too_new">El codi QR que heu escanejat prové d\'una versió més recent de Briar Mailbox.\n\nActualitzeu el Briar a la darrera versió i torneu-ho a provar.</string>
<string name="contact_qr_code_for_mailbox">El codi QR que heu escanejat serveix per afegir un contacte del Briar.\n\nSi voleu afegir un contacte, aneu a la llista de contactes i toqueu la icona +.</string>
<string name="mailbox_setup_qr_code_wrong_description">El codi QR que heu escanejat no prové del Briar Mailbox.\n\nObriu l\'aplicació Briar Mailbox al dispositiu Mailbox i escanegeu el codi QR que hi apareix.</string>
<string name="mailbox_setup_already_paired_title">La bústia ja està enllaçada</string>
<string name="mailbox_setup_already_paired_description">Desenllaceu la bústia de l\'altre dispositiu i torneu-ho a provar.</string>
<string name="mailbox_setup_io_error_title">No s\'ha pogut connectar</string>
<string name="mailbox_setup_io_error_description">Assegureu-vos que tots dos dispositius estiguin connectats a Internet i torneu-ho a provar.</string>
<string name="mailbox_setup_assertion_error_title">Error de bústia</string>
<string name="mailbox_setup_assertion_error_description">Envieu comentaris (amb dades anònimes) mitjançant l\'aplicació Briar si el problema persisteix.</string>
<string name="mailbox_setup_camera_error_description">No s\'ha pogut accedir a la càmera. Torneu-ho a provar després de reiniciar el dispositiu.</string>
<string name="mailbox_setup_paired_title">Connectat</string>
<string name="mailbox_setup_paired_description">La vostra bústia s\'ha enllaçat correctament amb el Briar.\n
\nMantingueu la vostra bústia connectada a l\'alimentació i a la Wi-Fi perquè estigui sempre en línia.</string>
<string name="tor_offline_title">Fora de línia</string> <string name="tor_offline_title">Fora de línia</string>
<string name="tor_offline_description">Assegureu-vos que aquest dispositiu estigui en línia i que es permeten les connexions a Internet.\n
\nDesprés, espereu que la icona del globus terraqüi a la pantalla de configuració de connexió es torni verda.</string>
<string name="tor_offline_button_check">Comproveu la configuració de la connexió</string>
<string name="mailbox_status_title">Estat de la bústia</string>
<string name="mailbox_status_connected_title">La bústia s\'està executant</string>
<string name="mailbox_status_problem_title">El Briar té problemes per connectar-se a la bústia</string>
<string name="mailbox_status_failure_title">La bústia no està disponible</string>
<string name="mailbox_status_app_too_old_title">En Briar és massa antic</string>
<string name="mailbox_status_app_too_old_message">Actualitzeu el Briar a la darrera versió i torneu-ho a provar.</string>
<string name="mailbox_status_mailbox_too_old_title">La bústia és massa antiga</string>
<string name="mailbox_status_mailbox_too_old_message">Actualitza la vostra bústia a la darrera versió i torneu-ho a provar.</string>
<string name="mailbox_status_check_button">Comprova la connexió</string>
<!--Example for string substitution: Last connection: 3min ago-->
<string name="mailbox_status_connected_info">Última connexió: %s</string>
<!--Indicates that there never was a connection to the mailbox. Last connection: Never-->
<string name="mailbox_status_connected_never">Mai</string>
<string name="mailbox_status_unlink_button">Desenllaça</string>
<string name="mailbox_status_unlink_dialog_title">Voleu desenllaçar la bústia?</string>
<string name="mailbox_status_unlink_dialog_question">Esteu segur que voleu desenllaçar la vostra bústia?</string>
<string name="mailbox_status_unlink_dialog_warning">Si desenllaçeu la vostra bústia no podreu rebre missatges mentre el Briar estigui fora de línia.</string>
<string name="mailbox_status_unlink_no_wipe_title">La vostra bústia s\'ha desenllaçat</string>
<string name="mailbox_status_unlink_no_wipe_message">La propera vegada que tingueu accés al vostre dispositiu Mailbox, obriu l\'aplicació Mailbox i toqueu el botó «Desenllaça» per completar el procés.\n\nSi ja no teniu accés al dispositiu Mailbox, no us preocupeu. Les vostres dades estan xifrades, de manera que es mantindran segures encara que no completeu el procés.</string>
<string name="mailbox_status_unlink_success">La vostra bústia s\'ha desenllaçat</string>
<string name="mailbox_error_notification_channel_title">Problema amb la bústia Briar</string>
<string name="mailbox_error_notification_title">La bústia Briar no està disponible</string>
<string name="mailbox_error_notification_text">Toqueu per solucionar el problema.</string>
<string name="mailbox_error_wizard_button">Soluciona el problema</string>
<string name="mailbox_error_wizard_title">Assistent per a la resolució de problemes de la bústia</string>
<string name="mailbox_error_wizard_question1">Teniu accés al vostre dispositiu Mailbox?</string>
<string name="mailbox_error_wizard_answer1">Sí, hi tinc accés ara mateix.</string>
<string name="mailbox_error_wizard_answer2">Ara mateix no, però hi puc accedir més tard.</string>
<string name="mailbox_error_wizard_answer3">No, ja no hi tinc accés.</string>
<string name="mailbox_error_wizard_info1_1">Comproveu que el dispositiu Mailbox estigui encès i connectat a Internet.</string>
<string name="mailbox_error_wizard_question1_1">Obriu l\'aplicació Mailbox. Què veus?</string>
<string name="mailbox_error_wizard_answer1_1">Veig instruccions per configurar la bústia</string>
<string name="mailbox_error_wizard_answer1_2">Veig un codi QR</string>
<string name="mailbox_error_wizard_answer1_3">Veig «El Mailbox s\'està executant»</string>
<string name="mailbox_error_wizard_answer1_4">Veig «Dispositiu fora de línia»</string>
<string name="mailbox_error_wizard_info1_1_1">Desenllaceu la vostra bústia mitjançant el botó següent i, a continuació, seguiu les instruccions del dispositiu Mailbox per enllaçar-la de nou.</string>
<string name="mailbox_error_wizard_info_1_1_2">Desenllaceu la bústia amb el botó següent i escanegeu el codi QR per tornar-la a enllaçar.</string>
<string name="mailbox_error_wizard_info1_1_3">Feu servir el botó següent per comprovar la connexió entre Briar i el Mailbox.\n\n
Si la connexió torna a fallar:\n
\u2022 Comproveu que les aplicacions Mailbox i Briar estiguin actualitzades a la darrera versió.\n
\u2022 Reinicieu els vostres dispositius Mailbox i Briar i torneu-ho a provar.</string>
<string name="mailbox_error_wizard_info1_1_4">Comproveu que el dispositiu Mailbox estigui connectat correctament a Internet.\n\nComproveu que l\'hora, data i zona horària del dispositiu Mailbox siguin correctes.\n\nComproveu que les aplicacions Mailbox i Briar estiguin actualitzades a la darrera versió.\ n\nReinicieu els vostres dispositius Mailbox i Briar i torneu-ho a provar.</string>
<string name="mailbox_error_wizard_info2">Torneu a aquesta pantalla quan tingueu accés al dispositiu.</string>
<string name="mailbox_error_wizard_info3">Desenllaceu la vostra bústia amb el botó següent.\n\nDesprés de desenllaçar la vostra bústia antiga, podeu configurar una bústia nova en qualsevol moment.</string>
<!--About-->
<string name="about_title">Quant a</string>
<string name="briar_version">Versió del Briar: %s</string>
<string name="tor_version">Versió del Tor: %s</string>
<string name="links">Enllaços</string>
<string name="briar_website">\u2022 <a href="">Lloc web</a></string>
<string name="briar_source_code">\u2022 <a href="">Codi font</a></string>
<string name="briar_changelog">\u2022 <a href="">Registre de canvis</a></string>
<string name="briar_privacy_policy">\u2022 <a href="">Política de privadesa</a></string>
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
<string name="translator_thanks">Gràcies a tots els col·laboradors del Localization Lab, especialment a Jaime Muñoz</string>
<!--Conversation Settings--> <!--Conversation Settings-->
<string name="disappearing_messages_title">Missatges efímers</string> <string name="disappearing_messages_title">Missatges efímers</string>
<string name="disappearing_messages_explanation_long">Si activeu aquesta configuració, es crearà un/a nou/va
els missatges d\'aquesta conversa desapareixen automàticament al cap de 7\u00A0dies.
\n\nEl compte enrere per a la còpia del missatge del remitent comença després d\'haver estat lliurat.
El compte enrere comença per al destinatari després que hagi llegit el missatge.
\n\nEls missatges que desapareixeran estan marcats amb una icona de bomba.
\n\nTingues en compte que els destinataris encara poden fer còpies dels missatges que envieu.
\n\nSi canvieu aquesta configuració, s\'aplicarà als vostres missatges nous immediatament i als vostres
missatges del contacte un cop rebin el vostre següent missatge.
El vostre contacte també pot canviar aquesta configuració per a tots dos.</string>
<string name="learn_more">Més informació</string> <string name="learn_more">Més informació</string>
<string name="disappearing_messages_summary">Feu que els missatges futurs d\'aquesta conversa desapareguin automàticament al cap de 7\u00A0dies.</string>
<!--Settings Actions--> <!--Settings Actions-->
<string name="pref_category_actions">Accions</string> <string name="pref_category_actions">Accions</string>
<string name="send_feedback">Envieu comentaris</string> <string name="send_feedback">Envieu comentaris</string>
@@ -575,6 +700,7 @@ Així que l\'actualitzi li veureu una icona diferent .</string>
<string name="link_warning_open_link">Obre l\'enllaç</string> <string name="link_warning_open_link">Obre l\'enllaç</string>
<!--Crash Reporter--> <!--Crash Reporter-->
<string name="crash_report_title">Informe de fallida de Briar</string> <string name="crash_report_title">Informe de fallida de Briar</string>
<string name="briar_crashed">El Briar ha fallat</string>
<string name="not_your_fault">Això no és culpa vostra.</string> <string name="not_your_fault">Això no és culpa vostra.</string>
<string name="please_send_report">Ajudi\'ns a construir un Briar millor enviant-nos un informe de fallida.</string> <string name="please_send_report">Ajudi\'ns a construir un Briar millor enviant-nos un informe de fallida.</string>
<string name="report_is_encrypted">Us garantim que l\'informe es xifra i s\'envia de manera segura.</string> <string name="report_is_encrypted">Us garantim que l\'informe es xifra i s\'envia de manera segura.</string>
@@ -582,19 +708,35 @@ Així que l\'actualitzi li veureu una icona diferent .</string>
<string name="describe_crash">Descriviu el que hi ha succeït (opcional)</string> <string name="describe_crash">Descriviu el que hi ha succeït (opcional)</string>
<string name="enter_feedback">Escriviu els vostres comentaris</string> <string name="enter_feedback">Escriviu els vostres comentaris</string>
<string name="optional_contact_email">La vostra adreça de correu (opcional)</string> <string name="optional_contact_email">La vostra adreça de correu (opcional)</string>
<string name="privacy_policy">En enviar-nos dades, accepteu la nostra <a href="">política de privadesa</a></string>
<string name="include_debug_report_crash">Inclou dades anònimes sobre la fallida</string> <string name="include_debug_report_crash">Inclou dades anònimes sobre la fallida</string>
<string name="include_debug_report_feedback">Inclou dades anònimes sobre el dispositiu</string> <string name="include_debug_report_feedback">Inclou dades anònimes sobre el dispositiu</string>
<string name="dev_report_user_info">Informació de l\'usuari</string>
<string name="dev_report_basic_info">Informació bàsica</string> <string name="dev_report_basic_info">Informació bàsica</string>
<string name="dev_report_device_info">Informació del dispositiu</string>
<string name="dev_report_stacktrace">Traça de la pila</string>
<string name="dev_report_time_info">Informació del temps</string>
<string name="dev_report_memory">Memòria</string>
<string name="dev_report_storage">Emmagatzematge</string> <string name="dev_report_storage">Emmagatzematge</string>
<string name="dev_report_connectivity">Connectivitat</string>
<string name="dev_report_network_usage">Ús de la xarxa</string>
<string name="dev_report_build_config">Configuració de la compilació</string>
<string name="dev_report_logcat">Registre de l\'aplicació</string>
<string name="dev_report_device_features">Característiques del dispositiu</string>
<string name="send_report">Envia l\'informe</string> <string name="send_report">Envia l\'informe</string>
<string name="close">Tanca</string> <string name="close">Tanca</string>
<string name="dev_report_sending">S\'està enviant els comentaris...</string>
<string name="dev_report_sent">Comentaris enviats</string>
<string name="dev_report_saved">S\'ha desat l\'informe. Se us enviarà la propera vegada que inicieu sessió a Briar.</string> <string name="dev_report_saved">S\'ha desat l\'informe. Se us enviarà la propera vegada que inicieu sessió a Briar.</string>
<string name="dev_report_error">Error: no s\'ha pogut enviar l\'informe</string>
<!--Sign Out--> <!--Sign Out-->
<string name="progress_title_logout">S\'està tancant la sessió de Briar...</string> <string name="progress_title_logout">S\'està tancant la sessió de Briar...</string>
<!--Screen Filters & Tapjacking--> <!--Screen Filters & Tapjacking-->
<string name="screen_filter_title">S\'ha detectat superposició de la pantalla</string> <string name="screen_filter_title">S\'ha detectat superposició de la pantalla</string>
<string name="screen_filter_body">Una altra aplicació es troba damunt de Briar. Per protegir la vostra seguretat, Briar no respondrà a les pulsacions quan una altra aplicació s\'hi hagi sobreposat.\n\nLes següents aplicacions poden estar sobreposades a Briar:\n\n%1$s</string> <string name="screen_filter_body">Una altra aplicació es troba damunt de Briar. Per protegir la vostra seguretat, Briar no respondrà a les pulsacions quan una altra aplicació s\'hi hagi sobreposat.\n\nLes següents aplicacions poden estar sobreposades a Briar:\n\n%1$s</string>
<string name="screen_filter_body_api_30">Una altra aplicació està dibuixant a sobre del Briar. Per protegir la vostra seguretat, el Briar no respondrà als tocs quan hi hagi una altra aplicació dibuixant a sobre.\n\nReviseu les aplicacions a continuació per trobar l\'aplicació responsable.</string>
<string name="screen_filter_allow">Permet que aquestes aplicacions se sobreposin a Briar</string> <string name="screen_filter_allow">Permet que aquestes aplicacions se sobreposin a Briar</string>
<string name="screen_filter_review_apps">Revisa les aplicacions</string>
<!--Permission Requests--> <!--Permission Requests-->
<string name="permission_camera_title">Permís de la càmera</string> <string name="permission_camera_title">Permís de la càmera</string>
<string name="permission_camera_request_body">Per escanejar el codi QR, Briar necessita accedir a la càmera.</string> <string name="permission_camera_request_body">Per escanejar el codi QR, Briar necessita accedir a la càmera.</string>
@@ -602,7 +744,17 @@ Així que l\'actualitzi li veureu una icona diferent .</string>
<string name="permission_location_request_body">Per a descobrir dispositius Bluetooth, Briar necessita accedir a la vostra posició.\n\nBriar no guarda la vostra posició ni la comparteix amb ningú.</string> <string name="permission_location_request_body">Per a descobrir dispositius Bluetooth, Briar necessita accedir a la vostra posició.\n\nBriar no guarda la vostra posició ni la comparteix amb ningú.</string>
<string name="permission_camera_location_title">Permís d\'accés a la càmera i a la posició</string> <string name="permission_camera_location_title">Permís d\'accés a la càmera i a la posició</string>
<string name="permission_camera_location_request_body">Per escanejar el codi QR, Briar necessita accedir a la càmera.\n\nPer trobar dispositius Bluetooth, Briar necessita accedir a la vostra posició.\n\nBriar no guarda la vostra posició ni la comparteix amb ningú.</string> <string name="permission_camera_location_request_body">Per escanejar el codi QR, Briar necessita accedir a la càmera.\n\nPer trobar dispositius Bluetooth, Briar necessita accedir a la vostra posició.\n\nBriar no guarda la vostra posició ni la comparteix amb ningú.</string>
<string name="permission_camera_bluetooth_title">Càmera i dispositius propers</string>
<string name="permission_camera_bluetooth_request_body">Per escanejar el codi QR, el Briar necessita accedir a la càmera.\n\nPer descobrir dispositius Bluetooth, el Briar necessita permís per trobar i connectar-se a dispositius propers.</string>
<string name="permission_camera_denied_body">Heu denegat l\'accés a la càmera però per afegir contactes cal utilitzar la càmera.\n\nRecomanem que permeteu l\'accés a la càmera.</string> <string name="permission_camera_denied_body">Heu denegat l\'accés a la càmera però per afegir contactes cal utilitzar la càmera.\n\nRecomanem que permeteu l\'accés a la càmera.</string>
<string name="permission_location_denied_body">Heu denegat l\'accés a la vostra ubicació, però el Briar necessita aquest permís per descobrir dispositius Bluetooth.\n\nPenseu en concedir l\'accés.</string>
<string name="permission_location_setting_title">Configuració d\'ubicació</string>
<string name="permission_location_setting_body">La configuració d\'ubicació del dispositiu ha d\'estar activada per trobar altres dispositius mitjançant Bluetooth. Activeu la ubicació per continuar. Podeu tornar a desactivar-lo després.</string>
<string name="permission_location_setting_hotspot_body">La configuració d\'ubicació del dispositiu s\'ha d\'activar per crear un punt d\'accés Wi-Fi. Activeu la ubicació per continuar. Podeu tornar a desactivar-lo després.</string>
<string name="permission_location_setting_button">Activa la ubicació</string>
<string name="permission_bluetooth_title">Permís de dispositius propers</string>
<string name="permission_bluetooth_body">Per utilitzar la comunicació Bluetooth, el Briar necessita permís per trobar i connectar-se a dispositius propers.</string>
<string name="permission_bluetooth_denied_body">Heu denegat l\'accés als dispositius propers, però el Briar necessita aquest permís per utilitzar Bluetooth.\n\nPenseu en concedir l\'accés.</string>
<string name="qr_code">Codi QR</string> <string name="qr_code">Codi QR</string>
<string name="show_qr_code_fullscreen">Mostra el codi QR a pantalla completa</string> <string name="show_qr_code_fullscreen">Mostra el codi QR a pantalla completa</string>
<!--App Locking--> <!--App Locking-->
@@ -615,17 +767,92 @@ Així que l\'actualitzi li veureu una icona diferent .</string>
<!--Connections Screen--> <!--Connections Screen-->
<string name="transports_help_text">Briar pot contactar els vostres contactes via Internet, WiFi o Bluetooth.\n\nTotes les connexions d\'Internet van via la xarxa Tor per privacitat.\n\nSi es pot arribar a un contacte per diversos mètodes, Briar els usa tots simultàniament.</string> <string name="transports_help_text">Briar pot contactar els vostres contactes via Internet, WiFi o Bluetooth.\n\nTotes les connexions d\'Internet van via la xarxa Tor per privacitat.\n\nSi es pot arribar a un contacte per diversos mètodes, Briar els usa tots simultàniament.</string>
<!--Share app offline--> <!--Share app offline-->
<string name="hotspot_title">Comparteix aquesta aplicació fora de línia</string>
<string name="hotspot_intro">Compartiu aquesta aplicació amb algú proper sense connexió a Internet mitjançant la Wi-Fi del telèfon.
\n\nEl vostre telèfon iniciarà un punt d\'accés Wi-Fi. Les persones properes poden connectar-se al punt d\'accés i baixar l\'aplicació Briar des del telèfon.</string>
<string name="hotspot_button_start_sharing">Inicia el punt d\'accés</string>
<string name="hotspot_button_stop_sharing">Atura el punt d\'accés</string>
<string name="hotspot_progress_text_start">S\'està configurant el punt d\'accés...</string>
<string name="hotspot_notification_channel_title">Punt d\'accés Wi-Fi</string> <string name="hotspot_notification_channel_title">Punt d\'accés Wi-Fi</string>
<string name="hotspot_notification_title">Compartint el Briar fora de línia</string>
<string name="hotspot_button_connected">Endavant</string> <string name="hotspot_button_connected">Endavant</string>
<string name="permission_hotspot_location_request_body">Per crear un punt d\'accés Wi-Fi, el Briar necessita permís per accedir a la vostra ubicació.\n\nel Briar no emmagatzema la vostra ubicació ni la comparteix amb ningú.</string>
<string name="permission_hotspot_location_request_precise_body">Per crear un punt d\'accés Wi-Fi, el Briar necessita permís per accedir a la vostra ubicació precisa.\n\nel Briar no emmagatzema la vostra ubicació ni la comparteix amb ningú.</string>
<string name="permission_hotspot_location_denied_body">Heu denegat l\'accés a la vostra ubicació, però el Briar necessita aquest permís per crear un punt d\'accés Wi-Fi.\n\nPenseu en concedir l\'accés.</string>
<string name="permission_hotspot_location_denied_precise_body">Heu denegat l\'accés a la vostra ubicació precisa, però el Briar necessita aquest permís per crear un punt d\'accés Wi-Fi.\n\nPenseu en concedir l\'accés.</string>
<string name="wifi_settings_title">Configuració Wi-Fi</string>
<string name="wifi_settings_request_enable_body">Per crear un punt d\'accés Wi-Fi, el Briar ha d\'utilitzar Wi-Fi. Activeu-lo.</string>
<string name="hotspot_tab_manual">Manual</string>
<!--The placeholder to be inserted into the string 'hotspot_manual_wifi': People can connect by %s--> <!--The placeholder to be inserted into the string 'hotspot_manual_wifi': People can connect by %s-->
<string name="hotspot_scanning_a_qr_code">escanejant un codi QR</string>
<!--Wi-Fi setup--> <!--Wi-Fi setup-->
<!--The %s placeholder will be replaced with the translation of 'hotspot_scanning_a_qr_code'--> <!--The %s placeholder will be replaced with the translation of 'hotspot_scanning_a_qr_code'-->
<string name="hotspot_manual_wifi">El vostre telèfon proporciona un punt d\'accés Wi-Fi. Les persones que vulguin baixar el Briar es poden connectar al punt d\'accés afegint-lo a la configuració Wi-Fi del seu dispositiu mitjançant els detalls següents o mitjançant %s. Quan s\'hagin connectat al punt d\'accés, premeu «Següent».</string>
<string name="hotspot_manual_wifi_ssid">Nom de la xarxa</string>
<string name="hotspot_qr_wifi">El vostre telèfon proporciona un punt d\'accés Wi-Fi. Les persones que vulguin baixar el Briar poden connectar-se al punt d\'accés escanejant aquest codi QR. Quan s\'hagin connectat al punt d\'accés, premeu «Següent».</string>
<string name="hotspot_no_peers_connected">No hi ha cap dispositiu connectat</string>
<plurals name="hotspot_peers_connected">
<item quantity="one">%s dispositiu connectat</item>
<item quantity="other">%s dispositius connectats</item>
</plurals>
<!--Download link--> <!--Download link-->
<!--The %s placeholder will be replaced with the translation of 'hotspot_scanning_a_qr_code'--> <!--The %s placeholder will be replaced with the translation of 'hotspot_scanning_a_qr_code'-->
<string name="hotspot_manual_site">El vostre telèfon proporciona un punt d\'accés Wi-Fi. Les persones connectades al punt d\'accés poden baixar el Briar escrivint l\'enllaç següent en un navegador web o %s.</string>
<string name="hotspot_manual_site_address">Adreça (URL)</string>
<string name="hotspot_qr_site">El vostre telèfon proporciona una punt d\'accés Wi-Fi. Les persones connectades al punt d\'accés poden baixar el Briar escanejant aquest codi QR.</string>
<!--e.g. Download Briar 1.2.20--> <!--e.g. Download Briar 1.2.20-->
<string name="website_download_title_1">Baixa el Briar %s</string>
<string name="website_download_intro_1">Algú a prop ha compartit el Briar amb vós.</string>
<string name="website_download_button">Baixa el Briar</string>
<string name="website_download_outro">Un cop finalitzada la baixada, obriu el fitxer baixat i instal·leu-lo.</string>
<string name="website_troubleshooting_title">Detecció d\'errors</string> <string name="website_troubleshooting_title">Detecció d\'errors</string>
<string name="website_troubleshooting_1">Si no podeu baixar l\'aplicació, proveu amb un altre navegador web.</string>
<string name="website_troubleshooting_2_old">Per instal·lar l\'aplicació baixada, és possible que hàgiu de permetre la instal·lació d\'aplicacions des de «Fonts desconegudes» a la configuració del sistema. Després, potser haureu de tornar a baixar l\'aplicació. Us recomanem que desactiveu la configuració «Fonts desconegudes» després d\'instal·lar l\'aplicació.</string>
<string name="website_troubleshooting_2_new">Per instal·lar l\'aplicació baixada, potser haureu de permetre que el navegador instal·li aplicacions desconegudes. Després d\'instal·lar l\'aplicació, us recomanem que elimineu el permís del navegador per instal·lar aplicacions desconegudes.</string>
<string name="hotspot_help_wifi_title">Problemes amb la connexió Wi-Fi:</string>
<string name="hotspot_help_wifi_1">Proveu a desactivar i reactivar la Wi-Fi als dos telèfons i torneu-ho a provar.</string>
<string name="hotspot_help_wifi_2">Si el vostre telèfon es queixa que la Wi-Fi no té Internet, digueu-li que voleu mantenir-vos connectat de totes maneres.</string>
<string name="hotspot_help_wifi_3">Reinicieu el telèfon que executa el punt d\'accés Wi-Fi, inicieu el Briar i torneu a provar de compartir.</string>
<string name="hotspot_help_site_title">Problemes per visitar el lloc web local:</string>
<string name="hotspot_help_site_1">Torneu a comprovar que heu introduït l\'adreça exactament com es mostra. Un petit error pot fer que falli.</string>
<string name="hotspot_help_site_2">Assegureu-vos que el vostre telèfon encara estigui connectat a la Wi-Fi correcta (vegeu més amunt) quan intenteu accedir al lloc.</string>
<string name="hotspot_help_site_3">Si teniu un tallafoc, comproveu que no bloquegi l\'accés.</string>
<string name="hotspot_help_site_4">Si podeu visitar el lloc, però no baixar l\'aplicació Briar, proveu-ho amb un navegador web diferent.</string>
<string name="hotspot_help_fallback_title">No funciona res?</string>
<string name="hotspot_help_fallback_intro">Podeu provar de desar l\'aplicació com a fitxer .apk per compartir-la d\'una altra manera. Un cop el fitxer s\'ha transferit a l\'altre dispositiu, es pot utilitzar per instal·lar el Briar.
\n\nConsell: per compartir mitjançant Bluetooth, és possible que hàgiu de canviar el nom del fitxer perquè acabi amb .zip.</string>
<string name="hotspot_help_fallback_button">Desa l\'aplicació</string>
<!--error handling--> <!--error handling-->
<string name="hotspot_error_intro">S\'ha produït un error en intentar compartir l\'aplicació mitjançant Wi-Fi:</string>
<string name="hotspot_error_no_wifi_direct">El dispositiu no és compatible amb Wi-Fi Direct</string>
<string name="hotspot_error_start_callback_failed">El punt d\'accés no s\'ha pogut iniciar: error %s</string>
<string name="hotspot_error_start_callback_failed_unknown">El punt d\'accés no s\'ha pogut iniciar amb un error desconegut, motiu %d</string>
<string name="hotspot_error_start_callback_no_group_info">El punt d\'accés no s\'ha pogut iniciar: no hi ha informació del grup</string>
<string name="hotspot_error_web_server_start">S\'ha produït un error en iniciar el servidor web</string>
<string name="hotspot_error_web_server_serve">S\'ha produït un error en presentar el lloc web.\n\nSi el problema persisteix, envieu comentaris (amb dades anònimes) mitjançant l\'aplicació Briar.</string>
<string name="hotspot_flag_test">Avís: aquesta aplicació s\'ha instal·lat amb l\'Android Studio i NO es pot instal·lar en un altre dispositiu.</string>
<string name="hotspot_error_framework_busy">No es pot iniciar el punt d\'accés.\n\nSi teniu un altre punt d\'accés en execució o compartiu la vostra connexió a Internet mitjançant Wi-Fi, proveu d\'aturar-lo i torneu-ho a provar més tard.</string>
<!--Transfer Data via Removable Drives--> <!--Transfer Data via Removable Drives-->
<string name="removable_drive_menu_title">Connecta mitjançant una unitat extraïble</string>
<string name="removable_drive_intro">Si no us podeu connectar al vostre contacte a través d\'Internet, Wi-Fi o Bluetooth, el Briar també pot transferir missatges en una unitat extraïble, com ara un llapis USB o una targeta SD.</string>
<string name="removable_drive_explanation">Si no podeu connectar-vos al vostre contacte a través d\'Internet, Wi-Fi o Bluetooth, el Briar també pot transferir missatges en una unitat extraïble, com ara un llapis USB o una targeta SD.\n\nQuan feu servir el botó «Envia dades», qualsevol dada que estigui esperant per ser enviada al contacte s\'escriurà a la unitat extraïble. Això inclou missatges privats, fitxers adjunts, blogs, fòrums i grups privats.\n\nTot es xifrarà abans que s\'escrigui a la unitat extraïble.\n\nQuan el vostre contacte rebi la unitat extraïble, pot utilitzar el botó «Rebre dades» per importar els missatges al Briar.</string>
<string name="removable_drive_title_send">Envia dades</string>
<string name="removable_drive_title_receive">Rebre dades</string>
<string name="removable_drive_send_intro">Toqueu el botó següent per crear un fitxer nou que contingui els missatges xifrats. Podeu triar on es desarà el fitxer.\n\nSi voleu desar el fitxer en una unitat extraïble, inseriu la unitat ara.</string>
<string name="removable_drive_send_no_data">Actualment no hi ha cap missatge esperant per ser enviat a aquest contacte.</string>
<string name="removable_drive_send_not_supported">Aquest contacte utilitza una versió antiga del Briar o un dispositiu antic que no admet aquesta funció.</string>
<string name="removable_drive_send_button">Trieu el fitxer per exportar</string>
<string name="removable_drive_ongoing">Espereu que es completi la tasca en curs</string>
<string name="removable_drive_receive_intro">Toqueu el botó següent per triar el fitxer que t\'ha enviat el vostre contacte.\n\nSi el fitxer es troba en una unitat extraïble, inseriu-la ara.</string>
<string name="removable_drive_receive_button">Trieu el fitxer per importar</string>
<string name="removable_drive_success_send_title">Exportació correcta</string>
<string name="removable_drive_success_send_text">Les dades s\'han exportat correctament. Ara teniu 28 dies per transportar el fitxer al vostre contacte.\n\nSi el fitxer es troba en una unitat extraïble, feu servir la notificació de la barra d\'estat per expulsar la unitat abans de desconnectar-la.</string>
<string name="removable_drive_success_receive_title">Importació correcta</string>
<string name="removable_drive_success_receive_text">S\'han rebut tots els missatges xifrats continguts en aquest fitxer.</string>
<string name="removable_drive_error_send_title">S\'ha produït un error en exportar les dades</string>
<string name="removable_drive_error_send_text">S\'ha produït un error escrivint dades al fitxer.\n\nSi utilitzeu una unitat extraïble, assegureu-vos que estigui inserida correctament i torneu-ho a provar.\n\nSi l\'error persisteix, envieu comentaris per informar l\'equip del Briar sobre el assumpte.</string>
<string name="removable_drive_error_receive_title">S\'ha produït un error en importar les dades</string>
<string name="removable_drive_error_receive_text">El fitxer seleccionat no contenia res que el Briar pogués reconèixer.\n\nComproveu que heu triat el fitxer correcte.\n\nSi el vostre contacte va crear el fitxer fa més de 28 dies, el Briar no el podrà reconèixer.</string>
<!--Screenshots--> <!--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.-->
<string name="screenshot_alice">Alba</string> <string name="screenshot_alice">Alba</string>

View File

@@ -237,6 +237,8 @@
<string name="qr_code_invalid">Der QR-Code ist ungültig</string> <string name="qr_code_invalid">Der QR-Code ist ungültig</string>
<string name="qr_code_too_old_1">Der QR-Code, den du gescannt hast, kommt von einer älteren Version von Briar.\n\nBitte deinen Kontakt, zur neuesten Version upzudaten, und versuche es erneut.</string> <string name="qr_code_too_old_1">Der QR-Code, den du gescannt hast, kommt von einer älteren Version von Briar.\n\nBitte deinen Kontakt, zur neuesten Version upzudaten, und versuche es erneut.</string>
<string name="qr_code_too_new_1">Der QR-Code, den du gescannt hast, kommt von einer neueren Version.\n\nBitte aktualisiere Briar auf die neueste Version und versuche es erneut.</string> <string name="qr_code_too_new_1">Der QR-Code, den du gescannt hast, kommt von einer neueren Version.\n\nBitte aktualisiere Briar auf die neueste Version und versuche es erneut.</string>
<string name="mailbox_qr_code_for_contact">Der QR-Code, den du gescannt hast, stammt von einer Briar-Mailbox.\n\nWenn du dich mit einer Mailbox verbinden willst, dann wähle in den Einstellungen &gt; Mailbox im Briar-Menü.</string>
<string name="qr_code_format_unknown">Der QR-Code, den du gescannt hast, ist nicht dazu gedacht, einen Kontakt hinzuzufügen.\n\nBitte scanne den QR-Code, der auf dem Display deines Kontaktes angezeigt wird.</string>
<string name="camera_error">Kamerafehler</string> <string name="camera_error">Kamerafehler</string>
<string name="connecting_to_device">Verbinde mit Gerät\u2026</string> <string name="connecting_to_device">Verbinde mit Gerät\u2026</string>
<string name="authenticating_with_device">Authentifiziere Gerät\u2026</string> <string name="authenticating_with_device">Authentifiziere Gerät\u2026</string>
@@ -431,6 +433,10 @@
<string name="forum_declined_toast">Einladung abgelehnt</string> <string name="forum_declined_toast">Einladung abgelehnt</string>
<string name="shared_by_format">Geteilt durch %s</string> <string name="shared_by_format">Geteilt durch %s</string>
<string name="forum_invitation_already_sharing">Bereits geteilt.</string> <string name="forum_invitation_already_sharing">Bereits geteilt.</string>
<string name="forum_invitation_already_invited">Die Einladung wurde bereits versendet.</string>
<string name="forum_invitation_invite_received">Die Einladung wurde bereits empfangen.</string>
<string name="forum_invitation_not_supported">Dies wird von diesem Kontakt nicht unterstützt.</string>
<string name="forum_invitation_error">Fehler. Dies ist ein Bug und ist nicht dein Fehler.</string>
<string name="forum_invitation_response_accepted_sent">Du hast die Forumeinladung von %s angenommen.</string> <string name="forum_invitation_response_accepted_sent">Du hast die Forumeinladung von %s angenommen.</string>
<string name="forum_invitation_response_declined_sent">Du hast die Forumeinladung von %s abgelehnt.</string> <string name="forum_invitation_response_declined_sent">Du hast die Forumeinladung von %s abgelehnt.</string>
<string name="forum_invitation_response_declined_auto">Die Forumeinladung von %s wurde automatisch abgelehnt.</string> <string name="forum_invitation_response_declined_auto">Die Forumeinladung von %s wurde automatisch abgelehnt.</string>
@@ -594,9 +600,13 @@
<string name="mailbox_setup_download_link">Download-Link teilen</string> <string name="mailbox_setup_download_link">Download-Link teilen</string>
<string name="mailbox_setup_button_scan">Mailbox QR-Code scannen</string> <string name="mailbox_setup_button_scan">Mailbox QR-Code scannen</string>
<string name="permission_camera_qr_denied_body">Du hast den Zugriff auf die Kamera verweigert, aber das Scannen eines QR-Codes erfordert die Verwendung der Kamera.\n\nBitte gewähre den Zugriff.</string> <string name="permission_camera_qr_denied_body">Du hast den Zugriff auf die Kamera verweigert, aber das Scannen eines QR-Codes erfordert die Verwendung der Kamera.\n\nBitte gewähre den Zugriff.</string>
<string name="mailbox_setup_connecting">Verbinde…</string> <string name="mailbox_setup_connecting">Verbinde mit der Mailbox</string>
<string name="mailbox_setup_qr_code_wrong_title">Falscher QR-Code</string> <!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_qr_code_wrong_description">Der gescannte Code ist ungültig. Bitte öffne die Briar Mailbox-App auf deinem Mailbox-Gerät und scanne den dort angezeigten QR-Code.</string> <string name="mailbox_setup_connecting_info">Dies könnte %1s erfordern.</string>
<string name="mailbox_qr_code_too_old">Der QR-Code, den du gescannt hast, stammt von einer älteren Briar-Mailbox Version.\n\nBitte aktualisiere die Briar-Mailbox auf die neueste Version und probiere es noch einmal.</string>
<string name="mailbox_qr_code_too_new">Der QR-Code, den du gescannt hast, stammt von einer neueren Briar-Mailbox Version.\n\nBitte aktualisiere Briar auf die neueste Version und probiere es noch einmal.</string>
<string name="contact_qr_code_for_mailbox">Der QR-Code, den du gescannt hast, ist dafür gedacht, einen Kontakt hinzuzufügen.\n\nWenn du einen Kontakt hinzufügen möchtest, gehe bitte zur Kontaktliste und klicke auf das + Symbol.</string>
<string name="mailbox_setup_qr_code_wrong_description">Der QR-Code, den du gescannt hast, stammt nicht von einer Briar-Mailbox.\n\nBitte öffne die Briar-Mailbox auf deinem Mailbox-Gerät und scanne den davon angezeigten QR-Code.</string>
<string name="mailbox_setup_already_paired_title">Mailbox bereits verknüpft</string> <string name="mailbox_setup_already_paired_title">Mailbox bereits verknüpft</string>
<string name="mailbox_setup_already_paired_description">Verknüpfung der Mailbox auf deinem anderen Gerät aufheben und erneut versuchen.</string> <string name="mailbox_setup_already_paired_description">Verknüpfung der Mailbox auf deinem anderen Gerät aufheben und erneut versuchen.</string>
<string name="mailbox_setup_io_error_title">Keine Verknüpfung möglich</string> <string name="mailbox_setup_io_error_title">Keine Verknüpfung möglich</string>

View File

@@ -235,6 +235,7 @@
<string name="menu_contact">Contacto</string> <string name="menu_contact">Contacto</string>
<!--Adding Contacts--> <!--Adding Contacts-->
<string name="add_contact_title">Agregar contacto cercano</string> <string name="add_contact_title">Agregar contacto cercano</string>
<string name="add_contact_error_two_way">¿Ambos escanearon los códigos QR del otro?</string>
<string name="face_to_face">Debes reunirte con la persona a la que quieras añadir como contacto.\n\nHaciéndolo así prevendrás que nadie te suplante o pueda leer tus mensajes en el futuro.</string> <string name="face_to_face">Debes reunirte con la persona a la que quieras añadir como contacto.\n\nHaciéndolo así prevendrás que nadie te suplante o pueda leer tus mensajes en el futuro.</string>
<string name="continue_button">Continuar</string> <string name="continue_button">Continuar</string>
<string name="try_again_button">Prueba de nuevo</string> <string name="try_again_button">Prueba de nuevo</string>
@@ -308,6 +309,7 @@
<string name="different_person_button">Diferente Persona</string> <string name="different_person_button">Diferente Persona</string>
<string name="duplicate_link_dialog_text_3">%1$s y %2$s te enviaron el mismo enlace.\n\nUno de ellos puede estar tratando de descubrir quiénes son tus contactos.\n\nNo les digas que recibiste el mismo enlace de otra persona.</string> <string name="duplicate_link_dialog_text_3">%1$s y %2$s te enviaron el mismo enlace.\n\nUno de ellos puede estar tratando de descubrir quiénes son tus contactos.\n\nNo les digas que recibiste el mismo enlace de otra persona.</string>
<string name="pending_contact_updated_toast">Contacto pendiente actualizado</string> <string name="pending_contact_updated_toast">Contacto pendiente actualizado</string>
<string name="info_both_must_enter_links">Ambos deben agregar los enlaces del otro</string>
<!--Peer trust levels--> <!--Peer trust levels-->
<string name="peer_trust_level_unverified">Contacto no verificado</string> <string name="peer_trust_level_unverified">Contacto no verificado</string>
<string name="peer_trust_level_verified">Contacto verificado</string> <string name="peer_trust_level_verified">Contacto verificado</string>
@@ -341,6 +343,7 @@
<string name="connect_via_bluetooth_intro">En el caso que las conexiones Bluetooth no funcionen automáticamente, puedes usar esta pantalla para conectarte manualmente.\n\nTu contacto necesita estar cerca para que esto funcione.\n\nAmbos deberían presionar \"Inicio\" al mismo tiempo.</string> <string name="connect_via_bluetooth_intro">En el caso que las conexiones Bluetooth no funcionen automáticamente, puedes usar esta pantalla para conectarte manualmente.\n\nTu contacto necesita estar cerca para que esto funcione.\n\nAmbos deberían presionar \"Inicio\" al mismo tiempo.</string>
<string name="connect_via_bluetooth_already_discovering">Ya se está intentando conectar vía Bluetooth. Por favor inténtalo de nuevo en unos segundos.</string> <string name="connect_via_bluetooth_already_discovering">Ya se está intentando conectar vía Bluetooth. Por favor inténtalo de nuevo en unos segundos.</string>
<string name="connect_via_bluetooth_no_location_permission">No se puede continuar sin permiso de ubicación</string> <string name="connect_via_bluetooth_no_location_permission">No se puede continuar sin permiso de ubicación</string>
<string name="connect_via_bluetooth_no_bluetooth_permission">No se puede continuar sin el permiso de dispositivos cercanos</string>
<string name="connect_via_bluetooth_start">Conectar mediante Bluetooth...</string> <string name="connect_via_bluetooth_start">Conectar mediante Bluetooth...</string>
<string name="connect_via_bluetooth_success">Conectado exitosamente mediante Bluetooth</string> <string name="connect_via_bluetooth_success">Conectado exitosamente mediante Bluetooth</string>
<string name="connect_via_bluetooth_error">No se pudo conectar mediante Bluetooth.</string> <string name="connect_via_bluetooth_error">No se pudo conectar mediante Bluetooth.</string>
@@ -604,9 +607,7 @@
<string name="mailbox_setup_download_link">Compartir enlace de descarga</string> <string name="mailbox_setup_download_link">Compartir enlace de descarga</string>
<string name="mailbox_setup_button_scan">Escanear código QR de Buzón</string> <string name="mailbox_setup_button_scan">Escanear código QR de Buzón</string>
<string name="permission_camera_qr_denied_body">Has denegado el acceso a la cámara, pero para escanear un código QR se requiere el uso de la cámara.\n\nPor favor considera conceder el acceso.</string> <string name="permission_camera_qr_denied_body">Has denegado el acceso a la cámara, pero para escanear un código QR se requiere el uso de la cámara.\n\nPor favor considera conceder el acceso.</string>
<string name="mailbox_setup_connecting">Conectando...</string> <!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_qr_code_wrong_title">Código QR erróneo</string>
<string name="mailbox_setup_qr_code_wrong_description">El código escaneado no es válido. Por favor abre la aplicación Buzón de Briar en tu dispositivo de buzón y escanea el código QR que presenta.</string>
<string name="mailbox_setup_already_paired_title">Buzón ya vinculado</string> <string name="mailbox_setup_already_paired_title">Buzón ya vinculado</string>
<string name="mailbox_setup_already_paired_description">Desvincula el Buzón en tu otro dispositivo e inténtalo de nuevo.</string> <string name="mailbox_setup_already_paired_description">Desvincula el Buzón en tu otro dispositivo e inténtalo de nuevo.</string>
<string name="mailbox_setup_io_error_title">No se pudo conectar</string> <string name="mailbox_setup_io_error_title">No se pudo conectar</string>
@@ -743,6 +744,7 @@
<string name="permission_location_request_body">Para descubrir dispositivos Bluetooth, Briar necesita permiso para acceder tu ubicación.\n\nBriar no la almacena o la comparte con nadie.</string> <string name="permission_location_request_body">Para descubrir dispositivos Bluetooth, Briar necesita permiso para acceder tu ubicación.\n\nBriar no la almacena o la comparte con nadie.</string>
<string name="permission_camera_location_title">Cámara y ubicación</string> <string name="permission_camera_location_title">Cámara y ubicación</string>
<string name="permission_camera_location_request_body">Para escanear el código QR, Briar necesita acceso a la cámara.\n\nPara descubrir dispositivos Bluetooth, Briar necesita permiso para acceder tu ubicación.\n\nBriar no la almacena o la comparte con nadie.</string> <string name="permission_camera_location_request_body">Para escanear el código QR, Briar necesita acceso a la cámara.\n\nPara descubrir dispositivos Bluetooth, Briar necesita permiso para acceder tu ubicación.\n\nBriar no la almacena o la comparte con nadie.</string>
<string name="permission_camera_bluetooth_title">Cámara y dispositivos cercanos</string>
<string name="permission_camera_denied_body">Has denegado el acceso a la cámara, pero para añadir contactos se requiere el uso de la cámara.\n\nPor favor considera la posibilidad de conceder el acceso.</string> <string name="permission_camera_denied_body">Has denegado el acceso a la cámara, pero para añadir contactos se requiere el uso de la cámara.\n\nPor favor considera la posibilidad de conceder el acceso.</string>
<string name="permission_location_denied_body">Has denegado el acceso a tu ubicación, pero Briar necesita este permiso para descubrir dispositivos Bluetooth.\n\nPor favor considera la posibilidad de conceder el acceso.</string> <string name="permission_location_denied_body">Has denegado el acceso a tu ubicación, pero Briar necesita este permiso para descubrir dispositivos Bluetooth.\n\nPor favor considera la posibilidad de conceder el acceso.</string>
<string name="permission_location_setting_title">Configuración de ubicación</string> <string name="permission_location_setting_title">Configuración de ubicación</string>

View File

@@ -247,6 +247,8 @@
<string name="qr_code_invalid">کد کیوآر نامعتبر می باشد</string> <string name="qr_code_invalid">کد کیوآر نامعتبر می باشد</string>
<string name="qr_code_too_old_1">کد QR که اسکن کرده‌اید مربوط به نسخه قدیمی Briar است.\n\nلطفا از مخاطب خود بخواهید به آخرین نسخه ارتقا دهد و سپس دوباره امتحان کنید.</string> <string name="qr_code_too_old_1">کد QR که اسکن کرده‌اید مربوط به نسخه قدیمی Briar است.\n\nلطفا از مخاطب خود بخواهید به آخرین نسخه ارتقا دهد و سپس دوباره امتحان کنید.</string>
<string name="qr_code_too_new_1">کد QR که اسکن کرده‌اید مربوط به نسخه جدیدتری از Briar است.\n\nلطفا به آخرین نسخه ارتقا دهید و سپس دوباره امتحان کنید.</string> <string name="qr_code_too_new_1">کد QR که اسکن کرده‌اید مربوط به نسخه جدیدتری از Briar است.\n\nلطفا به آخرین نسخه ارتقا دهید و سپس دوباره امتحان کنید.</string>
<string name="mailbox_qr_code_for_contact">کد QR که اسکن کرده‌اید از صندوق پستی Briar می‌آید.\n\nاگر می‌خواهید یک صندوق پستی را پیوند دهید، لطفا تنظیمات &gt; صندوق پستی از منوی Briar را انتخاب کنید.</string>
<string name="qr_code_format_unknown">کد QR که اسکن کرده اید برای افزودن مخاطب Briar نیست.\n\nلطفا کد QR نشان داده شده در صفحه مخاطب خود را اسکن کنید.</string>
<string name="camera_error">خطای دوربین</string> <string name="camera_error">خطای دوربین</string>
<string name="connecting_to_device">اتصال به دستگاهu2026\</string> <string name="connecting_to_device">اتصال به دستگاهu2026\</string>
<string name="authenticating_with_device">تصدیق سازی با دستگاه u2026\</string> <string name="authenticating_with_device">تصدیق سازی با دستگاه u2026\</string>
@@ -626,9 +628,13 @@
<string name="mailbox_setup_download_link">اشتراک لینک دانلود</string> <string name="mailbox_setup_download_link">اشتراک لینک دانلود</string>
<string name="mailbox_setup_button_scan">اسکن کد QR Mailbox</string> <string name="mailbox_setup_button_scan">اسکن کد QR Mailbox</string>
<string name="permission_camera_qr_denied_body">شما دسترسی به دوربین را رد کرده‌اید، اما اسکن کد QR مستلزم استفاده از دوربین است.\n\nلطفا دسترسی به دوربین را بدهید.</string> <string name="permission_camera_qr_denied_body">شما دسترسی به دوربین را رد کرده‌اید، اما اسکن کد QR مستلزم استفاده از دوربین است.\n\nلطفا دسترسی به دوربین را بدهید.</string>
<string name="mailbox_setup_connecting">در حال اتصال...</string> <string name="mailbox_setup_connecting">در حال اتصال به صندوق پست…</string>
<string name="mailbox_setup_qr_code_wrong_title">کد QR اشتباه است</string> <!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_qr_code_wrong_description">کد اسکن‌شده نامعتبر است. لطفا برنامه Briar Mailbox را در دستگاه Mailbox خود باز کنید و کد QR ارائه‌شده را اسکن کنید.</string> <string name="mailbox_setup_connecting_info">این ممکن است تا %1s طول بکشد</string>
<string name="mailbox_qr_code_too_old">کد QR که اسکن کرده‌اید از نسخه قدیمی‌تری از صندوق پستی Briar آمده است.\n\nلطفا صندوق پستی Briar را به آخرین نسخه ارتقا دهید و سپس دوباره امتحان کنید.</string>
<string name="mailbox_qr_code_too_new">کد QR که اسکن کرده‌اید از نسخه جدیدتری از صندوق پستی Briar آمده است.\n\nلطفا Briar را به آخرین نسخه ارتقا دهید و سپس دوباره امتحان کنید.</string>
<string name="contact_qr_code_for_mailbox">کد QR که اسکن کرده‌اید برای افزودن مخاطب Briar است.\n\nاگر می‌خواهید مخاطبی را اضافه کنید، لطفا به فهرست مخاطبین بروید و روی نماد + ضربه بزنید.</string>
<string name="mailbox_setup_qr_code_wrong_description">کد QR که اسکن کرده‌اید از صندوق پستی Briar نمی‌آید.\n\nلطفا برنامه صندوق پستی Briar را در دستگاه مربوط به صندوق پستی خود باز کنید و کد QR ارائه‌شده را اسکن کنید.</string>
<string name="mailbox_setup_already_paired_title">صندوق‌پستی قبلا متصل شده است</string> <string name="mailbox_setup_already_paired_title">صندوق‌پستی قبلا متصل شده است</string>
<string name="mailbox_setup_already_paired_description">اتصال صندوق‌پستی دستگاه دیگرتان را لغو کنید و دوباره امتحان کنید.</string> <string name="mailbox_setup_already_paired_description">اتصال صندوق‌پستی دستگاه دیگرتان را لغو کنید و دوباره امتحان کنید.</string>
<string name="mailbox_setup_io_error_title">متصل نشد</string> <string name="mailbox_setup_io_error_title">متصل نشد</string>

View File

@@ -593,9 +593,7 @@
<string name="mailbox_setup_download_link">Partager le lien de téléchargement</string> <string name="mailbox_setup_download_link">Partager le lien de téléchargement</string>
<string name="mailbox_setup_button_scan">Balayer le code QR de Boîte de courriel</string> <string name="mailbox_setup_button_scan">Balayer le code QR de Boîte de courriel</string>
<string name="permission_camera_qr_denied_body">Vous avez refusé laccès à lappareil photo, mais le balayage dun code QR de contacts exige de lutiliser.\n\nVeuillez envisager dy accorder laccès.</string> <string name="permission_camera_qr_denied_body">Vous avez refusé laccès à lappareil photo, mais le balayage dun code QR de contacts exige de lutiliser.\n\nVeuillez envisager dy accorder laccès.</string>
<string name="mailbox_setup_connecting">Connexion…</string> <!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_qr_code_wrong_title">Le code QR est erroné</string>
<string name="mailbox_setup_qr_code_wrong_description">Le code balayé est invalide. Veuillez ouvrir lappli Boîte de courriel de Briar sur votre appareil Boîte de courriel et balayer le code quelle présente.</string>
<string name="mailbox_setup_already_paired_title">La Boîte de courriel est déjà reliée</string> <string name="mailbox_setup_already_paired_title">La Boîte de courriel est déjà reliée</string>
<string name="mailbox_setup_already_paired_description">Annuler le lien avec la Boîte de courriel sur lautre appareil et réessayer.</string> <string name="mailbox_setup_already_paired_description">Annuler le lien avec la Boîte de courriel sur lautre appareil et réessayer.</string>
<string name="mailbox_setup_io_error_title">Connexion impossible</string> <string name="mailbox_setup_io_error_title">Connexion impossible</string>

View File

@@ -578,9 +578,7 @@
<string name="mailbox_setup_download_link">Deila niðurhalstengli</string> <string name="mailbox_setup_download_link">Deila niðurhalstengli</string>
<string name="mailbox_setup_button_scan">Skanna QR-kóða pósthólfs</string> <string name="mailbox_setup_button_scan">Skanna QR-kóða pósthólfs</string>
<string name="permission_camera_qr_denied_body">Þú hefur hafnað aðgangi að myndavélinni, en það að skanna QR-kóða krefst notkun hennar.\n\nÍhugaðu að veita þennan aðgang.</string> <string name="permission_camera_qr_denied_body">Þú hefur hafnað aðgangi að myndavélinni, en það að skanna QR-kóða krefst notkun hennar.\n\nÍhugaðu að veita þennan aðgang.</string>
<string name="mailbox_setup_connecting">Tengist…</string> <!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_qr_code_wrong_title">Rangur QR-kóði</string>
<string name="mailbox_setup_qr_code_wrong_description">Skannaði kóðinn er ógildur. Opnaðu Briar Mailbox forritið á pósthólfstækinu þínu og skannaðu QR-kóðann sem það birtir.</string>
<string name="mailbox_setup_already_paired_title">Pósthólf er þegar tengt</string> <string name="mailbox_setup_already_paired_title">Pósthólf er þegar tengt</string>
<string name="mailbox_setup_already_paired_description">Aftengdu pósthólfið á hinu tækinu þínu og prófaðu aftur.</string> <string name="mailbox_setup_already_paired_description">Aftengdu pósthólfið á hinu tækinu þínu og prófaðu aftur.</string>
<string name="mailbox_setup_io_error_title">Mistókst að tengjast</string> <string name="mailbox_setup_io_error_title">Mistókst að tengjast</string>

View File

@@ -246,6 +246,8 @@
<string name="qr_code_invalid">Il codice QR non è valido</string> <string name="qr_code_invalid">Il codice QR non è valido</string>
<string name="qr_code_too_old_1">Il codice QR che hai scansionato proviene da una versione più vecchia di Briar.\n\nChiedi al tuo contatto di aggiornare all\'ultima versione e poi riprova.</string> <string name="qr_code_too_old_1">Il codice QR che hai scansionato proviene da una versione più vecchia di Briar.\n\nChiedi al tuo contatto di aggiornare all\'ultima versione e poi riprova.</string>
<string name="qr_code_too_new_1">Il codice QR che hai scansionato proviene da una versione più recente di Briar.\n\nAggiorna all\'ultima versione e poi riprova.</string> <string name="qr_code_too_new_1">Il codice QR che hai scansionato proviene da una versione più recente di Briar.\n\nAggiorna all\'ultima versione e poi riprova.</string>
<string name="mailbox_qr_code_for_contact">Il codice QR che hai scansionato proviene da Briar Mailbox\n\nSe vuoi collegare una casella postale, scegli Impostazioni &gt; Casella postale dal menu di Briar.</string>
<string name="qr_code_format_unknown">Il codice QR che hai scansionato non è fatto per l\'aggiunta di un contatto Briar.\n\nScansiona il codice QR mostrato nello schermo del tuo contatto.</string>
<string name="camera_error">Errore fotocamera</string> <string name="camera_error">Errore fotocamera</string>
<string name="connecting_to_device">Connessione al dispositivo\u2026</string> <string name="connecting_to_device">Connessione al dispositivo\u2026</string>
<string name="authenticating_with_device">Autenticazione con il dispositivo\u2026</string> <string name="authenticating_with_device">Autenticazione con il dispositivo\u2026</string>
@@ -444,6 +446,10 @@
<string name="forum_declined_toast">Invito declinato</string> <string name="forum_declined_toast">Invito declinato</string>
<string name="shared_by_format">Condiviso da %s</string> <string name="shared_by_format">Condiviso da %s</string>
<string name="forum_invitation_already_sharing">Già in condivisione</string> <string name="forum_invitation_already_sharing">Già in condivisione</string>
<string name="forum_invitation_already_invited">Invito già spedito</string>
<string name="forum_invitation_invite_received">Invito già ricevuto</string>
<string name="forum_invitation_not_supported">Non supportato da questo contatto</string>
<string name="forum_invitation_error">Errore. Si tratta di un bug, non è colpa tua</string>
<string name="forum_invitation_response_accepted_sent">Hai accettato l\'invito al forum da %s</string> <string name="forum_invitation_response_accepted_sent">Hai accettato l\'invito al forum da %s</string>
<string name="forum_invitation_response_declined_sent">Hai declinato l\'invito al forum da %s</string> <string name="forum_invitation_response_declined_sent">Hai declinato l\'invito al forum da %s</string>
<string name="forum_invitation_response_declined_auto">L\'invito di %s al forum è stato rifiutato automaticamente.</string> <string name="forum_invitation_response_declined_auto">L\'invito di %s al forum è stato rifiutato automaticamente.</string>
@@ -608,9 +614,13 @@
<string name="mailbox_setup_download_link">Condividi link di download</string> <string name="mailbox_setup_download_link">Condividi link di download</string>
<string name="mailbox_setup_button_scan">Scansiona codice QR Mailbox</string> <string name="mailbox_setup_button_scan">Scansiona codice QR Mailbox</string>
<string name="permission_camera_qr_denied_body">Hai negato l\'accesso alla fotocamera, ma la scansione di un codice QR ne richiede l\'utilizzo.\n\nPrendi in considerazione di consentirne l\'accesso.</string> <string name="permission_camera_qr_denied_body">Hai negato l\'accesso alla fotocamera, ma la scansione di un codice QR ne richiede l\'utilizzo.\n\nPrendi in considerazione di consentirne l\'accesso.</string>
<string name="mailbox_setup_connecting">Connessione in corso...</string> <string name="mailbox_setup_connecting">Connessione alla casella postale…</string>
<string name="mailbox_setup_qr_code_wrong_title">Codice QR errato</string> <!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_qr_code_wrong_description">Il codice scansionato non è valido. Apri l\'app Briar Mailbox nel tuo dispositivo della casella postale e scansiona il codice QR che mostra.</string> <string name="mailbox_setup_connecting_info">Potrebbe impiegarci %1s</string>
<string name="mailbox_qr_code_too_old">Il codice QR che hai scansionato proviene da una versione più vecchia di Briar Mailbox.\n\nAggiorna Briar Mailbox all\'ultima versione e poi riprova.</string>
<string name="mailbox_qr_code_too_new">Il codice QR che hai scansionato proviene da una versione più recente di Briar Mailbox.\n\nAggiorna Briar all\'ultima versione e poi riprova.</string>
<string name="contact_qr_code_for_mailbox">Il codice QR che hai scansionato è fatto per l\'aggiunta di un contatto Briar.\n\nSe vuoi aggiungere un contatto, vai nell\'elenco dei contatti e tocca l\'icona + .</string>
<string name="mailbox_setup_qr_code_wrong_description">Il codice QR che hai scansionato non proviene da Briar Mailbox.\n\nApri l\'app Briar Mailbox sul tuo dispositivo della casella postale e scansiona il codice QR mostrato.</string>
<string name="mailbox_setup_already_paired_title">Casella postale già collegata</string> <string name="mailbox_setup_already_paired_title">Casella postale già collegata</string>
<string name="mailbox_setup_already_paired_description">Scollega la casella postale sul tuo altro dispositivo e riprova.</string> <string name="mailbox_setup_already_paired_description">Scollega la casella postale sul tuo altro dispositivo e riprova.</string>
<string name="mailbox_setup_io_error_title">Impossibile connettere</string> <string name="mailbox_setup_io_error_title">Impossibile connettere</string>

View File

@@ -214,6 +214,7 @@
<string name="menu_contact">連絡先</string> <string name="menu_contact">連絡先</string>
<!--Adding Contacts--> <!--Adding Contacts-->
<string name="add_contact_title">近くの人を連絡先に追加する</string> <string name="add_contact_title">近くの人を連絡先に追加する</string>
<string name="add_contact_error_two_way">お互いのQRコードを読み取りましたか</string>
<string name="face_to_face">連絡先として追加したい人と会う必要があります。\n\nこれにより、だれかがあなたになりすましたり、メッセージを読んだりするのを防ぐことができます。</string> <string name="face_to_face">連絡先として追加したい人と会う必要があります。\n\nこれにより、だれかがあなたになりすましたり、メッセージを読んだりするのを防ぐことができます。</string>
<string name="continue_button">続ける</string> <string name="continue_button">続ける</string>
<string name="try_again_button">もう一度やり直してください</string> <string name="try_again_button">もう一度やり直してください</string>
@@ -222,13 +223,16 @@
<string name="contact_added_toast">追加された連絡先:%s</string> <string name="contact_added_toast">追加された連絡先:%s</string>
<string name="contact_already_exists">連絡先%sは既に存在しています </string> <string name="contact_already_exists">連絡先%sは既に存在しています </string>
<string name="qr_code_invalid">QRコードが無効です</string> <string name="qr_code_invalid">QRコードが無効です</string>
<string name="qr_code_too_old_1">スキャンしたQRコードはBriarの古いバージョンから取得されました。\n\n最新版へアップグレードしてもらって、もう一度お試しください。</string> <string name="qr_code_too_old_1">読み取ったQRコードはBriarの古いバージョンから生じました。\n\n最新版へアップグレードしてもらって、もう一度お試しください。</string>
<string name="qr_code_too_new_1">スキャンしたQRコードは、新しいバージョンのBriarから取得されました。\n\n最新版にアップグレードしてから、もう一度お試しください。</string> <string name="qr_code_too_new_1">読み取ったQRコードは、新しいバージョンのBriarから生じました。\n\n最新版にアップグレードしてから、もう一度お試しください。</string>
<string name="mailbox_qr_code_for_contact">読み取ったQRコードは、Briarメールボックスから生じるものです。\n\nメールボックスに関連付けたいならば、設定を選択してください&gt; Briarメニューからメールボックス</string>
<string name="qr_code_format_unknown">読み取ったQRコードは、Briarの連絡先を追加するために示されたものではありません。\n\nあなたの連絡先の画面上に表示されたQRコードを読み取ってください。</string>
<string name="camera_error">カメラエラー</string> <string name="camera_error">カメラエラー</string>
<string name="connecting_to_device">端末に接続中\u2026</string> <string name="connecting_to_device">端末に接続中\u2026</string>
<string name="authenticating_with_device">端末同士での認証中\u2026</string> <string name="authenticating_with_device">端末同士での認証中\u2026</string>
<string name="connection_error_title">連絡先に接続できませんでした</string> <string name="connection_error_title">連絡先に接続できませんでした</string>
<string name="connection_error_feedback">この問題が解決しない場合、アプリを改善するために<a href="feedback">フィードバック</a>を送信してください。</string> <string name="connection_error_feedback">この問題が解決しない場合、アプリを改善するために<a href="feedback">フィードバック</a>を送信してください。</string>
<string name="info_both_must_scan">お互いのQRコードを読み取る必要があります</string>
<!--Adding Contacts Remotely--> <!--Adding Contacts Remotely-->
<string name="add_contact_remotely_title_case">離れた場所にいる相手を連絡先に追加</string> <string name="add_contact_remotely_title_case">離れた場所にいる相手を連絡先に追加</string>
<string name="add_contact_nearby_title">近くにいる相手を連絡先に追加</string> <string name="add_contact_nearby_title">近くにいる相手を連絡先に追加</string>
@@ -285,6 +289,7 @@
<string name="different_person_button">別の人</string> <string name="different_person_button">別の人</string>
<string name="duplicate_link_dialog_text_3">%1$sと%2$sから同じリンクを受信しました。\n\nどちらかがあなたの連絡先の内容を知ろうとしている可能性があります。\n\n他の人から同じリンクを受け取ったことを伝えないでください。</string> <string name="duplicate_link_dialog_text_3">%1$sと%2$sから同じリンクを受信しました。\n\nどちらかがあなたの連絡先の内容を知ろうとしている可能性があります。\n\n他の人から同じリンクを受け取ったことを伝えないでください。</string>
<string name="pending_contact_updated_toast">保留中の連絡先が更新されました</string> <string name="pending_contact_updated_toast">保留中の連絡先が更新されました</string>
<string name="info_both_must_enter_links">お互いのリンクを追加する必要があります</string>
<!--Peer trust levels--> <!--Peer trust levels-->
<string name="peer_trust_level_unverified">検証されてない連絡先</string> <string name="peer_trust_level_unverified">検証されてない連絡先</string>
<string name="peer_trust_level_verified">検証された連絡先</string> <string name="peer_trust_level_verified">検証された連絡先</string>
@@ -569,13 +574,17 @@
<string name="mailbox_setup_intro">メールボックスはあなたがオフラインの間、連絡先があなたにメッセージを送信することを有効にします。メールボックスはあなたがオンラインになるまで、メッセージを受信し保管します。\n <string name="mailbox_setup_intro">メールボックスはあなたがオフラインの間、連絡先があなたにメッセージを送信することを有効にします。メールボックスはあなたがオンラインになるまで、メッセージを受信し保管します。\n
\n予備端末上にBriarのメールボックスアプリをインストールできます。それを電源とWi-Fiに接続し、常時オンラインにしてください。</string> \n予備端末上にBriarのメールボックスアプリをインストールできます。それを電源とWi-Fiに接続し、常時オンラインにしてください。</string>
<string name="mailbox_setup_download">最初に、Google PlayまたはBriarをダウンロードしたどこかで\"Briar Mailbox\"を検索して、他の端末上にメールボックスアプリをインストールします。\n <string name="mailbox_setup_download">最初に、Google PlayまたはBriarをダウンロードしたどこかで\"Briar Mailbox\"を検索して、他の端末上にメールボックスアプリをインストールします。\n
\nそして、メールボックスアプリによって表示されるQRコードをスキャンして、Briarとあなたのメールボックス結びつけます。</string> \nそして、メールボックスアプリによって表示されるQRコードを読み取って、Briarとあなたのメールボックス結びつけます。</string>
<string name="mailbox_setup_download_link">ダウンロードリンクを共有</string> <string name="mailbox_setup_download_link">ダウンロードリンクを共有</string>
<string name="mailbox_setup_button_scan">メールボックスのQRコードをスキャン</string> <string name="mailbox_setup_button_scan">メールボックスのQRコードを読み取る</string>
<string name="permission_camera_qr_denied_body">カメラへのアクセスをが拒否されましたが、QRコードをスキャンするにはカメラを使用する必要があります。\n\nカメラへのアクセスの許可を考えてください</string> <string name="permission_camera_qr_denied_body">あなたはカメラアクセスすることを拒否しましたが、QRコードを読み取るにはカメラを使用する必要があります。\n\nアクセス権を付与することを考慮願います</string>
<string name="mailbox_setup_connecting">接続中…</string> <string name="mailbox_setup_connecting">メールボックスへ接続中…</string>
<string name="mailbox_setup_qr_code_wrong_title">不正なQRコード</string> <!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_qr_code_wrong_description">スキャンされたコードは無効です。メールボックス端末上のBriarメールボックスアプリを開き、提示されたQRコードをスキャンしてください。</string> <string name="mailbox_setup_connecting_info">%1sまでかかる場合があります</string>
<string name="mailbox_qr_code_too_old">読み取ったQRコードは、古いバージョンのBriarメールボックスから生じました。\n\nBriarメールボックスを最新版にアップグレードしてから、もう一度お試しください。</string>
<string name="mailbox_qr_code_too_new">読み取ったQRコードは、新しいバージョンのBriarメールボックスから生じました。\n\nBriarを最新版にアップグレードしてから、もう一度お試しください。</string>
<string name="contact_qr_code_for_mailbox">読み取ったQRコードは、Briarの連絡先を追加するためのものです。\n\n連絡先を追加したいならば、連絡先一覧に行き、+アイコンをタップしてください。</string>
<string name="mailbox_setup_qr_code_wrong_description">読み取ったQRコードは、Briarメールボックスから生じたものではありません。メールボックス端末上のBriarメールボックスアプリを開き、提示されたQRコードを読み取ってください。</string>
<string name="mailbox_setup_already_paired_title">メールボックスは既に結びつけられています</string> <string name="mailbox_setup_already_paired_title">メールボックスは既に結びつけられています</string>
<string name="mailbox_setup_already_paired_description">あなたの端末上のメールボックスの結びつけを解き、再試行してください。</string> <string name="mailbox_setup_already_paired_description">あなたの端末上のメールボックスの結びつけを解き、再試行してください。</string>
<string name="mailbox_setup_io_error_title">接続できません</string> <string name="mailbox_setup_io_error_title">接続できません</string>
@@ -707,16 +716,22 @@
<string name="screen_filter_review_apps">アプリを確認</string> <string name="screen_filter_review_apps">アプリを確認</string>
<!--Permission Requests--> <!--Permission Requests-->
<string name="permission_camera_title">カメラへのアクセス許可</string> <string name="permission_camera_title">カメラへのアクセス許可</string>
<string name="permission_camera_request_body">QRコードをスキャンするには、Briarはカメラにアクセスする必要があります。</string> <string name="permission_camera_request_body">QRコードを読み取るには、Briarはカメラにアクセスする必要があります。</string>
<string name="permission_location_title">位置情報へのアクセスの許可</string> <string name="permission_location_title">位置情報へのアクセスの許可</string>
<string name="permission_location_request_body">Bluetooth端末を検出するには、Briarがあなたの位置情報へのアクセスを必要とします。\n\nBriarはあなたの場所を保存したり、誰とも共有したりしません。</string> <string name="permission_location_request_body">Bluetooth端末を検出するには、Briarがあなたの位置情報へのアクセスを必要とします。\n\nBriarはあなたの場所を保存したり、誰とも共有したりしません。</string>
<string name="permission_camera_location_title">カメラと位置情報</string> <string name="permission_camera_location_title">カメラと位置情報</string>
<string name="permission_camera_location_request_body">QRコードをスキャンするには、Briarはカメラにアクセスする必要があります。\n\nBluetooth端末を検出するには、Briarは現在地情報にアクセスする許可が必要です。\n\nBriarは現在地を保存したり、誰とも共有したりしません。</string> <string name="permission_camera_location_request_body">QRコードを読み取るには、Briarはカメラにアクセスする必要があります。\n\nBluetooth端末を検出するには、Briarは現在地情報にアクセスする許可が必要です。\n\nBriarは現在地を保存したり、誰とも共有したりしません。</string>
<string name="permission_camera_denied_body">カメラへのアクセスをが拒否されましたが、連絡先を追加するにはカメラを使用する必要があります。\n\nカメラへのアクセスの許可を考えてください。</string> <string name="permission_camera_bluetooth_title">カメラと付近の端末</string>
<string name="permission_location_denied_body">あなたの位置情報にアクセスすることを拒否しましたが、BriarはBluetooth端末を発見するのに、この権限が必要です。\n\nアクセス権を付与することを考慮願います。</string> <string name="permission_camera_bluetooth_request_body">QRコードを読み取るには、Briarはカメラにアクセスする必要があります。\n\nBluetooth端末を検出するには、Briarは付近の端末を探し接続する許可が必要です。</string>
<string name="permission_camera_denied_body">あなたはカメラにアクセスすることを拒否しましたが、連絡先を追加するには、カメラを使用する必要があります。\n\nアクセス権を付与することを考慮願います。</string>
<string name="permission_location_denied_body">あなたは位置情報にアクセスすることを拒否しましたが、BriarはBluetooth端末を発見するのに、この権限が必要です。\n\nアクセス権を付与することを考慮願います。</string>
<string name="permission_location_setting_title">位置情報設定</string> <string name="permission_location_setting_title">位置情報設定</string>
<string name="permission_location_setting_body">端末の位置情報設定は、Bluetoothを介して他の端末を見つけるために、オンにする必要があります。続けるには位置情報を有効にしてください。その後、位置情報を無効にできます。</string> <string name="permission_location_setting_body">端末の位置情報設定は、Bluetoothを介して他の端末を見つけるために、オンにする必要があります。続けるには位置情報を有効にしてください。その後、位置情報を無効にできます。</string>
<string name="permission_location_setting_hotspot_body">端末の位置情報設定は、Wi-Fiホットスポットを作成するために、オンにする必要があります。続けるには位置情報を有効にしてください。その後、位置情報を無効にできます。</string>
<string name="permission_location_setting_button">位置情報を有効化</string> <string name="permission_location_setting_button">位置情報を有効化</string>
<string name="permission_bluetooth_title">付近の端末の権限</string>
<string name="permission_bluetooth_body">Bluetooth通信を使用するには、Briarは付近の端末を探し接続する権限が必要です。</string>
<string name="permission_bluetooth_denied_body">あなたは付近の端末にアクセスすることを拒否しましたが、BriarはBluetoothを使用するのに、この権限が必要です。\n\nアクセス権を付与することを考慮願います。</string>
<string name="qr_code">QRコード</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--> <!--App Locking-->
@@ -739,17 +754,19 @@
<string name="hotspot_notification_title">Briarをオフラインで共有</string> <string name="hotspot_notification_title">Briarをオフラインで共有</string>
<string name="hotspot_button_connected">次へ</string> <string name="hotspot_button_connected">次へ</string>
<string name="permission_hotspot_location_request_body">Wi-Fiホットスポットを作るには、Briarはあなたの位置情報にアクセスする権限が必要です。\n\nBriarはあなたの位置情報を保存せず、誰かに共有することもありません。</string> <string name="permission_hotspot_location_request_body">Wi-Fiホットスポットを作るには、Briarはあなたの位置情報にアクセスする権限が必要です。\n\nBriarはあなたの位置情報を保存せず、誰かに共有することもありません。</string>
<string name="permission_hotspot_location_denied_body">あなたの位置情報にアクセスすることを拒否しましたが、BriarはWi-Fiホットスポットを作るのに、この権限が必要です。\n\nアクセス権を付与することを考慮願います</string> <string name="permission_hotspot_location_request_precise_body">Wi-Fiホットスポットを作るには、Briarはあなたの正確な位置情報にアクセスする権限が必要です。\n\nBriarはあなたの位置情報を保存せず、誰かに共有することもありません</string>
<string name="permission_hotspot_location_denied_body">あなたは位置情報にアクセスすることを拒否しましたが、BriarはWi-Fiホットスポットを作るのに、この権限が必要です。\n\nアクセス権を付与することを考慮願います。</string>
<string name="permission_hotspot_location_denied_precise_body">あなたは正確な位置情報にアクセスすることを拒否しましたが、BriarはWi-Fiホットスポットを作るのに、この権限が必要です。\n\nアクセス権を付与することを考慮願います。</string>
<string name="wifi_settings_title">Wi-Fi設定</string> <string name="wifi_settings_title">Wi-Fi設定</string>
<string name="wifi_settings_request_enable_body">Wi-Fiホットスポットを作るには、BriarWi-Fiの使用が必要です。有効にしてください。</string> <string name="wifi_settings_request_enable_body">Wi-Fiホットスポットを作るには、BriarWi-Fiの使用が必要です。有効にしてください。</string>
<string name="hotspot_tab_manual">手動</string> <string name="hotspot_tab_manual">手動</string>
<!--The placeholder to be inserted into the string 'hotspot_manual_wifi': People can connect by %s--> <!--The placeholder to be inserted into the string 'hotspot_manual_wifi': People can connect by %s-->
<string name="hotspot_scanning_a_qr_code">QRコードをスキャン</string> <string name="hotspot_scanning_a_qr_code">QRコードを読み取る</string>
<!--Wi-Fi setup--> <!--Wi-Fi setup-->
<!--The %s placeholder will be replaced with the translation of 'hotspot_scanning_a_qr_code'--> <!--The %s placeholder will be replaced with the translation of 'hotspot_scanning_a_qr_code'-->
<string name="hotspot_manual_wifi">あなたの電話機はWi-Fiホットスポットを提供しています。Briarのダウンロードを希望する人は、以下の方法で端末のWi-Fi設定に追加するか、または %s によるホットスポットに接続してください。そのホットスポットに接続されたら、「次へ」を押してください。</string> <string name="hotspot_manual_wifi">あなたの電話機はWi-Fiホットスポットを提供しています。Briarのダウンロードを希望する人は、以下の方法で端末のWi-Fi設定に追加するか、または %s によるホットスポットに接続してください。そのホットスポットに接続されたら、「次へ」を押してください。</string>
<string name="hotspot_manual_wifi_ssid">ネットワーク名</string> <string name="hotspot_manual_wifi_ssid">ネットワーク名</string>
<string name="hotspot_qr_wifi">あなたの電話機はWi-Fiホットスポットを提供しています。Briarのダウンロードを希望する人は、このQRコードをスキャンしてホットスポットに接続してください。そのホットスポットに接続されたら、「次へ」を押してください。</string> <string name="hotspot_qr_wifi">あなたの電話機はWi-Fiホットスポットを提供しています。Briarのダウンロードを希望する人は、このQRコードを読み取ってホットスポットに接続してください。そのホットスポットに接続されたら、「次へ」を押してください。</string>
<string name="hotspot_no_peers_connected">接続された端末なし</string> <string name="hotspot_no_peers_connected">接続された端末なし</string>
<plurals name="hotspot_peers_connected"> <plurals name="hotspot_peers_connected">
<item quantity="other">%s機の接続された端末</item> <item quantity="other">%s機の接続された端末</item>
@@ -758,7 +775,7 @@
<!--The %s placeholder will be replaced with the translation of 'hotspot_scanning_a_qr_code'--> <!--The %s placeholder will be replaced with the translation of 'hotspot_scanning_a_qr_code'-->
<string name="hotspot_manual_site">あなたの電話機はWi-Fiホットスポットを提供しています。ホットスポットに接続された人は、ウェブブラウザまたは %s 内で以下のリンクを入力して、Briarをダウンロードできます。</string> <string name="hotspot_manual_site">あなたの電話機はWi-Fiホットスポットを提供しています。ホットスポットに接続された人は、ウェブブラウザまたは %s 内で以下のリンクを入力して、Briarをダウンロードできます。</string>
<string name="hotspot_manual_site_address">アドレスURL</string> <string name="hotspot_manual_site_address">アドレスURL</string>
<string name="hotspot_qr_site">あなたの電話機はWi-Fiホットスポットを提供しています。ホットスポットに接続された人は、このQRコードをスキャンして、Briarをダウンロードできます。</string> <string name="hotspot_qr_site">あなたの電話機はWi-Fiホットスポットを提供しています。ホットスポットに接続された人は、このQRコードを読み取って、Briarをダウンロードできます。</string>
<!--e.g. Download Briar 1.2.20--> <!--e.g. Download Briar 1.2.20-->
<string name="website_download_title_1">Briar %sをダウンロード</string> <string name="website_download_title_1">Briar %sをダウンロード</string>
<string name="website_download_intro_1">近くの誰かが、あなたとBriarを共有しました。</string> <string name="website_download_intro_1">近くの誰かが、あなたとBriarを共有しました。</string>

View File

@@ -588,9 +588,7 @@
<string name="mailbox_setup_download_link">ჩამოტვირთვის ბმულის გაზიარება</string> <string name="mailbox_setup_download_link">ჩამოტვირთვის ბმულის გაზიარება</string>
<string name="mailbox_setup_button_scan">საფოსტო ყუთის QR კოდის სკანირება</string> <string name="mailbox_setup_button_scan">საფოსტო ყუთის QR კოდის სკანირება</string>
<string name="permission_camera_qr_denied_body">თქვენ უარყავით კამერაზე წვდომა, მაგრამ QR კოდის სკანირებისთვის საჭიროა კამერის გამოყენება.\n\nგთხოვთ, განიხილოთ წვდომის მინიჭება.</string> <string name="permission_camera_qr_denied_body">თქვენ უარყავით კამერაზე წვდომა, მაგრამ QR კოდის სკანირებისთვის საჭიროა კამერის გამოყენება.\n\nგთხოვთ, განიხილოთ წვდომის მინიჭება.</string>
<string name="mailbox_setup_connecting">უკავშირდება…</string> <!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_qr_code_wrong_title">არასწორი QR კოდი</string>
<string name="mailbox_setup_qr_code_wrong_description">სკანირებული კოდი არასწორია. გთხოვთ გახსნათ Briar Mailbox აპი თქვენს მოწყობილობაზე საფოსტო ყუთით და დაასკანიროთ იქ არსებული QR კოდი.</string>
<string name="mailbox_setup_already_paired_title">საფოსტო ყუთი უკვე დაკავშირებულია</string> <string name="mailbox_setup_already_paired_title">საფოსტო ყუთი უკვე დაკავშირებულია</string>
<string name="mailbox_setup_already_paired_description">ჩახსენით საფოსტო ყუთი სათადარიგო მოწყობილობაზე და ისევ სცადეთ.</string> <string name="mailbox_setup_already_paired_description">ჩახსენით საფოსტო ყუთი სათადარიგო მოწყობილობაზე და ისევ სცადეთ.</string>
<string name="mailbox_setup_io_error_title">დაკავშირება ვერ ხერხდება</string> <string name="mailbox_setup_io_error_title">დაკავშირება ვერ ხერხდება</string>

View File

@@ -604,8 +604,7 @@
\nGalite įsidiegti Briar pašto dėžutę laisvame atsarginiame įrenginyje. Palikite įrenginį prijungtą prie maitinimo šaltinio ir belaidžio (Wi-Fi) ryšio, kad jis būtų pastoviai prijungtas prie interneto.</string> \nGalite įsidiegti Briar pašto dėžutę laisvame atsarginiame įrenginyje. Palikite įrenginį prijungtą prie maitinimo šaltinio ir belaidžio (Wi-Fi) ryšio, kad jis būtų pastoviai prijungtas prie interneto.</string>
<string name="mailbox_setup_download_link">Bendrinti atsisiuntimo nuorodą</string> <string name="mailbox_setup_download_link">Bendrinti atsisiuntimo nuorodą</string>
<string name="mailbox_setup_button_scan">Skenuoti pašto dėžutės QR kodą</string> <string name="mailbox_setup_button_scan">Skenuoti pašto dėžutės QR kodą</string>
<string name="mailbox_setup_connecting">Jungiamasi…</string> <!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_qr_code_wrong_title">Neteisingas QR kodas</string>
<string name="mailbox_setup_already_paired_title">Pašto dėžutė jau susieta</string> <string name="mailbox_setup_already_paired_title">Pašto dėžutė jau susieta</string>
<string name="mailbox_setup_io_error_title">Nepavyko prisijungti</string> <string name="mailbox_setup_io_error_title">Nepavyko prisijungti</string>
<string name="mailbox_setup_io_error_description">Įsitikinkite, kad abu įrenginiai yra prisijungę prie interneto ir bandykite dar kartą.</string> <string name="mailbox_setup_io_error_description">Įsitikinkite, kad abu įrenginiai yra prisijungę prie interneto ir bandykite dar kartą.</string>

View File

@@ -283,7 +283,11 @@
<string name="duplicate_link_dialog_text_3">%1$sနှင့်%2$s တူညီသောလင့်ခ်ကို သင့်အား ပေးပို့ခဲ့သည်။ \n\n သူတို့ထဲမှ တစ်ဦးက သင့်အဆက်အသွယ်များသည် မည်သူဖြစ်သည်ကို ရှာဖွေရန် ကြိုးစားနေပေမည်။ \n\nသင်သည် အခြားသူတစ်ဦးထံမှ တူညီသောလင့်ခ်ကို ရရှိထားကြောင်း ၎င်းတို့အား မပြောပါနှင့်။</string> <string name="duplicate_link_dialog_text_3">%1$sနှင့်%2$s တူညီသောလင့်ခ်ကို သင့်အား ပေးပို့ခဲ့သည်။ \n\n သူတို့ထဲမှ တစ်ဦးက သင့်အဆက်အသွယ်များသည် မည်သူဖြစ်သည်ကို ရှာဖွေရန် ကြိုးစားနေပေမည်။ \n\nသင်သည် အခြားသူတစ်ဦးထံမှ တူညီသောလင့်ခ်ကို ရရှိထားကြောင်း ၎င်းတို့အား မပြောပါနှင့်။</string>
<string name="pending_contact_updated_toast">ဆိုင်းငံ့ထားသော အဆက်အသွယ်ကို အပ်ဒိတ်လုပ်ပြီး</string> <string name="pending_contact_updated_toast">ဆိုင်းငံ့ထားသော အဆက်အသွယ်ကို အပ်ဒိတ်လုပ်ပြီး</string>
<!--Peer trust levels--> <!--Peer trust levels-->
<string name="peer_trust_level_unverified">မစိစစ်ရသေးသော အဆက်အသွယ်</string>
<string name="peer_trust_level_verified">စိစစ်ထားပြီးသော အဆက်အသွယ်
</string>
<string name="peer_trust_level_ourselves">ကျွနု်ပ်ကို</string> <string name="peer_trust_level_ourselves">ကျွနု်ပ်ကို</string>
<string name="peer_trust_level_stranger">သူစိမ်း</string>
<!--Introductions--> <!--Introductions-->
<string name="introduction_onboarding_title">သင့်ရဲ့ အဆက်အသွယ်လိပ်စာများကို မိတ်ဆက်ပါ</string> <string name="introduction_onboarding_title">သင့်ရဲ့ အဆက်အသွယ်လိပ်စာများကို မိတ်ဆက်ပါ</string>
<string name="introduction_menu_item">မိတ်ဆက်ခြင်း ပြုလုပ်ပါ</string> <string name="introduction_menu_item">မိတ်ဆက်ခြင်း ပြုလုပ်ပါ</string>
@@ -404,6 +408,8 @@
<string name="forum_declined_toast">ဖိတ်ကြားမှု ငြင်းဆိုခဲ့သည်</string> <string name="forum_declined_toast">ဖိတ်ကြားမှု ငြင်းဆိုခဲ့သည်</string>
<string name="shared_by_format">%s မှ မျှဝေခဲ့သည်</string> <string name="shared_by_format">%s မှ မျှဝေခဲ့သည်</string>
<string name="forum_invitation_already_sharing">မျှဝေထားခြင်းဖြစ်သည်</string> <string name="forum_invitation_already_sharing">မျှဝေထားခြင်းဖြစ်သည်</string>
<string name="forum_invitation_already_invited">ဖိတ်ကြားစာ ပို့ပြီးပြီ</string>
<string name="forum_invitation_invite_received">ဖိတ်ကြားစာ လက်ခံရရှိသည်</string>
<string name="forum_invitation_response_accepted_sent">%s မှ ဖိတ်ကြားမှုကို သင်သည် လက်ခံခဲ့ပါသည်။</string> <string name="forum_invitation_response_accepted_sent">%s မှ ဖိတ်ကြားမှုကို သင်သည် လက်ခံခဲ့ပါသည်။</string>
<string name="forum_invitation_response_declined_sent">%s မှ ဖိတ်ကြားမှုကို သင်သည် ငြင်းဆိုခဲ့ပါသည်။</string> <string name="forum_invitation_response_declined_sent">%s မှ ဖိတ်ကြားမှုကို သင်သည် ငြင်းဆိုခဲ့ပါသည်။</string>
<string name="forum_invitation_response_declined_auto">%s မှ ပို့ထားသော ဖိုရမ်သို့ ဖိတ်ခေါ်ချက်ကို အလိုအလျောက် ငြင်းပယ်ခဲ့ပါသည်။</string> <string name="forum_invitation_response_declined_auto">%s မှ ပို့ထားသော ဖိုရမ်သို့ ဖိတ်ခေါ်ချက်ကို အလိုအလျောက် ငြင်းပယ်ခဲ့ပါသည်။</string>
@@ -565,8 +571,7 @@
<string name="mailbox_setup_download_link">ဒေါင်းလုဒ်လင့်ခ်ကို မျှဝေပါ</string> <string name="mailbox_setup_download_link">ဒေါင်းလုဒ်လင့်ခ်ကို မျှဝေပါ</string>
<string name="mailbox_setup_button_scan">စာတိုက်ပုံး QR ကုဒ်ကို စကင်န်ဖတ်ပါ</string> <string name="mailbox_setup_button_scan">စာတိုက်ပုံး QR ကုဒ်ကို စကင်န်ဖတ်ပါ</string>
<string name="permission_camera_qr_denied_body">သင်သည် ကင်မရာအသုံးပြုခွင့်ကို ငြင်းပယ်ထားသော်လည်း QR ကုဒ်ကို စကင်န်ဖတ်ရန် ကင်မရာကို အသုံးပြုရန် လိုအပ်ပါသည်။ \n\nကျေးဇူးပြု၍ ဝင်ရောက်ခွင့်ပြုရန် စဉ်းစားပါ။</string> <string name="permission_camera_qr_denied_body">သင်သည် ကင်မရာအသုံးပြုခွင့်ကို ငြင်းပယ်ထားသော်လည်း QR ကုဒ်ကို စကင်န်ဖတ်ရန် ကင်မရာကို အသုံးပြုရန် လိုအပ်ပါသည်။ \n\nကျေးဇူးပြု၍ ဝင်ရောက်ခွင့်ပြုရန် စဉ်းစားပါ။</string>
<string name="mailbox_setup_connecting">ချိတ်ဆက်နေသည်…</string> <!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_qr_code_wrong_title">QR ကုဒ် မှားနေသည်</string>
<string name="mailbox_setup_paired_title">ချိတ်ဆက်ပြီးပါပြီ</string> <string name="mailbox_setup_paired_title">ချိတ်ဆက်ပြီးပါပြီ</string>
<string name="tor_offline_title">အော့ဖ်လိုင်း</string> <string name="tor_offline_title">အော့ဖ်လိုင်း</string>
<string name="tor_offline_button_check">ချိတ်ဆက်မှု ဆက်တင်များကို စစ်ဆေးပါ</string> <string name="tor_offline_button_check">ချိတ်ဆက်မှု ဆက်တင်များကို စစ်ဆေးပါ</string>

View File

@@ -589,9 +589,7 @@
<string name="mailbox_setup_download_link">Dele nedlastingslenke</string> <string name="mailbox_setup_download_link">Dele nedlastingslenke</string>
<string name="mailbox_setup_button_scan">Skanne Mailboks-QR-kode</string> <string name="mailbox_setup_button_scan">Skanne Mailboks-QR-kode</string>
<string name="permission_camera_qr_denied_body">Du har forbudt tilgang til kameraet, men for å kunne skanne QR-koder kreves kameratilgang.\n\nVennligst vurder å gi tilgang.</string> <string name="permission_camera_qr_denied_body">Du har forbudt tilgang til kameraet, men for å kunne skanne QR-koder kreves kameratilgang.\n\nVennligst vurder å gi tilgang.</string>
<string name="mailbox_setup_connecting">Kobler til…</string> <!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_qr_code_wrong_title">Feil QR-kode</string>
<string name="mailbox_setup_qr_code_wrong_description">Den skannede koden er ugyldig. Vennligst åpne Briar Mailboks-app\'en på din Mailboks-enhet og skann QR-koden den viser frem.</string>
<string name="mailbox_setup_already_paired_title">Mailboks\'en er allerede lenket opp</string> <string name="mailbox_setup_already_paired_title">Mailboks\'en er allerede lenket opp</string>
<string name="mailbox_setup_already_paired_description">Lenke fra Mailboks-appen på din andre enhet og prøv igjen.</string> <string name="mailbox_setup_already_paired_description">Lenke fra Mailboks-appen på din andre enhet og prøv igjen.</string>
<string name="mailbox_setup_io_error_title">Kunne ikke koble til</string> <string name="mailbox_setup_io_error_title">Kunne ikke koble til</string>

View File

@@ -618,9 +618,7 @@
<string name="mailbox_setup_download_link">Udostępnij łącze pobierania</string> <string name="mailbox_setup_download_link">Udostępnij łącze pobierania</string>
<string name="mailbox_setup_button_scan">Zeskanuj kod QR aplikacji Mailbox</string> <string name="mailbox_setup_button_scan">Zeskanuj kod QR aplikacji Mailbox</string>
<string name="permission_camera_qr_denied_body">Odmówiłeś dostępu do aparatu, ale zeskanowanie kodu QR wymaga użycia aparatu.\n\nRozważ przyznanie dostępu.</string> <string name="permission_camera_qr_denied_body">Odmówiłeś dostępu do aparatu, ale zeskanowanie kodu QR wymaga użycia aparatu.\n\nRozważ przyznanie dostępu.</string>
<string name="mailbox_setup_connecting">Trwa łączenie...</string> <!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_qr_code_wrong_title">Nieprawidłowy kod QR</string>
<string name="mailbox_setup_qr_code_wrong_description">Zeskanowany kod jest nieprawidłowy. Otwórz aplikację Briar Mailbox na urządzeniu z Mailbox i zeskanuj wyświetlany kod QR. </string>
<string name="mailbox_setup_already_paired_title">Mailbox już podłączony</string> <string name="mailbox_setup_already_paired_title">Mailbox już podłączony</string>
<string name="mailbox_setup_already_paired_description">Odłącz Mailbox na drugim urządzeniu i spróbuj ponownie.</string> <string name="mailbox_setup_already_paired_description">Odłącz Mailbox na drugim urządzeniu i spróbuj ponownie.</string>
<string name="mailbox_setup_io_error_title">Nie udało się połączyć</string> <string name="mailbox_setup_io_error_title">Nie udało się połączyć</string>

View File

@@ -600,9 +600,7 @@
<string name="mailbox_setup_download_link">Compartilhar Link de Download</string> <string name="mailbox_setup_download_link">Compartilhar Link de Download</string>
<string name="mailbox_setup_button_scan">Escanear código QR do Mailbox</string> <string name="mailbox_setup_button_scan">Escanear código QR do Mailbox</string>
<string name="permission_camera_qr_denied_body">Você negou acesso à câmera, mas escanear um código QR requer o uso da câmera.\n\nPor favor, considere conceder acesso.</string> <string name="permission_camera_qr_denied_body">Você negou acesso à câmera, mas escanear um código QR requer o uso da câmera.\n\nPor favor, considere conceder acesso.</string>
<string name="mailbox_setup_connecting">Conectando...</string> <!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_qr_code_wrong_title">Código QR errado</string>
<string name="mailbox_setup_qr_code_wrong_description">O código escaneado é inválido. Por favor, abra o app Briar Mailbox no seu dispositivo Mailbox e escaneie o código QR apresentado.</string>
<string name="mailbox_setup_already_paired_title">Mailbox já vinculado</string> <string name="mailbox_setup_already_paired_title">Mailbox já vinculado</string>
<string name="mailbox_setup_already_paired_description">Desvincule o Mailbox no seu outro dispositivo e tente de novo.</string> <string name="mailbox_setup_already_paired_description">Desvincule o Mailbox no seu outro dispositivo e tente de novo.</string>
<string name="mailbox_setup_io_error_title">Não foi possível conectar</string> <string name="mailbox_setup_io_error_title">Não foi possível conectar</string>

View File

@@ -161,6 +161,7 @@
<string name="error_start_activity">Indisponibil pentru sistemul dvs.</string> <string name="error_start_activity">Indisponibil pentru sistemul dvs.</string>
<string name="status_heading">Stare:</string> <string name="status_heading">Stare:</string>
<string name="error">Eroare</string> <string name="error">Eroare</string>
<string name="info">Informații</string>
<!--Contacts and Private Conversations--> <!--Contacts and Private Conversations-->
<string name="no_contacts">Niciun contact de afișat</string> <string name="no_contacts">Niciun contact de afișat</string>
<string name="no_contacts_action">Atingeți iconița + pentru a adăuga un contact</string> <string name="no_contacts_action">Atingeți iconița + pentru a adăuga un contact</string>
@@ -234,6 +235,7 @@
<string name="menu_contact">Contact</string> <string name="menu_contact">Contact</string>
<!--Adding Contacts--> <!--Adding Contacts-->
<string name="add_contact_title">Adăugați un contact din apropiere</string> <string name="add_contact_title">Adăugați un contact din apropiere</string>
<string name="add_contact_error_two_way">Ați scanat amândoi codurile QR ale celuilalt?</string>
<string name="face_to_face">Trebuie să vă întâlniți cu persoana pe care doriți să o adăugați la contacte.\n\nAcest pas împiedică alte persoane să se substituie dvs. sau să citească mesajele dvs. în viitor.</string> <string name="face_to_face">Trebuie să vă întâlniți cu persoana pe care doriți să o adăugați la contacte.\n\nAcest pas împiedică alte persoane să se substituie dvs. sau să citească mesajele dvs. în viitor.</string>
<string name="continue_button">Continuați</string> <string name="continue_button">Continuați</string>
<string name="try_again_button">Încercați din nou</string> <string name="try_again_button">Încercați din nou</string>
@@ -244,11 +246,14 @@
<string name="qr_code_invalid">Codul QR este nevalid!</string> <string name="qr_code_invalid">Codul QR este nevalid!</string>
<string name="qr_code_too_old_1">Codul QR pe care l-ați scanat provine de la o versiune Briar mai veche.\n\nSolicitați contactului să actualizeze aplicația la cea mai recentă versiune, după care încercați din nou.</string> <string name="qr_code_too_old_1">Codul QR pe care l-ați scanat provine de la o versiune Briar mai veche.\n\nSolicitați contactului să actualizeze aplicația la cea mai recentă versiune, după care încercați din nou.</string>
<string name="qr_code_too_new_1">Codul QR pe care l-ați scanat provine de la o versiune Briar mai nouă.\n\nActualizați aplicația la cea mai recentă versiune, după care încercați din nou.</string> <string name="qr_code_too_new_1">Codul QR pe care l-ați scanat provine de la o versiune Briar mai nouă.\n\nActualizați aplicația la cea mai recentă versiune, după care încercați din nou.</string>
<string name="mailbox_qr_code_for_contact">Codul QR pe care l-ați scanat provine de la o Cutie poștală Briar.\n\nDacă doriți să conectați Cutia poștală, vă rugăm să mergeți în Setări &gt; Cutie Poștală în meniul Briar..</string>
<string name="qr_code_format_unknown">Codul QR pe care l-ați scanat nu este menit pentru a adăuga un contact Briar.\n\nVă rugăm să scanați codul QR afișat pe ecranul contactului dumneavoastră.</string>
<string name="camera_error">Eroare cameră</string> <string name="camera_error">Eroare cameră</string>
<string name="connecting_to_device">Se conectează la dispozitiv\u2026</string> <string name="connecting_to_device">Se conectează la dispozitiv\u2026</string>
<string name="authenticating_with_device">Se realizează autentificarea cu dispozitivul\u2026</string> <string name="authenticating_with_device">Se realizează autentificarea cu dispozitivul\u2026</string>
<string name="connection_error_title">Nu a fost posibilă conectarea cu contactul dvs.</string> <string name="connection_error_title">Nu a fost posibilă conectarea cu contactul dvs.</string>
<string name="connection_error_feedback">Dacă problema persistă, <a href="feedback">trimiteți feedback</a> pentru a ne ajuta să îmbunătățim aplicația.</string> <string name="connection_error_feedback">Dacă problema persistă, <a href="feedback">trimiteți feedback</a> pentru a ne ajuta să îmbunătățim aplicația.</string>
<string name="info_both_must_scan">Trebuie să scanați amândoi codurile QR ale celuilalt.</string>
<!--Adding Contacts Remotely--> <!--Adding Contacts Remotely-->
<string name="add_contact_remotely_title_case">Adăugați contact de la distanță</string> <string name="add_contact_remotely_title_case">Adăugați contact de la distanță</string>
<string name="add_contact_nearby_title">Adaugă un contact din apropiere</string> <string name="add_contact_nearby_title">Adaugă un contact din apropiere</string>
@@ -307,6 +312,7 @@
<string name="different_person_button">Persoană diferită</string> <string name="different_person_button">Persoană diferită</string>
<string name="duplicate_link_dialog_text_3">%1$s și %2$sv-au trimis același link.\n\nUna dintre aceste persoane ar putea încerca să afle cine sunt contactele dvs.\n\nNu îi spuneți că ați primit același link de la altcineva.</string> <string name="duplicate_link_dialog_text_3">%1$s și %2$sv-au trimis același link.\n\nUna dintre aceste persoane ar putea încerca să afle cine sunt contactele dvs.\n\nNu îi spuneți că ați primit același link de la altcineva.</string>
<string name="pending_contact_updated_toast">Contact în așteptare actualizat</string> <string name="pending_contact_updated_toast">Contact în așteptare actualizat</string>
<string name="info_both_must_enter_links">Amândoi trebuie să adăugați linkurile celuilalt</string>
<!--Peer trust levels--> <!--Peer trust levels-->
<string name="peer_trust_level_unverified">Contact neverificat</string> <string name="peer_trust_level_unverified">Contact neverificat</string>
<string name="peer_trust_level_verified">Contact verificat</string> <string name="peer_trust_level_verified">Contact verificat</string>
@@ -340,6 +346,7 @@
<string name="connect_via_bluetooth_intro">În cazul în care conexiunile Bluetooth nu funcționează automat, puteți utiliza acest ecran pentru a vă conecta manual.\n\nContactul trebuie să fie în apropiere pentru ca procedura să funcționeze.\n\nDvs. și contactul trebuie să apăsați pe „Start” simultan.</string> <string name="connect_via_bluetooth_intro">În cazul în care conexiunile Bluetooth nu funcționează automat, puteți utiliza acest ecran pentru a vă conecta manual.\n\nContactul trebuie să fie în apropiere pentru ca procedura să funcționeze.\n\nDvs. și contactul trebuie să apăsați pe „Start” simultan.</string>
<string name="connect_via_bluetooth_already_discovering">Încercați deja să vă conectați prin Bluetooth. Încercați din nou în scurt timp.</string> <string name="connect_via_bluetooth_already_discovering">Încercați deja să vă conectați prin Bluetooth. Încercați din nou în scurt timp.</string>
<string name="connect_via_bluetooth_no_location_permission">Nu se poate continua fără a permite accesul la locație</string> <string name="connect_via_bluetooth_no_location_permission">Nu se poate continua fără a permite accesul la locație</string>
<string name="connect_via_bluetooth_no_bluetooth_permission">Nu se poate continua fără permisiunea dispozitivelor din apropiere</string>
<string name="connect_via_bluetooth_start">Se conectează prin Bluetooth...</string> <string name="connect_via_bluetooth_start">Se conectează prin Bluetooth...</string>
<string name="connect_via_bluetooth_success">Conectare reușită prin Bluetooth</string> <string name="connect_via_bluetooth_success">Conectare reușită prin Bluetooth</string>
<string name="connect_via_bluetooth_error">Nu s-a reușit conectarea prin Bluetooth.</string> <string name="connect_via_bluetooth_error">Nu s-a reușit conectarea prin Bluetooth.</string>
@@ -603,9 +610,13 @@
<string name="mailbox_setup_download_link">Partajați linkul de descărcare</string> <string name="mailbox_setup_download_link">Partajați linkul de descărcare</string>
<string name="mailbox_setup_button_scan">Scanați codul QR al cutiei poștale</string> <string name="mailbox_setup_button_scan">Scanați codul QR al cutiei poștale</string>
<string name="permission_camera_qr_denied_body">Ați refuzat accesul la cameră, dar scanarea unui cod QR necesită utilizarea camerei.\n\nLuați în considerare acordarea accesului.</string> <string name="permission_camera_qr_denied_body">Ați refuzat accesul la cameră, dar scanarea unui cod QR necesită utilizarea camerei.\n\nLuați în considerare acordarea accesului.</string>
<string name="mailbox_setup_connecting">Se conectează...</string> <string name="mailbox_setup_connecting">Se conectează la Cutia poștală…</string>
<string name="mailbox_setup_qr_code_wrong_title">Cod QR greșit</string> <!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
<string name="mailbox_setup_qr_code_wrong_description">Codul scanat nu este valid. Deschideți aplicația de cutie poștală Briar pe dispozitivul pe care este instalată și scanați codul QR pe care îl afișează.</string> <string name="mailbox_setup_connecting_info">Asta ar putea dura până la %1s</string>
<string name="mailbox_qr_code_too_old">Codul QR pe care l-ați scanat provine de la o versiune de Cutie poștală Briar mai veche.\n\nActualizați Cutia poștală la cea mai recentă versiune, după care încercați din nou.</string>
<string name="mailbox_qr_code_too_new">Codul QR pe care l-ați scanat provine de la o versiune de Cutie poștală Briar mai nouă.\n\nActualizați Briar la cea mai recentă versiune, după care încercați din nou.</string>
<string name="contact_qr_code_for_mailbox">Codul QR pe care l-ați scanat este pentru a adăuga un contact Briar.\n\nDacă doriți să adăugați un contact, vă rugăm să mergeți la lista de contacte și să apăsați +.</string>
<string name="mailbox_setup_qr_code_wrong_description">Codul QR pe care l-ați scanat nu provine de la o Cutie poștală Briar.\n\nVă rugăm să deschideți aplicația de Cutie poștală Briar pe dispozitivul pe care este instalată și scanați codul QR pe care îl afișează.</string>
<string name="mailbox_setup_already_paired_title">Cutia poștală este deja conectată</string> <string name="mailbox_setup_already_paired_title">Cutia poștală este deja conectată</string>
<string name="mailbox_setup_already_paired_description">Anulați conectarea cutiei poștale la celălalt dispozitiv și încercați din nou.</string> <string name="mailbox_setup_already_paired_description">Anulați conectarea cutiei poștale la celălalt dispozitiv și încercați din nou.</string>
<string name="mailbox_setup_io_error_title">Nu s-a reușit conectarea</string> <string name="mailbox_setup_io_error_title">Nu s-a reușit conectarea</string>
@@ -742,11 +753,17 @@
<string name="permission_location_request_body">Pentru a putea descoperi dispozitive Bluetooth, Briar are nevoie de permisiunea de a accesa locația dvs.\n\nBriar nu stochează locația dvs. și nici nu o partajează cu nimeni. </string> <string name="permission_location_request_body">Pentru a putea descoperi dispozitive Bluetooth, Briar are nevoie de permisiunea de a accesa locația dvs.\n\nBriar nu stochează locația dvs. și nici nu o partajează cu nimeni. </string>
<string name="permission_camera_location_title">Cameră foto și locație</string> <string name="permission_camera_location_title">Cameră foto și locație</string>
<string name="permission_camera_location_request_body">Pentru a scana codul QR, Briar are nevoie de acces la camera foto.\n\nPentru a putea descoperi dispozitive Bluetooth, Briar are nevoie de permisiunea de acces la locația dvs.\n\nBriar nu stochează locația dvs. și nici nu o partajează cu nimeni.</string> <string name="permission_camera_location_request_body">Pentru a scana codul QR, Briar are nevoie de acces la camera foto.\n\nPentru a putea descoperi dispozitive Bluetooth, Briar are nevoie de permisiunea de acces la locația dvs.\n\nBriar nu stochează locația dvs. și nici nu o partajează cu nimeni.</string>
<string name="permission_camera_bluetooth_title">Camera și dispozitivele din apropiere</string>
<string name="permission_camera_bluetooth_request_body">Pentru a scana codul QR, Briar are nevoie de acces la camera foto.\n\nPentru a descoperi dispozitive Bluetooth, Briar are nevoie de permisiunea de a găsi și de a se conecta la dispozitivele din apropiere.</string>
<string name="permission_camera_denied_body">Ați refuzat accesul la camera foto, dar folosirea acesteia este obligatorie pentru a adăuga contacte.\n\nVă rugăm să luați în considerare acordarea accesului.</string> <string name="permission_camera_denied_body">Ați refuzat accesul la camera foto, dar folosirea acesteia este obligatorie pentru a adăuga contacte.\n\nVă rugăm să luați în considerare acordarea accesului.</string>
<string name="permission_location_denied_body">Ați refuzat accesul la locație, dar Briar are nevoie de această permisiune pentru a descoperi dispozitive Bluetooth.\n\nVă rugăm să luați în considerare acordarea accesului.</string> <string name="permission_location_denied_body">Ați refuzat accesul la locație, dar Briar are nevoie de această permisiune pentru a descoperi dispozitive Bluetooth.\n\nVă rugăm să luați în considerare acordarea accesului.</string>
<string name="permission_location_setting_title">Setări privind locația</string> <string name="permission_location_setting_title">Setări privind locația</string>
<string name="permission_location_setting_body">Funcția de locație a dispozitivului dvs. trebuie să fie activată pentru a găsi alte dispozitive prin Bluetooth. Activați locația pentru a continua. Puteți să o dezactivați din nou ulterior.</string> <string name="permission_location_setting_body">Funcția de locație a dispozitivului dvs. trebuie să fie activată pentru a găsi alte dispozitive prin Bluetooth. Activați locația pentru a continua. Puteți să o dezactivați din nou ulterior.</string>
<string name="permission_location_setting_hotspot_body">Setarea de localizare a dispozitivului dumneavoastră trebuie să fie activată pentru a crea un hotspot Wi-Fi. Vă rugăm să activați localizarea pentru a continua. Puteți să o dezactivați din nou după aceea.</string>
<string name="permission_location_setting_button">Activați locația</string> <string name="permission_location_setting_button">Activați locația</string>
<string name="permission_bluetooth_title">Permisiunea dispozitivelor apropiate</string>
<string name="permission_bluetooth_body">Pentru a utiliza comunicarea Bluetooth, Briar are nevoie de permisiunea de a găsi și de a se conecta la dispozitivele din apropiere.</string>
<string name="permission_bluetooth_denied_body">Ați refuzat accesul la dispozitivele din apropiere, dar Briar are nevoie de această permisiune pentru a utiliza Bluetooth.\n\nVă rugăm să luați în considerare acordarea accesului.</string>
<string name="qr_code">Cod QR</string> <string name="qr_code">Cod QR</string>
<string name="show_qr_code_fullscreen">Afișați codul QR pe tot ecranul</string> <string name="show_qr_code_fullscreen">Afișați codul QR pe tot ecranul</string>
<!--App Locking--> <!--App Locking-->
@@ -769,7 +786,9 @@
<string name="hotspot_notification_title">Se partajează Brian offline</string> <string name="hotspot_notification_title">Se partajează Brian offline</string>
<string name="hotspot_button_connected">Înainte</string> <string name="hotspot_button_connected">Înainte</string>
<string name="permission_hotspot_location_request_body">Pentru a crea un hotspot Wi-Fi, Briar are nevoie de permisiunea de a accesa locația dvs.\n\nBriar nu stochează locația dvs. și nici nu o partajează cu nimeni.</string> <string name="permission_hotspot_location_request_body">Pentru a crea un hotspot Wi-Fi, Briar are nevoie de permisiunea de a accesa locația dvs.\n\nBriar nu stochează locația dvs. și nici nu o partajează cu nimeni.</string>
<string name="permission_hotspot_location_request_precise_body">Pentru a crea un hotspot Wi-Fi, Briar are nevoie de permisiunea de a accesa locația dumneavoastră exactă.\n\nBriar nu stochează locația dumneavoastră și nu o împărtășește cu nimeni.</string>
<string name="permission_hotspot_location_denied_body">Ați refuzat accesul la locație, dar Briar are nevoie de această permisiune pentru a crea un hotspot Wi-Fi.\n\nVă rugăm să luați în considerare acordarea accesului.</string> <string name="permission_hotspot_location_denied_body">Ați refuzat accesul la locație, dar Briar are nevoie de această permisiune pentru a crea un hotspot Wi-Fi.\n\nVă rugăm să luați în considerare acordarea accesului.</string>
<string name="permission_hotspot_location_denied_precise_body">Ați refuzat accesul la locația dumneavoastră exactă, dar Briar are nevoie de această permisiune pentru a crea un hotspot Wi-Fi.Vă rugăm să luați în considerare acordarea accesului.</string>
<string name="wifi_settings_title">Se setează Wi-Fi</string> <string name="wifi_settings_title">Se setează Wi-Fi</string>
<string name="wifi_settings_request_enable_body">Pentru a crea un hotspot Wi-Fi, Briar trebuie să folosească Wi-Fi. Vă rugăm să activați funcția.</string> <string name="wifi_settings_request_enable_body">Pentru a crea un hotspot Wi-Fi, Briar trebuie să folosească Wi-Fi. Vă rugăm să activați funcția.</string>
<string name="hotspot_tab_manual">Manual</string> <string name="hotspot_tab_manual">Manual</string>

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