Compare commits

...

56 Commits

Author SHA1 Message Date
Michael Rogers
ef2286ab53 Bumped version number for beta release. 2017-09-20 14:51:10 +01:00
akwizgran
47b25f3221 Merge branch '1064-rss-date-npe' into 'master'
Fix NPE when some RSS items don't have dates and add test

Closes #1064

See merge request !591
2017-09-20 12:21:06 +00:00
Torsten Grote
c30bfa12ce Fix NPE when some RSS items don't have dates and add test 2017-09-20 09:11:06 -03:00
akwizgran
d0fc04251d Merge branch 'three-new-langs' into 'master'
Add Norwegian Bokmål, Occitan (post 1500) and Serbian

See merge request !593
2017-09-20 11:15:44 +00:00
akwizgran
dcbb41eb7a Merge branch '1069-forum-sharing-exception' into 'master'
Fix crash when sharing a forum while it was just shared with us

Closes #1069

See merge request !592
2017-09-20 11:14:20 +00:00
Torsten Grote
999bdf8866 Add Norwegian Bokmål, Occitan (post 1500) and Serbian 2017-09-19 14:47:39 -03:00
Torsten Grote
911c0c0fd9 Fix crash when sharing a forum while it was just shared with us 2017-09-19 14:30:57 -03:00
akwizgran
99d8cc64a6 Merge branch '1024-message-tree-npe' into 'master'
Don't add threaded messages to the UI before their parents

Closes #1024

See merge request !585
2017-09-19 15:37:58 +00:00
akwizgran
ba727d7568 Don't add threaded messages to the UI before their parents. 2017-09-19 16:31:27 +01:00
Torsten Grote
ed01048f9f Merge branch 'remove-old-bluetooth-code' into 'master'
Remove old Bluetooth code and location permission

See merge request !584
2017-09-19 14:16:13 +00:00
Torsten Grote
043ee3c58e Merge branch '1044-crash-when-setting-ringtone' into 'master'
Don't crash if the chosen ringtone can't be loaded

Closes #1044

See merge request !586
2017-09-19 13:11:44 +00:00
Torsten Grote
6e0af7deda Merge branch '1060-upgrade-tor' into 'master'
Upgrade Tor to 0.2.9.12

Closes #1060

See merge request !590
2017-09-19 12:14:55 +00:00
akwizgran
9591db2097 Upgrade Tor to 0.2.9.12.
Libevent 2.0.22-stable, OpenSSL 1.0.2l and GeoIP 2017-09-06.
2017-09-19 12:49:22 +01:00
akwizgran
329a4c64f6 Merge branch '1028-lost-reply-id' into 'master'
Keep the reply ID up to date in ThreadListActivity

Closes #1028

See merge request !587
2017-09-18 15:10:38 +00:00
Torsten Grote
79015bc5ae Merge branch '1042-catch-npe-when-getting-socket-streams' into 'master'
Catch NPE when getting socket input/output streams

Closes #1042

See merge request !589
2017-09-18 14:55:08 +00:00
akwizgran
27422ab9f9 Catch NPE when getting socket input/output streams.
Works around a bug in Android 7, fixed in 7.1.
2017-09-18 15:47:12 +01:00
Torsten Grote
abcb682498 Merge branch '1040-rss-feed-illegal-argument-exception' into 'master'
Catch IllegalArgumentException when parsing RSS feed

Closes #1040

See merge request !588
2017-09-18 14:38:22 +00:00
akwizgran
5044127c46 Catch IllegalArgumentException when parsing RSS feed. 2017-09-18 15:26:12 +01:00
akwizgran
0e4b8ca62e Keep the activity's reply ID up to date. 2017-09-18 15:13:16 +01:00
akwizgran
822017c69c Don't crash if the chosen ringtone can't be loaded. 2017-09-18 13:37:10 +01:00
akwizgran
eb6561b93d Updated translations for German, French and Russian. 2017-09-15 10:40:05 +01:00
Michael Rogers
d24b1884a2 Removed old Bluetooth code and the location permission it requires. 2017-08-11 12:42:47 +01:00
Michael Rogers
078534889e Bumped version number for beta release. 2017-08-04 15:16:51 +01:00
Torsten Grote
e92713006a Fix string in Spanish translation 2017-08-04 10:57:43 -03:00
akwizgran
18f43f3bc1 Merge branch '871-rss-feeds-lost' into 'master'
Fix bug where RSS feeds got lost when a fetching error occured

Closes #871

See merge request !583
2017-08-04 13:52:26 +00:00
akwizgran
a4118b40e1 Merge branch 'debug-build-alongside-beta' into 'master'
Make debug builds installable alongside official beta build

See merge request !582
2017-08-02 16:54:25 +00:00
Torsten Grote
de29fbc324 Fix bug where RSS feeds got lost when a fetching error occured 2017-08-01 15:32:51 -03:00
Torsten Grote
3197dcf9b5 Merge branch 'checked-camera-exceptions' into 'master'
Throw checked exceptions for camera errors

See merge request !580
2017-08-01 16:54:45 +00:00
akwizgran
35aad409fd Merge branch '994-notification-sound-delay' into 'master'
Always play a notification sound, if at least 2sec after last one

Closes #994

See merge request !581
2017-08-01 16:20:35 +00:00
Torsten Grote
08ce6a7331 Change app name for debug builds 2017-08-01 13:08:12 -03:00
Torsten Grote
33a0099065 Make debug builds installable alongside official beta build 2017-08-01 12:57:11 -03:00
Torsten Grote
34d20fafda Always play a notification sound, if at least 2sec after last one
This is the same behavior as Signal.
We might want to adjust the delay later on.

This is also introduces a new BriarNotificationBuilder as a first step
to clean up the Notification Manager code.
2017-08-01 12:47:11 -03:00
Michael Rogers
aafddcd0f0 Bumped version number for beta release (for real this time). 2017-08-01 16:43:47 +01:00
akwizgran
0d6983b4ef Throw checked exceptions for camera errors. 2017-08-01 15:56:20 +01:00
akwizgran
69bfb72171 Merge branch '1002-cam-get-params-npe' into 'master'
Catch RuntimeException when getting camera parameters

See merge request !579
2017-08-01 13:56:45 +00:00
Torsten Grote
1aa33ec9b2 Catch RuntimeException when getting camera parameters 2017-08-01 10:49:04 -03:00
akwizgran
6702df1e22 Merge branch '1008-qr-decoding-crash' into 'master'
Catch IllegalArgumentException when decoding QrCode

Closes #1008

See merge request !578
2017-08-01 13:36:09 +00:00
akwizgran
c1748c9a86 Bumped version number for beta release. 2017-08-01 14:32:05 +01:00
akwizgran
9df624c62a Merge branch '1009-camera-npe' into 'master'
Prevent NPE in CameraView

Closes #1009 and #997

See merge request !577
2017-08-01 13:29:33 +00:00
Torsten Grote
0ee6197d7f Catch IllegalArgumentException when decoding QrCode 2017-08-01 10:21:02 -03:00
Torsten Grote
b03a7dce3e Catch runtime exception when setting best camera parameters
Closes #997
2017-08-01 10:09:21 -03:00
Torsten Grote
6c59d7dd5f Prevent NPE in CameraView
This prevents crashes, but still might cause the camera to not show up
thus preventing the user from adding contacts.
2017-08-01 09:41:42 -03:00
Michael Rogers
050191f0ef Bumped version number for beta release. 2017-08-01 12:31:47 +01:00
akwizgran
4b5a19ce5d Merge branch 'update-translations' into 'master'
Update translations, add Turkish and Russian

See merge request !575
2017-08-01 09:28:17 +00:00
akwizgran
7c4dd991b9 Merge branch '1016-reblog-runtime-error' into 'master'
Runtime error fix due to window requests

Closes #1016 and #1007

See merge request !576
2017-08-01 09:25:39 +00:00
Ernir Erlingsson
8455569e88 moved window requests above onCreate 2017-07-30 22:42:03 +02:00
Torsten Grote
d25676559c Update translations, add Turkish and Russian 2017-07-29 11:03:51 -03:00
Michael Rogers
a9437f7985 Bumped version number for beta release. 2017-07-28 18:01:19 +01:00
akwizgran
8141a97fc9 Merge branch '1015-recent-emoji-crash' into 'master'
Prevent a crash caused by empty emoji

Closes #1015

See merge request !571
2017-07-28 16:59:02 +00:00
Torsten Grote
db842bd7e4 Prevent a crash caused by empty emoji
The crash happens because the serialization of recently used emoji uses
';' to separate the emojis.
One of the ASCII emojis however has a ';' in the beginning.
When this one is used by the user,
it causes an empty string to be returned when deserializing.

This commit prevents the crash by changing the separator to a tab.
It uses a different settings string to store the emoji,
so users will lose the list of recently used emoji when they update to
this version.

PS. That wasn't my idea ;)
2017-07-28 13:49:51 -03:00
Torsten Grote
6dbec3a864 Merge branch 'enable-logging-for-beta-builds' into 'master'
Enable logging for beta builds

See merge request !573
2017-07-28 15:58:01 +00:00
akwizgran
29f658cf4d Merge branch '1006-blog-crash' into 'master'
Prevent crash in blog by ensuring a listener always exists

Closes #1006

See merge request !574
2017-07-28 15:53:43 +00:00
akwizgran
ca83744a84 Merge branch 'close-feed-stream' into 'master'
Close InputStream from RSS feed and prevent NPE

See merge request !572
2017-07-28 15:48:01 +00:00
Torsten Grote
d91a9e2be4 Prevent crash in blog by ensuring a listener always exists 2017-07-28 12:42:56 -03:00
akwizgran
8408c3f467 Enable logging for beta builds.
Some devices were logging and others not, due to the log level being set in the SplashScreenActivity constructor.
2017-07-28 16:41:24 +01:00
Torsten Grote
544c83a64c Close InputStream from RSS feed and prevent NPE 2017-07-28 10:38:01 -03:00
111 changed files with 2898 additions and 3378 deletions

View File

@@ -12,8 +12,8 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 22
versionCode 14
versionName "0.14"
versionCode 1610
versionName "0.16.10"
consumerProguardFiles 'proguard-rules.txt'
}
@@ -32,31 +32,31 @@ dependencies {
def torBinaryDir = 'src/main/res/raw'
task downloadTorGeoIp(type: Download) {
src 'https://briarproject.org/build/geoip-2017-05-02.zip'
src 'https://briarproject.org/build/geoip-2017-09-06.zip'
dest "$torBinaryDir/geoip.zip"
onlyIfNewer true
}
task downloadTorBinaryArm(type: Download) {
src 'https://briarproject.org/build/tor-0.2.9.11-arm.zip'
src 'https://briarproject.org/build/tor-0.2.9.12-arm.zip'
dest "$torBinaryDir/tor_arm.zip"
onlyIfNewer true
}
task downloadTorBinaryArmPie(type: Download) {
src 'https://briarproject.org/build/tor-0.2.9.11-arm-pie.zip'
src 'https://briarproject.org/build/tor-0.2.9.12-arm-pie.zip'
dest "$torBinaryDir/tor_arm_pie.zip"
onlyIfNewer true
}
task downloadTorBinaryX86(type: Download) {
src 'https://briarproject.org/build/tor-0.2.9.11-x86.zip'
src 'https://briarproject.org/build/tor-0.2.9.12-x86.zip'
dest "$torBinaryDir/tor_x86.zip"
onlyIfNewer true
}
task downloadTorBinaryX86Pie(type: Download) {
src 'https://briarproject.org/build/tor-0.2.9.11-x86-pie.zip'
src 'https://briarproject.org/build/tor-0.2.9.12-x86-pie.zip'
dest "$torBinaryDir/tor_x86_pie.zip"
onlyIfNewer true
}
@@ -64,31 +64,31 @@ task downloadTorBinaryX86Pie(type: Download) {
task verifyTorGeoIp(type: Verify, dependsOn: 'downloadTorGeoIp') {
src "$torBinaryDir/geoip.zip"
algorithm 'SHA-256'
checksum '51f4d1272fb867e1f3b36b67a584e2a33c40b40f62305457d799fd399cd77c9b'
checksum 'fe49d3adb86d3c512373101422a017dbb86c85a570524663f09dd8ce143a24f3'
}
task verifyTorBinaryArm(type: Verify, dependsOn: 'downloadTorBinaryArm') {
src "$torBinaryDir/tor_arm.zip"
algorithm 'SHA-256'
checksum '1da6008663a8ad98b349e62acbbf42c379f65ec504fa467cb119c187cd5a4c6b'
checksum '8ed0b347ffed1d6a4d2fd14495118eb92be83e9cc06e057e15220dc288b31688'
}
task verifyTorBinaryArmPie(type: Verify, dependsOn: 'downloadTorBinaryArmPie') {
src "$torBinaryDir/tor_arm_pie.zip"
algorithm 'SHA-256'
checksum 'eb061f880829e05f104690ac744848133f2dacef04759d425a2cff0df32c271e'
checksum '64403262511c29f462ca5e7c7621bfc3c944898364d1d5ad35a016bb8a034283'
}
task verifyTorBinaryX86(type: Verify, dependsOn: 'downloadTorBinaryX86') {
src "$torBinaryDir/tor_x86.zip"
algorithm 'SHA-256'
checksum 'f5308aff8303daca082f82227d02b51ddedba4ab1d1420739ada0427ae5dbb41'
checksum '61e014607a2079bcf1646289c67bff6372b1aded6e1d8d83d7791efda9a4d5ab'
}
task verifyTorBinaryX86Pie(type: Verify, dependsOn: 'downloadTorBinaryX86Pie') {
src "$torBinaryDir/tor_x86_pie.zip"
algorithm 'SHA-256'
checksum '889a6c81ac73d05d35ed610ca5a913cee44d333e4ae1749c2a107f2f7dd8197b'
checksum '18fbc98356697dd0895836ab46d5c9877d1c539193464f7db1e82a65adaaf288'
}
project.afterEvaluate {

View File

@@ -11,7 +11,6 @@ import android.content.IntentFilter;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
@@ -30,23 +29,14 @@ import org.briarproject.bramble.util.StringUtils;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
@@ -61,8 +51,6 @@ import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERA
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
@@ -79,10 +67,6 @@ class DroidtoothPlugin implements DuplexPlugin {
private static final Logger LOG =
Logger.getLogger(DroidtoothPlugin.class.getName());
private static final String FOUND =
"android.bluetooth.device.action.FOUND";
private static final String DISCOVERY_FINISHED =
"android.bluetooth.adapter.action.DISCOVERY_FINISHED";
private final Executor ioExecutor;
private final AndroidExecutor androidExecutor;
@@ -374,90 +358,6 @@ class DroidtoothPlugin implements DuplexPlugin {
return new DroidtoothTransportConnection(this, s);
}
@Override
public boolean supportsInvitations() {
return true;
}
@Override
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice) {
if (!isRunning()) return null;
// Use the invitation codes to generate the UUID
byte[] b = r.nextBytes(UUID_BYTES);
UUID uuid = UUID.nameUUIDFromBytes(b);
if (LOG.isLoggable(INFO)) LOG.info("Invitation UUID " + uuid);
// Bind a server socket for receiving invitation connections
BluetoothServerSocket ss;
try {
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
"RFCOMM", uuid);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
// Create the background tasks
CompletionService<BluetoothSocket> complete =
new ExecutorCompletionService<>(ioExecutor);
List<Future<BluetoothSocket>> futures = new ArrayList<>();
if (alice) {
// Return the first connected socket
futures.add(complete.submit(new ListeningTask(ss)));
futures.add(complete.submit(new DiscoveryTask(uuid.toString())));
} else {
// Return the first socket with readable data
futures.add(complete.submit(new ReadableTask(
new ListeningTask(ss))));
futures.add(complete.submit(new ReadableTask(
new DiscoveryTask(uuid.toString()))));
}
BluetoothSocket chosen = null;
try {
Future<BluetoothSocket> f = complete.poll(timeout, MILLISECONDS);
if (f == null) return null; // No task completed within the timeout
chosen = f.get();
return new DroidtoothTransportConnection(this, chosen);
} catch (InterruptedException e) {
LOG.info("Interrupted while exchanging invitations");
Thread.currentThread().interrupt();
return null;
} catch (ExecutionException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
} finally {
// Closing the socket will terminate the listener task
tryToClose(ss);
closeSockets(futures, chosen);
}
}
private void closeSockets(final List<Future<BluetoothSocket>> futures,
@Nullable final BluetoothSocket chosen) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
for (Future<BluetoothSocket> f : futures) {
try {
if (f.cancel(true)) {
LOG.info("Cancelled task");
} else {
BluetoothSocket s = f.get();
if (s != null && s != chosen) {
LOG.info("Closing unwanted socket");
s.close();
}
}
} catch (InterruptedException e) {
LOG.info("Interrupted while closing sockets");
return;
} catch (ExecutionException | IOException e) {
if (LOG.isLoggable(INFO)) LOG.info(e.toString());
}
}
}
});
}
@Override
public boolean supportsKeyAgreement() {
return true;
@@ -472,7 +372,7 @@ class DroidtoothPlugin implements DuplexPlugin {
// No truncation necessary because COMMIT_LENGTH = 16
UUID uuid = UUID.nameUUIDFromBytes(commitment);
if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid);
// Bind a server socket for receiving invitation connections
// Bind a server socket for receiving key agreement connections
BluetoothServerSocket ss;
try {
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
@@ -536,115 +436,6 @@ class DroidtoothPlugin implements DuplexPlugin {
}
}
private class DiscoveryTask implements Callable<BluetoothSocket> {
private final String uuid;
private DiscoveryTask(String uuid) {
this.uuid = uuid;
}
@Override
public BluetoothSocket call() throws Exception {
// Repeat discovery until we connect or get interrupted
while (true) {
// Discover nearby devices
LOG.info("Discovering nearby devices");
List<String> addresses = discoverDevices();
if (addresses.isEmpty()) {
LOG.info("No devices discovered");
continue;
}
// Connect to any device with the right UUID
for (String address : addresses) {
BluetoothSocket s = connect(address, uuid);
if (s != null) {
LOG.info("Outgoing connection");
return s;
}
}
}
}
private List<String> discoverDevices() throws InterruptedException {
IntentFilter filter = new IntentFilter();
filter.addAction(FOUND);
filter.addAction(DISCOVERY_FINISHED);
DiscoveryReceiver disco = new DiscoveryReceiver();
appContext.registerReceiver(disco, filter);
LOG.info("Starting discovery");
adapter.startDiscovery();
return disco.waitForAddresses();
}
}
private static class DiscoveryReceiver extends BroadcastReceiver {
private final CountDownLatch finished = new CountDownLatch(1);
private final List<String> addresses = new CopyOnWriteArrayList<>();
@Override
public void onReceive(Context ctx, Intent intent) {
String action = intent.getAction();
if (action.equals(DISCOVERY_FINISHED)) {
LOG.info("Discovery finished");
ctx.unregisterReceiver(this);
finished.countDown();
} else if (action.equals(FOUND)) {
BluetoothDevice d = intent.getParcelableExtra(EXTRA_DEVICE);
if (LOG.isLoggable(INFO)) {
LOG.info("Discovered device: " +
scrubMacAddress(d.getAddress()));
}
addresses.add(d.getAddress());
}
}
private List<String> waitForAddresses() throws InterruptedException {
finished.await();
List<String> shuffled = new ArrayList<>(addresses);
Collections.shuffle(shuffled);
return shuffled;
}
}
private static class ListeningTask implements Callable<BluetoothSocket> {
private final BluetoothServerSocket serverSocket;
private ListeningTask(BluetoothServerSocket serverSocket) {
this.serverSocket = serverSocket;
}
@Override
public BluetoothSocket call() throws IOException {
BluetoothSocket s = serverSocket.accept();
LOG.info("Incoming connection");
return s;
}
}
private static class ReadableTask implements Callable<BluetoothSocket> {
private final Callable<BluetoothSocket> connectionTask;
private ReadableTask(Callable<BluetoothSocket> connectionTask) {
this.connectionTask = connectionTask;
}
@Override
public BluetoothSocket call() throws Exception {
BluetoothSocket s = connectionTask.call();
InputStream in = s.getInputStream();
while (in.available() == 0) {
LOG.info("Waiting for data");
Thread.sleep(1000);
}
LOG.info("Data available");
return s;
}
}
private class BluetoothKeyAgreementListener extends KeyAgreementListener {
private final BluetoothServerSocket ss;

View File

@@ -17,7 +17,6 @@ import net.freehaven.tor.control.EventHandler;
import net.freehaven.tor.control.TorControlConnection;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
@@ -589,17 +588,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
}
@Override
public boolean supportsInvitations() {
return false;
}
@Override
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice) {
throw new UnsupportedOperationException();
}
@Override
public boolean supportsKeyAgreement() {
return false;

View File

@@ -3,6 +3,7 @@ package org.briarproject.bramble.plugin.tor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
import org.briarproject.bramble.util.IoUtils;
import java.io.IOException;
import java.io.InputStream;
@@ -21,12 +22,12 @@ class TorTransportConnection extends AbstractDuplexTransportConnection {
@Override
protected InputStream getInputStream() throws IOException {
return socket.getInputStream();
return IoUtils.getInputStream(socket);
}
@Override
protected OutputStream getOutputStream() throws IOException {
return socket.getOutputStream();
return IoUtils.getOutputStream(socket);
}
@Override

View File

@@ -10,8 +10,6 @@ public interface CryptoComponent {
SecretKey generateSecretKey();
PseudoRandom getPseudoRandom(int seed1, int seed2);
SecureRandom getSecureRandom();
KeyPair generateAgreementKeyPair();
@@ -24,15 +22,6 @@ public interface CryptoComponent {
KeyParser getMessageKeyParser();
/** Generates a random invitation code. */
int generateBTInvitationCode();
/**
* Derives a confirmation code from the given master secret.
* @param alice whether the code is for use by Alice or Bob.
*/
int deriveBTConfirmationCode(SecretKey master, boolean alice);
/**
* Derives a stream header key from the given master secret.
* @param alice whether the key is for use by Alice or Bob.

View File

@@ -1,12 +0,0 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* A deterministic pseudo-random number generator.
*/
@NotNullByDefault
public interface PseudoRandom {
byte[] nextBytes(int bytes);
}

View File

@@ -14,8 +14,9 @@ public interface StreamDecrypterFactory {
StreamDecrypter createStreamDecrypter(InputStream in, StreamContext ctx);
/**
* Creates a {@link StreamDecrypter} for decrypting an invitation stream.
* Creates a {@link StreamDecrypter} for decrypting a contact exchange
* stream.
*/
StreamDecrypter createInvitationStreamDecrypter(InputStream in,
StreamDecrypter createContactExchangeStreamDecrypter(InputStream in,
SecretKey headerKey);
}

View File

@@ -14,8 +14,9 @@ public interface StreamEncrypterFactory {
StreamEncrypter createStreamEncrypter(OutputStream out, StreamContext ctx);
/**
* Creates a {@link StreamEncrypter} for encrypting an invitation stream.
* Creates a {@link StreamEncrypter} for encrypting a contact exchange
* stream.
*/
StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
StreamEncrypter createContactExchangeStreamDecrypter(OutputStream out,
SecretKey headerKey);
}

View File

@@ -1,20 +0,0 @@
package org.briarproject.bramble.api.invitation;
public interface InvitationConstants {
/**
* The connection timeout in milliseconds.
*/
long CONNECTION_TIMEOUT = 60 * 1000;
/**
* The confirmation timeout in milliseconds.
*/
long CONFIRMATION_TIMEOUT = 60 * 1000;
/**
* The number of bits in an invitation or confirmation code. Codes must fit
* into six decimal digits.
*/
int CODE_BITS = 19;
}

View File

@@ -1,47 +0,0 @@
package org.briarproject.bramble.api.invitation;
/**
* An interface for receiving updates about the state of an
* {@link InvitationTask}.
*/
public interface InvitationListener {
/** Called if a connection to the remote peer is established. */
void connectionSucceeded();
/**
* Called if a connection to the remote peer cannot be established. This
* indicates that the protocol has ended unsuccessfully.
*/
void connectionFailed();
/** Called if key agreement with the remote peer succeeds. */
void keyAgreementSucceeded(int localCode, int remoteCode);
/**
* Called if key agreement with the remote peer fails or the connection is
* lost. This indicates that the protocol has ended unsuccessfully.
*/
void keyAgreementFailed();
/** Called if the remote peer's confirmation check succeeds. */
void remoteConfirmationSucceeded();
/**
* Called if remote peer's confirmation check fails or the connection is
* lost. This indicates that the protocol has ended unsuccessfully.
*/
void remoteConfirmationFailed();
/**
* Called if the exchange of pseudonyms succeeds. This indicates that the
* protocol has ended successfully.
*/
void pseudonymExchangeSucceeded(String remoteName);
/**
* Called if the exchange of pseudonyms fails or the connection is lost.
* This indicates that the protocol has ended unsuccessfully.
*/
void pseudonymExchangeFailed();
}

View File

@@ -1,85 +0,0 @@
package org.briarproject.bramble.api.invitation;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* A snapshot of the state of an {@link InvitationTask}.
*/
@Immutable
@NotNullByDefault
public class InvitationState {
private final int localInvitationCode, remoteInvitationCode;
private final int localConfirmationCode, remoteConfirmationCode;
private final boolean connected, connectionFailed;
private final boolean localCompared, remoteCompared;
private final boolean localMatched, remoteMatched;
@Nullable
private final String contactName;
public InvitationState(int localInvitationCode, int remoteInvitationCode,
int localConfirmationCode, int remoteConfirmationCode,
boolean connected, boolean connectionFailed, boolean localCompared,
boolean remoteCompared, boolean localMatched,
boolean remoteMatched, @Nullable String contactName) {
this.localInvitationCode = localInvitationCode;
this.remoteInvitationCode = remoteInvitationCode;
this.localConfirmationCode = localConfirmationCode;
this.remoteConfirmationCode = remoteConfirmationCode;
this.connected = connected;
this.connectionFailed = connectionFailed;
this.localCompared = localCompared;
this.remoteCompared = remoteCompared;
this.localMatched = localMatched;
this.remoteMatched = remoteMatched;
this.contactName = contactName;
}
public int getLocalInvitationCode() {
return localInvitationCode;
}
public int getRemoteInvitationCode() {
return remoteInvitationCode;
}
public int getLocalConfirmationCode() {
return localConfirmationCode;
}
public int getRemoteConfirmationCode() {
return remoteConfirmationCode;
}
public boolean getConnected() {
return connected;
}
public boolean getConnectionFailed() {
return connectionFailed;
}
public boolean getLocalCompared() {
return localCompared;
}
public boolean getRemoteCompared() {
return remoteCompared;
}
public boolean getLocalMatched() {
return localMatched;
}
public boolean getRemoteMatched() {
return remoteMatched;
}
@Nullable
public String getContactName() {
return contactName;
}
}

View File

@@ -1,38 +0,0 @@
package org.briarproject.bramble.api.invitation;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* A task for exchanging invitations with a remote peer.
*/
@NotNullByDefault
public interface InvitationTask {
/**
* Adds a listener to be informed of state changes and returns the
* task's current state.
*/
InvitationState addListener(InvitationListener l);
/**
* Removes the given listener.
*/
void removeListener(InvitationListener l);
/**
* Asynchronously starts the connection process.
*/
void connect();
/**
* Asynchronously informs the remote peer that the local peer's
* confirmation codes matched.
*/
void localConfirmationSucceeded();
/**
* Asynchronously informs the remote peer that the local peer's
* confirmation codes did not match.
*/
void localConfirmationFailed();
}

View File

@@ -1,15 +0,0 @@
package org.briarproject.bramble.api.invitation;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* Creates tasks for exchanging invitations with remote peers.
*/
@NotNullByDefault
public interface InvitationTaskFactory {
/**
* Creates a task using the given local and remote invitation codes.
*/
InvitationTask createTask(int localCode, int remoteCode);
}

View File

@@ -32,11 +32,6 @@ public interface PluginManager {
*/
Collection<DuplexPlugin> getDuplexPlugins();
/**
* Returns any duplex plugins that support invitations.
*/
Collection<DuplexPlugin> getInvitationPlugins();
/**
* Returns any duplex plugins that support key agreement.
*/

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.api.plugin.duplex;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -23,20 +22,6 @@ public interface DuplexPlugin extends Plugin {
@Nullable
DuplexTransportConnection createConnection(ContactId c);
/**
* Returns true if the plugin supports exchanging invitations.
*/
boolean supportsInvitations();
/**
* Attempts to create and return an invitation connection to the remote
* peer. Returns null if no connection can be established within the given
* time.
*/
@Nullable
DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice);
/**
* Returns true if the plugin supports short-range key agreement.
*/

View File

@@ -15,9 +15,9 @@ public interface StreamReaderFactory {
InputStream createStreamReader(InputStream in, StreamContext ctx);
/**
* Creates an {@link InputStream InputStream} for reading from an
* invitation stream.
* Creates an {@link InputStream InputStream} for reading from a contact
* exchangestream.
*/
InputStream createInvitationStreamReader(InputStream in,
InputStream createContactExchangeStreamReader(InputStream in,
SecretKey headerKey);
}

View File

@@ -15,9 +15,9 @@ public interface StreamWriterFactory {
OutputStream createStreamWriter(OutputStream out, StreamContext ctx);
/**
* Creates an {@link OutputStream OutputStream} for writing to an
* invitation stream.
* Creates an {@link OutputStream OutputStream} for writing to a contact
* exchange stream.
*/
OutputStream createInvitationStreamWriter(OutputStream out,
OutputStream createContactExchangeStreamWriter(OutputStream out,
SecretKey headerKey);
}

View File

@@ -8,6 +8,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import javax.annotation.Nullable;
@@ -59,4 +60,24 @@ public class IoUtils {
offset += read;
}
}
// Workaround for a bug in Android 7, see
// https://android-review.googlesource.com/#/c/271775/
public static InputStream getInputStream(Socket s) throws IOException {
try {
return s.getInputStream();
} catch (NullPointerException e) {
throw new IOException(e);
}
}
// Workaround for a bug in Android 7, see
// https://android-review.googlesource.com/#/c/271775/
public static OutputStream getOutputStream(Socket s) throws IOException {
try {
return s.getOutputStream();
} catch (NullPointerException e) {
throw new IOException(e);
}
}
}

View File

@@ -8,7 +8,6 @@ import org.briarproject.bramble.db.DatabaseExecutorModule;
import org.briarproject.bramble.db.DatabaseModule;
import org.briarproject.bramble.event.EventModule;
import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.invitation.InvitationModule;
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.plugin.PluginModule;
@@ -32,7 +31,6 @@ import dagger.Module;
DatabaseExecutorModule.class,
EventModule.class,
IdentityModule.class,
InvitationModule.class,
KeyAgreementModule.class,
LifecycleModule.class,
PluginModule.class,

View File

@@ -80,7 +80,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
private volatile boolean alice;
@Inject
public ContactExchangeTaskImpl(DatabaseComponent db,
ContactExchangeTaskImpl(DatabaseComponent db,
AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory, Clock clock,
ConnectionManager connectionManager, ContactManager contactManager,
@@ -146,12 +146,12 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
// Create the readers
InputStream streamReader =
streamReaderFactory.createInvitationStreamReader(in,
streamReaderFactory.createContactExchangeStreamReader(in,
alice ? bobHeaderKey : aliceHeaderKey);
BdfReader r = bdfReaderFactory.createReader(streamReader);
// Create the writers
OutputStream streamWriter =
streamWriterFactory.createInvitationStreamWriter(out,
streamWriterFactory.createContactExchangeStreamWriter(out,
alice ? aliceHeaderKey : bobHeaderKey);
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.plugin.TransportId;
@@ -41,7 +40,6 @@ import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.api.invitation.InvitationConstants.CODE_BITS;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
@@ -68,9 +66,6 @@ class CryptoComponentImpl implements CryptoComponent {
return s.getBytes(Charset.forName("US-ASCII"));
}
// KDF labels for bluetooth confirmation code derivation
private static final byte[] BT_A_CONFIRM = ascii("ALICE_CONFIRMATION_CODE");
private static final byte[] BT_B_CONFIRM = ascii("BOB_CONFIRMATION_CODE");
// KDF labels for contact exchange stream header key derivation
private static final byte[] A_INVITE = ascii("ALICE_INVITATION_KEY");
private static final byte[] B_INVITE = ascii("BOB_INVITATION_KEY");
@@ -171,14 +166,6 @@ class CryptoComponentImpl implements CryptoComponent {
return new SecretKey(b);
}
@Override
public PseudoRandom getPseudoRandom(int seed1, int seed2) {
byte[] seed = new byte[INT_32_BYTES * 2];
ByteUtils.writeUint32(seed1, seed, 0);
ByteUtils.writeUint32(seed2, seed, INT_32_BYTES);
return new PseudoRandomImpl(seed);
}
@Override
public SecureRandom getSecureRandom() {
return secureRandom;
@@ -250,20 +237,6 @@ class CryptoComponentImpl implements CryptoComponent {
return messageEncrypter.getKeyParser();
}
@Override
public int generateBTInvitationCode() {
int codeBytes = (CODE_BITS + 7) / 8;
byte[] random = new byte[codeBytes];
secureRandom.nextBytes(random);
return ByteUtils.readUint(random, CODE_BITS);
}
@Override
public int deriveBTConfirmationCode(SecretKey master, boolean alice) {
byte[] b = macKdf(master, alice ? BT_A_CONFIRM : BT_B_CONFIRM);
return ByteUtils.readUint(b, CODE_BITS);
}
@Override
public SecretKey deriveHeaderKey(SecretKey master,
boolean alice) {

View File

@@ -32,7 +32,7 @@ class StreamDecrypterFactoryImpl implements StreamDecrypterFactory {
}
@Override
public StreamDecrypter createInvitationStreamDecrypter(InputStream in,
public StreamDecrypter createContactExchangeStreamDecrypter(InputStream in,
SecretKey headerKey) {
return new StreamDecrypterImpl(in, cipherProvider.get(), 0, headerKey);
}

View File

@@ -46,8 +46,8 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
}
@Override
public StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
SecretKey headerKey) {
public StreamEncrypter createContactExchangeStreamDecrypter(
OutputStream out, SecretKey headerKey) {
AuthenticatedCipher cipher = cipherProvider.get();
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderNonce);

View File

@@ -1,119 +0,0 @@
package org.briarproject.bramble.invitation;
import org.briarproject.bramble.api.contact.ContactExchangeTask;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriter;
import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
/**
* A connection thread for the peer being Alice in the invitation protocol.
*/
@NotNullByDefault
class AliceConnector extends Connector {
private static final Logger LOG =
Logger.getLogger(AliceConnector.class.getName());
AliceConnector(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory,
ContactExchangeTask contactExchangeTask, ConnectorGroup group,
DuplexPlugin plugin, LocalAuthor localAuthor, PseudoRandom random) {
super(crypto, bdfReaderFactory, bdfWriterFactory, contactExchangeTask,
group, plugin, localAuthor, random);
}
@Override
public void run() {
// Create an incoming or outgoing connection
DuplexTransportConnection conn = createInvitationConnection(true);
if (conn == null) return;
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " connected");
// Don't proceed with more than one connection
if (group.getAndSetConnected()) {
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " redundant");
tryToClose(conn, false);
return;
}
// Carry out the key agreement protocol
InputStream in;
OutputStream out;
BdfReader r;
BdfWriter w;
SecretKey master;
try {
in = conn.getReader().getInputStream();
out = conn.getWriter().getOutputStream();
r = bdfReaderFactory.createReader(in);
w = bdfWriterFactory.createWriter(out);
// Alice goes first
sendPublicKeyHash(w);
byte[] hash = receivePublicKeyHash(r);
sendPublicKey(w);
byte[] key = receivePublicKey(r);
master = deriveMasterSecret(hash, key, true);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.keyAgreementFailed();
tryToClose(conn, true);
return;
} catch (GeneralSecurityException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.keyAgreementFailed();
tryToClose(conn, true);
return;
}
// The key agreement succeeded - derive the confirmation codes
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " agreement succeeded");
int aliceCode = crypto.deriveBTConfirmationCode(master, true);
int bobCode = crypto.deriveBTConfirmationCode(master, false);
group.keyAgreementSucceeded(aliceCode, bobCode);
// Exchange confirmation results
boolean localMatched, remoteMatched;
try {
localMatched = group.waitForLocalConfirmationResult();
sendConfirmation(w, localMatched);
remoteMatched = receiveConfirmation(r);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.remoteConfirmationFailed();
tryToClose(conn, true);
return;
} catch (InterruptedException e) {
LOG.warning("Interrupted while waiting for confirmation");
group.remoteConfirmationFailed();
tryToClose(conn, true);
Thread.currentThread().interrupt();
return;
}
if (remoteMatched) group.remoteConfirmationSucceeded();
else group.remoteConfirmationFailed();
if (!(localMatched && remoteMatched)) {
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " confirmation failed");
tryToClose(conn, false);
return;
}
// Confirmation succeeded - upgrade to a secure connection
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " confirmation succeeded");
contactExchangeTask.startExchange(group, localAuthor, master, conn,
plugin.getId(), true);
}
}

View File

@@ -1,119 +0,0 @@
package org.briarproject.bramble.invitation;
import org.briarproject.bramble.api.contact.ContactExchangeTask;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriter;
import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
/**
* A connection thread for the peer being Bob in the invitation protocol.
*/
@NotNullByDefault
class BobConnector extends Connector {
private static final Logger LOG =
Logger.getLogger(BobConnector.class.getName());
BobConnector(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory,
ContactExchangeTask contactExchangeTask, ConnectorGroup group,
DuplexPlugin plugin, LocalAuthor localAuthor, PseudoRandom random) {
super(crypto, bdfReaderFactory, bdfWriterFactory, contactExchangeTask,
group, plugin, localAuthor, random);
}
@Override
public void run() {
// Create an incoming or outgoing connection
DuplexTransportConnection conn = createInvitationConnection(false);
if (conn == null) return;
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " connected");
// Carry out the key agreement protocol
InputStream in;
OutputStream out;
BdfReader r;
BdfWriter w;
SecretKey master;
try {
in = conn.getReader().getInputStream();
out = conn.getWriter().getOutputStream();
r = bdfReaderFactory.createReader(in);
w = bdfWriterFactory.createWriter(out);
// Alice goes first
byte[] hash = receivePublicKeyHash(r);
// Don't proceed with more than one connection
if (group.getAndSetConnected()) {
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " redundant");
tryToClose(conn, false);
return;
}
sendPublicKeyHash(w);
byte[] key = receivePublicKey(r);
sendPublicKey(w);
master = deriveMasterSecret(hash, key, false);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.keyAgreementFailed();
tryToClose(conn, true);
return;
} catch (GeneralSecurityException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.keyAgreementFailed();
tryToClose(conn, true);
return;
}
// The key agreement succeeded - derive the confirmation codes
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " agreement succeeded");
int aliceCode = crypto.deriveBTConfirmationCode(master, true);
int bobCode = crypto.deriveBTConfirmationCode(master, false);
group.keyAgreementSucceeded(bobCode, aliceCode);
// Exchange confirmation results
boolean localMatched, remoteMatched;
try {
remoteMatched = receiveConfirmation(r);
localMatched = group.waitForLocalConfirmationResult();
sendConfirmation(w, localMatched);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.remoteConfirmationFailed();
tryToClose(conn, true);
return;
} catch (InterruptedException e) {
LOG.warning("Interrupted while waiting for confirmation");
group.remoteConfirmationFailed();
tryToClose(conn, true);
Thread.currentThread().interrupt();
return;
}
if (remoteMatched) group.remoteConfirmationSucceeded();
else group.remoteConfirmationFailed();
if (!(localMatched && remoteMatched)) {
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " confirmation failed");
tryToClose(conn, false);
return;
}
// Confirmation succeeded - upgrade to a secure connection
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " confirmation succeeded");
contactExchangeTask.startExchange(group, localAuthor, master, conn,
plugin.getId(), false);
}
}

View File

@@ -1,150 +0,0 @@
package org.briarproject.bramble.invitation;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactExchangeTask;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriter;
import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.api.invitation.InvitationConstants.CONNECTION_TIMEOUT;
// FIXME: This class has way too many dependencies
@NotNullByDefault
abstract class Connector extends Thread {
private static final Logger LOG =
Logger.getLogger(Connector.class.getName());
private static final String LABEL_PUBLIC_KEY =
"org.briarproject.bramble.invitation.PUBLIC_KEY";
protected final CryptoComponent crypto;
protected final BdfReaderFactory bdfReaderFactory;
protected final BdfWriterFactory bdfWriterFactory;
protected final ContactExchangeTask contactExchangeTask;
protected final ConnectorGroup group;
protected final DuplexPlugin plugin;
protected final LocalAuthor localAuthor;
protected final PseudoRandom random;
protected final String pluginName;
private final KeyPair keyPair;
private final KeyParser keyParser;
Connector(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory,
ContactExchangeTask contactExchangeTask, ConnectorGroup group,
DuplexPlugin plugin, LocalAuthor localAuthor, PseudoRandom random) {
super("Connector");
this.crypto = crypto;
this.bdfReaderFactory = bdfReaderFactory;
this.bdfWriterFactory = bdfWriterFactory;
this.contactExchangeTask = contactExchangeTask;
this.group = group;
this.plugin = plugin;
this.localAuthor = localAuthor;
this.random = random;
pluginName = plugin.getClass().getName();
keyPair = crypto.generateAgreementKeyPair();
keyParser = crypto.getAgreementKeyParser();
}
@Nullable
DuplexTransportConnection createInvitationConnection(boolean alice) {
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " creating invitation connection");
return plugin.createInvitationConnection(random, CONNECTION_TIMEOUT,
alice);
}
void sendPublicKeyHash(BdfWriter w) throws IOException {
byte[] hash =
crypto.hash(LABEL_PUBLIC_KEY, keyPair.getPublic().getEncoded());
w.writeRaw(hash);
w.flush();
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " sent hash");
}
byte[] receivePublicKeyHash(BdfReader r) throws IOException {
int hashLength = crypto.getHashLength();
byte[] b = r.readRaw(hashLength);
if (b.length < hashLength) throw new FormatException();
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " received hash");
return b;
}
void sendPublicKey(BdfWriter w) throws IOException {
byte[] key = keyPair.getPublic().getEncoded();
w.writeRaw(key);
w.flush();
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " sent key");
}
byte[] receivePublicKey(BdfReader r)
throws GeneralSecurityException, IOException {
byte[] b = r.readRaw(MAX_PUBLIC_KEY_LENGTH);
keyParser.parsePublicKey(b);
if (LOG.isLoggable(INFO)) LOG.info(pluginName + " received key");
return b;
}
SecretKey deriveMasterSecret(byte[] hash, byte[] key, boolean alice)
throws GeneralSecurityException {
// Check that the hash matches the key
byte[] keyHash =
crypto.hash(LABEL_PUBLIC_KEY, keyPair.getPublic().getEncoded());
if (!Arrays.equals(hash, keyHash)) {
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " hash does not match key");
throw new GeneralSecurityException();
}
// Derive the master secret
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " deriving master secret");
return crypto.deriveMasterSecret(key, keyPair, alice);
}
void sendConfirmation(BdfWriter w, boolean confirmed) throws IOException {
w.writeBoolean(confirmed);
w.flush();
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " sent confirmation: " + confirmed);
}
boolean receiveConfirmation(BdfReader r) throws IOException {
boolean confirmed = r.readBoolean();
if (LOG.isLoggable(INFO))
LOG.info(pluginName + " received confirmation: " + confirmed);
return confirmed;
}
protected void tryToClose(DuplexTransportConnection conn,
boolean exception) {
try {
LOG.info("Closing connection");
conn.getReader().dispose(exception, true);
conn.getWriter().dispose(exception);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
}

View File

@@ -1,278 +0,0 @@
package org.briarproject.bramble.invitation;
import org.briarproject.bramble.api.contact.ContactExchangeListener;
import org.briarproject.bramble.api.contact.ContactExchangeTask;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.invitation.InvitationListener;
import org.briarproject.bramble.api.invitation.InvitationState;
import org.briarproject.bramble.api.invitation.InvitationTask;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.invitation.InvitationConstants.CONFIRMATION_TIMEOUT;
/**
* A task consisting of one or more parallel connection attempts.
*/
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class ConnectorGroup extends Thread implements InvitationTask,
ContactExchangeListener {
private static final Logger LOG =
Logger.getLogger(ConnectorGroup.class.getName());
private final CryptoComponent crypto;
private final BdfReaderFactory bdfReaderFactory;
private final BdfWriterFactory bdfWriterFactory;
private final ContactExchangeTask contactExchangeTask;
private final IdentityManager identityManager;
private final PluginManager pluginManager;
private final int localInvitationCode, remoteInvitationCode;
private final Collection<InvitationListener> listeners;
private final AtomicBoolean connected;
private final CountDownLatch localConfirmationLatch;
private final Lock lock = new ReentrantLock();
// The following are locking: lock
private int localConfirmationCode = -1, remoteConfirmationCode = -1;
private boolean connectionFailed = false;
private boolean localCompared = false, remoteCompared = false;
private boolean localMatched = false, remoteMatched = false;
private String remoteName = null;
ConnectorGroup(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory,
ContactExchangeTask contactExchangeTask,
IdentityManager identityManager, PluginManager pluginManager,
int localInvitationCode, int remoteInvitationCode) {
super("ConnectorGroup");
this.crypto = crypto;
this.bdfReaderFactory = bdfReaderFactory;
this.bdfWriterFactory = bdfWriterFactory;
this.contactExchangeTask = contactExchangeTask;
this.identityManager = identityManager;
this.pluginManager = pluginManager;
this.localInvitationCode = localInvitationCode;
this.remoteInvitationCode = remoteInvitationCode;
listeners = new CopyOnWriteArrayList<InvitationListener>();
connected = new AtomicBoolean(false);
localConfirmationLatch = new CountDownLatch(1);
}
@Override
public InvitationState addListener(InvitationListener l) {
lock.lock();
try {
listeners.add(l);
return new InvitationState(localInvitationCode,
remoteInvitationCode, localConfirmationCode,
remoteConfirmationCode, connected.get(), connectionFailed,
localCompared, remoteCompared, localMatched, remoteMatched,
remoteName);
} finally {
lock.unlock();
}
}
@Override
public void removeListener(InvitationListener l) {
listeners.remove(l);
}
@Override
public void connect() {
start();
}
@Override
public void run() {
LocalAuthor localAuthor;
// Load the local pseudonym
try {
localAuthor = identityManager.getLocalAuthor();
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
lock.lock();
try {
connectionFailed = true;
} finally {
lock.unlock();
}
for (InvitationListener l : listeners) l.connectionFailed();
return;
}
// Start the connection threads
Collection<Connector> connectors = new ArrayList<Connector>();
// Alice is the party with the smaller invitation code
if (localInvitationCode < remoteInvitationCode) {
for (DuplexPlugin plugin : pluginManager.getInvitationPlugins()) {
Connector c = createAliceConnector(plugin, localAuthor);
connectors.add(c);
c.start();
}
} else {
for (DuplexPlugin plugin : pluginManager.getInvitationPlugins()) {
Connector c = createBobConnector(plugin, localAuthor);
connectors.add(c);
c.start();
}
}
// Wait for the connection threads to finish
try {
for (Connector c : connectors) c.join();
} catch (InterruptedException e) {
LOG.warning("Interrupted while waiting for connectors");
Thread.currentThread().interrupt();
}
// If none of the threads connected, inform the listeners
if (!connected.get()) {
lock.lock();
try {
connectionFailed = true;
} finally {
lock.unlock();
}
for (InvitationListener l : listeners) l.connectionFailed();
}
}
private Connector createAliceConnector(DuplexPlugin plugin,
LocalAuthor localAuthor) {
PseudoRandom random = crypto.getPseudoRandom(localInvitationCode,
remoteInvitationCode);
return new AliceConnector(crypto, bdfReaderFactory, bdfWriterFactory,
contactExchangeTask, this, plugin, localAuthor, random);
}
private Connector createBobConnector(DuplexPlugin plugin,
LocalAuthor localAuthor) {
PseudoRandom random = crypto.getPseudoRandom(remoteInvitationCode,
localInvitationCode);
return new BobConnector(crypto, bdfReaderFactory, bdfWriterFactory,
contactExchangeTask, this, plugin, localAuthor, random);
}
@Override
public void localConfirmationSucceeded() {
lock.lock();
try {
localCompared = true;
localMatched = true;
} finally {
lock.unlock();
}
localConfirmationLatch.countDown();
}
@Override
public void localConfirmationFailed() {
lock.lock();
try {
localCompared = true;
localMatched = false;
} finally {
lock.unlock();
}
localConfirmationLatch.countDown();
}
boolean getAndSetConnected() {
boolean redundant = connected.getAndSet(true);
if (!redundant)
for (InvitationListener l : listeners) l.connectionSucceeded();
return redundant;
}
void keyAgreementSucceeded(int localCode, int remoteCode) {
lock.lock();
try {
localConfirmationCode = localCode;
remoteConfirmationCode = remoteCode;
} finally {
lock.unlock();
}
for (InvitationListener l : listeners)
l.keyAgreementSucceeded(localCode, remoteCode);
}
void keyAgreementFailed() {
for (InvitationListener l : listeners) l.keyAgreementFailed();
}
boolean waitForLocalConfirmationResult() throws InterruptedException {
localConfirmationLatch.await(CONFIRMATION_TIMEOUT, MILLISECONDS);
lock.lock();
try {
return localMatched;
} finally {
lock.unlock();
}
}
void remoteConfirmationSucceeded() {
lock.lock();
try {
remoteCompared = true;
remoteMatched = true;
} finally {
lock.unlock();
}
for (InvitationListener l : listeners) l.remoteConfirmationSucceeded();
}
void remoteConfirmationFailed() {
lock.lock();
try {
remoteCompared = true;
remoteMatched = false;
} finally {
lock.unlock();
}
for (InvitationListener l : listeners) l.remoteConfirmationFailed();
}
@Override
public void contactExchangeSucceeded(Author remoteAuthor) {
String name = remoteAuthor.getName();
lock.lock();
try {
remoteName = name;
} finally {
lock.unlock();
}
for (InvitationListener l : listeners)
l.pseudonymExchangeSucceeded(name);
}
@Override
public void duplicateContact(Author remoteAuthor) {
// TODO differentiate
for (InvitationListener l : listeners) l.pseudonymExchangeFailed();
}
@Override
public void contactExchangeFailed() {
for (InvitationListener l : listeners) l.pseudonymExchangeFailed();
}
}

View File

@@ -1,16 +0,0 @@
package org.briarproject.bramble.invitation;
import org.briarproject.bramble.api.invitation.InvitationTaskFactory;
import dagger.Module;
import dagger.Provides;
@Module
public class InvitationModule {
@Provides
InvitationTaskFactory provideInvitationTaskFactory(
InvitationTaskFactoryImpl invitationTaskFactory) {
return invitationTaskFactory;
}
}

View File

@@ -1,47 +0,0 @@
package org.briarproject.bramble.invitation;
import org.briarproject.bramble.api.contact.ContactExchangeTask;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.invitation.InvitationTask;
import org.briarproject.bramble.api.invitation.InvitationTaskFactory;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginManager;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
@Immutable
@NotNullByDefault
class InvitationTaskFactoryImpl implements InvitationTaskFactory {
private final CryptoComponent crypto;
private final BdfReaderFactory bdfReaderFactory;
private final BdfWriterFactory bdfWriterFactory;
private final ContactExchangeTask contactExchangeTask;
private final IdentityManager identityManager;
private final PluginManager pluginManager;
@Inject
InvitationTaskFactoryImpl(CryptoComponent crypto,
BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory,
ContactExchangeTask contactExchangeTask,
IdentityManager identityManager, PluginManager pluginManager) {
this.crypto = crypto;
this.bdfReaderFactory = bdfReaderFactory;
this.bdfWriterFactory = bdfWriterFactory;
this.contactExchangeTask = contactExchangeTask;
this.identityManager = identityManager;
this.pluginManager = pluginManager;
}
@Override
public InvitationTask createTask(int localCode, int remoteCode) {
return new ConnectorGroup(crypto, bdfReaderFactory, bdfWriterFactory,
contactExchangeTask, identityManager, pluginManager,
localCode, remoteCode);
}
}

View File

@@ -164,14 +164,6 @@ class PluginManagerImpl implements PluginManager, Service {
return new ArrayList<DuplexPlugin>(duplexPlugins);
}
@Override
public Collection<DuplexPlugin> getInvitationPlugins() {
List<DuplexPlugin> supported = new ArrayList<DuplexPlugin>();
for (DuplexPlugin d : duplexPlugins)
if (d.supportsInvitations()) supported.add(d);
return supported;
}
@Override
public Collection<DuplexPlugin> getKeyAgreementPlugins() {
List<DuplexPlugin> supported = new ArrayList<DuplexPlugin>();

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.plugin.tcp;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
@@ -281,17 +280,6 @@ abstract class TcpPlugin implements DuplexPlugin {
}
}
@Override
public boolean supportsInvitations() {
return false;
}
@Override
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice) {
throw new UnsupportedOperationException();
}
@Override
public boolean supportsKeyAgreement() {
return false;

View File

@@ -3,6 +3,7 @@ package org.briarproject.bramble.plugin.tcp;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnection;
import org.briarproject.bramble.util.IoUtils;
import java.io.IOException;
import java.io.InputStream;
@@ -24,12 +25,12 @@ class TcpTransportConnection extends AbstractDuplexTransportConnection {
@Override
protected InputStream getInputStream() throws IOException {
return socket.getInputStream();
return IoUtils.getInputStream(socket);
}
@Override
protected OutputStream getOutputStream() throws IOException {
return socket.getOutputStream();
return IoUtils.getOutputStream(socket);
}
@Override

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.reporting;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.IoUtils;
import java.io.Closeable;
import java.io.File;
@@ -130,7 +131,7 @@ public class DevReportServer {
OutputStream out = null;
try {
socket.setSoTimeout(SOCKET_TIMEOUT_MS);
in = socket.getInputStream();
in = IoUtils.getInputStream(socket);
reportDir.mkdirs();
reportFile = File.createTempFile(FILE_PREFIX, FILE_SUFFIX,
reportDir);

View File

@@ -93,7 +93,7 @@ class DevReporterImpl implements DevReporter {
InputStream in = null;
try {
Socket s = connectToDevelopers();
out = s.getOutputStream();
out = IoUtils.getOutputStream(s);
in = new FileInputStream(f);
IoUtils.copyAndClose(in, out);
f.delete();

View File

@@ -57,8 +57,8 @@ class SocksSocket extends Socket {
// Connect to the proxy
super.connect(proxy, connectToProxyTimeout);
OutputStream out = getOutputStream();
InputStream in = getInputStream();
OutputStream out = IoUtils.getOutputStream(this);
InputStream in = IoUtils.getInputStream(this);
// Request SOCKS 5 with no authentication
sendMethodRequest(out);

View File

@@ -29,10 +29,10 @@ class StreamReaderFactoryImpl implements StreamReaderFactory {
}
@Override
public InputStream createInvitationStreamReader(InputStream in,
public InputStream createContactExchangeStreamReader(InputStream in,
SecretKey headerKey) {
return new StreamReaderImpl(
streamDecrypterFactory.createInvitationStreamDecrypter(in,
streamDecrypterFactory.createContactExchangeStreamDecrypter(in,
headerKey));
}
}

View File

@@ -30,10 +30,10 @@ class StreamWriterFactoryImpl implements StreamWriterFactory {
}
@Override
public OutputStream createInvitationStreamWriter(OutputStream out,
public OutputStream createContactExchangeStreamWriter(OutputStream out,
SecretKey headerKey) {
return new StreamWriterImpl(
streamEncrypterFactory.createInvitationStreamEncrypter(out,
streamEncrypterFactory.createContactExchangeStreamDecrypter(out,
headerKey));
}
}

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.spongycastle.crypto.Digest;
import org.spongycastle.crypto.engines.Salsa20Engine;
@@ -11,11 +10,11 @@ import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
@NotNullByDefault
class PseudoRandomImpl implements PseudoRandom {
class PseudoRandom {
private final Salsa20Engine cipher = new Salsa20Engine();
PseudoRandomImpl(byte[] seed) {
PseudoRandom(byte[] seed) {
// Hash the seed to produce a 32-byte key
byte[] key = new byte[32];
Digest digest = new Blake2sDigest();
@@ -26,8 +25,7 @@ class PseudoRandomImpl implements PseudoRandom {
cipher.init(true, new ParametersWithIV(new KeyParameter(key), nonce));
}
@Override
public byte[] nextBytes(int length) {
byte[] nextBytes(int length) {
byte[] in = new byte[length], out = new byte[length];
cipher.processBytes(in, 0, length, out, 0);
return out;

View File

@@ -1,7 +1,5 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.SecureRandomSpi;
@@ -19,7 +17,7 @@ class PseudoSecureRandom extends SecureRandom {
private final PseudoRandom pseudoRandom;
private PseudoSecureRandomSpi(byte[] seed) {
pseudoRandom = new PseudoRandomImpl(seed);
pseudoRandom = new PseudoRandom(seed);
}
@Override

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
@@ -19,33 +18,23 @@ import org.briarproject.bramble.util.OsUtils;
import org.briarproject.bramble.util.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static javax.bluetooth.DiscoveryAgent.GIAC;
@@ -67,7 +56,6 @@ class BluetoothPlugin implements DuplexPlugin {
private final Backoff backoff;
private final DuplexPluginCallback callback;
private final int maxLatency;
private final Semaphore discoverySemaphore = new Semaphore(1);
private final AtomicBoolean used = new AtomicBoolean(false);
private volatile boolean running = false;
@@ -273,95 +261,6 @@ class BluetoothPlugin implements DuplexPlugin {
return new BluetoothTransportConnection(this, s);
}
@Override
public boolean supportsInvitations() {
return true;
}
@Override
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice) {
if (!running) return null;
// Use the invitation codes to generate the UUID
byte[] b = r.nextBytes(UUID_BYTES);
String uuid = UUID.nameUUIDFromBytes(b).toString();
String url = makeUrl("localhost", uuid);
// Make the device discoverable if possible
makeDeviceDiscoverable();
// Bind a server socket for receiving invitation connections
final StreamConnectionNotifier ss;
try {
ss = (StreamConnectionNotifier) Connector.open(url);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
if (!running) {
tryToClose(ss);
return null;
}
// Create the background tasks
CompletionService<StreamConnection> complete =
new ExecutorCompletionService<>(ioExecutor);
List<Future<StreamConnection>> futures = new ArrayList<>();
if (alice) {
// Return the first connected socket
futures.add(complete.submit(new ListeningTask(ss)));
futures.add(complete.submit(new DiscoveryTask(uuid)));
} else {
// Return the first socket with readable data
futures.add(complete.submit(new ReadableTask(
new ListeningTask(ss))));
futures.add(complete.submit(new ReadableTask(
new DiscoveryTask(uuid))));
}
StreamConnection chosen = null;
try {
Future<StreamConnection> f = complete.poll(timeout, MILLISECONDS);
if (f == null) return null; // No task completed within the timeout
chosen = f.get();
return new BluetoothTransportConnection(this, chosen);
} catch (InterruptedException e) {
LOG.info("Interrupted while exchanging invitations");
Thread.currentThread().interrupt();
return null;
} catch (ExecutionException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
} finally {
// Closing the socket will terminate the listener task
tryToClose(ss);
closeSockets(futures, chosen);
}
}
private void closeSockets(final List<Future<StreamConnection>> futures,
@Nullable final StreamConnection chosen) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
for (Future<StreamConnection> f : futures) {
try {
if (f.cancel(true)) {
LOG.info("Cancelled task");
} else {
StreamConnection s = f.get();
if (s != null && s != chosen) {
LOG.info("Closing unwanted socket");
s.close();
}
}
} catch (InterruptedException e) {
LOG.info("Interrupted while closing sockets");
return;
} catch (ExecutionException | IOException e) {
if (LOG.isLoggable(INFO)) LOG.info(e.toString());
}
}
}
});
}
@Override
public boolean supportsKeyAgreement() {
return true;
@@ -376,7 +275,7 @@ class BluetoothPlugin implements DuplexPlugin {
String url = makeUrl("localhost", uuid);
// Make the device discoverable if possible
makeDeviceDiscoverable();
// Bind a server socket for receiving invitation connections
// Bind a server socket for receiving key agreementconnections
final StreamConnectionNotifier ss;
try {
ss = (StreamConnectionNotifier) Connector.open(url);
@@ -431,77 +330,6 @@ class BluetoothPlugin implements DuplexPlugin {
}
}
private class DiscoveryTask implements Callable<StreamConnection> {
private final String uuid;
private DiscoveryTask(String uuid) {
this.uuid = uuid;
}
@Override
public StreamConnection call() throws Exception {
// Repeat discovery until we connect or get interrupted
DiscoveryAgent discoveryAgent = localDevice.getDiscoveryAgent();
while (true) {
if (!discoverySemaphore.tryAcquire())
throw new Exception("Discovery is already in progress");
try {
InvitationListener listener =
new InvitationListener(discoveryAgent, uuid);
discoveryAgent.startInquiry(GIAC, listener);
String url = listener.waitForUrl();
if (url != null) {
StreamConnection s = connect(url);
if (s != null) {
LOG.info("Outgoing connection");
return s;
}
}
} finally {
discoverySemaphore.release();
}
}
}
}
private static class ListeningTask implements Callable<StreamConnection> {
private final StreamConnectionNotifier serverSocket;
private ListeningTask(StreamConnectionNotifier serverSocket) {
this.serverSocket = serverSocket;
}
@Override
public StreamConnection call() throws Exception {
StreamConnection s = serverSocket.acceptAndOpen();
LOG.info("Incoming connection");
return s;
}
}
private static class ReadableTask implements Callable<StreamConnection> {
private final Callable<StreamConnection> connectionTask;
private ReadableTask(Callable<StreamConnection> connectionTask) {
this.connectionTask = connectionTask;
}
@Override
public StreamConnection call() throws Exception {
StreamConnection s = connectionTask.call();
InputStream in = s.openInputStream();
while (in.available() == 0) {
LOG.info("Waiting for data");
Thread.sleep(1000);
}
LOG.info("Data available");
return s;
}
}
private class BluetoothKeyAgreementListener extends KeyAgreementListener {
private final StreamConnectionNotifier ss;

View File

@@ -1,109 +0,0 @@
package org.briarproject.bramble.plugin.bluetooth;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DataElement;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import static java.util.logging.Level.WARNING;
class InvitationListener implements DiscoveryListener {
private static final Logger LOG =
Logger.getLogger(InvitationListener.class.getName());
private final AtomicInteger searches = new AtomicInteger(1);
private final CountDownLatch finished = new CountDownLatch(1);
private final DiscoveryAgent discoveryAgent;
private final String uuid;
private volatile String url = null;
InvitationListener(DiscoveryAgent discoveryAgent, String uuid) {
this.discoveryAgent = discoveryAgent;
this.uuid = uuid;
}
String waitForUrl() throws InterruptedException {
finished.await();
return url;
}
@Override
public void deviceDiscovered(RemoteDevice device, DeviceClass deviceClass) {
UUID[] uuids = new UUID[] {new UUID(uuid, false)};
// Try to discover the services associated with the UUID
try {
discoveryAgent.searchServices(null, uuids, device, this);
searches.incrementAndGet();
} catch (BluetoothStateException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
@Override
public void servicesDiscovered(int transaction, ServiceRecord[] services) {
for (ServiceRecord record : services) {
// Does this service have a URL?
String serviceUrl = record.getConnectionURL(
ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
if (serviceUrl == null) continue;
// Does this service have the UUID we're looking for?
Collection<String> uuids = new TreeSet<>();
findNestedClassIds(record.getAttributeValue(0x1), uuids);
for (String u : uuids) {
if (uuid.equalsIgnoreCase(u)) {
// The UUID matches - store the URL
url = serviceUrl;
finished.countDown();
return;
}
}
}
}
@Override
public void inquiryCompleted(int discoveryType) {
if (searches.decrementAndGet() == 0) finished.countDown();
}
@Override
public void serviceSearchCompleted(int transaction, int response) {
if (searches.decrementAndGet() == 0) finished.countDown();
}
// UUIDs are sometimes buried in nested data elements
private void findNestedClassIds(Object o, Collection<String> ids) {
o = getDataElementValue(o);
if (o instanceof Enumeration<?>) {
for (Object o1 : Collections.list((Enumeration<?>) o))
findNestedClassIds(o1, ids);
} else if (o instanceof UUID) {
ids.add(o.toString());
}
}
private Object getDataElementValue(Object o) {
if (o instanceof DataElement) {
// Bluecove throws an exception if the type is unknown
try {
return ((DataElement) o).getValue();
} catch (ClassCastException e) {
return null;
}
}
return null;
}
}

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.plugin.modem;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.PseudoRandom;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
@@ -167,17 +166,6 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
return new ModemTransportConnection();
}
@Override
public boolean supportsInvitations() {
return false;
}
@Override
public DuplexTransportConnection createInvitationConnection(PseudoRandom r,
long timeout, boolean alice) {
throw new UnsupportedOperationException();
}
@Override
public boolean supportsKeyAgreement() {
return false;

View File

@@ -1,6 +1,6 @@
[main]
host = https://www.transifex.com
lang_map = pt_BR: pt-rBR, fr_FR: fr
lang_map = pt_BR: pt-rBR, fr_FR: fr, nb_NO: nb
[briar.stringsxml-5]
file_filter = src/main/res/values-<lang>/strings.xml

View File

@@ -78,15 +78,19 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 22
versionCode 1604
versionName "0.16.4"
versionCode 1610
versionName "0.16.10"
applicationId "org.briarproject.briar.beta"
resValue "string", "app_package", "org.briarproject.briar.beta"
resValue "string", "app_name", "Briar Beta"
buildConfigField "String", "GitHash", "\"${getGitHash()}\""
}
buildTypes {
debug {
applicationIdSuffix ".debug"
resValue "string", "app_package", "org.briarproject.briar.beta.debug"
resValue "string", "app_name", "Briar Debug"
shrinkResources false
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'

View File

@@ -15,8 +15,6 @@
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Since API 23, this is needed to add contacts via Bluetooth -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:name="org.briarproject.briar.android.BriarApplicationImpl"
@@ -292,16 +290,6 @@
/>
</activity>
<activity
android:name="org.briarproject.briar.android.invitation.AddContactActivity"
android:label="@string/add_contact_title"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.keyagreement.KeyAgreementActivity"
android:label="@string/add_contact_title"

View File

@@ -12,7 +12,6 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.invitation.InvitationTaskFactory;
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
import org.briarproject.bramble.api.keyagreement.PayloadParser;
@@ -89,8 +88,6 @@ public interface AndroidComponent
EventBus eventBus();
InvitationTaskFactory invitationTaskFactory();
AndroidNotificationManager androidNotificationManager();
ScreenFilterMonitor screenFilterMonitor();

View File

@@ -5,11 +5,8 @@ import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.UiThread;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.support.v4.content.ContextCompat;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DatabaseExecutor;
@@ -25,12 +22,14 @@ import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.R;
import org.briarproject.briar.android.contact.ConversationActivity;
import org.briarproject.briar.android.forum.ForumActivity;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
import org.briarproject.briar.android.util.BriarNotificationBuilder;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.blog.event.BlogPostAddedEvent;
import org.briarproject.briar.api.forum.event.ForumPostReceivedEvent;
@@ -48,6 +47,7 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
@@ -61,8 +61,6 @@ import static android.content.Context.NOTIFICATION_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
import static android.support.v4.app.NotificationCompat.VISIBILITY_PRIVATE;
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
import static java.util.logging.Level.WARNING;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
import static org.briarproject.briar.android.contact.ConversationActivity.CONTACT_ID;
@@ -95,6 +93,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private static final String BLOG_URI =
"content://org.briarproject.briar/blog";
private static final long SOUND_DELAY = TimeUnit.SECONDS.toMillis(2);
private static final Logger LOG =
Logger.getLogger(AndroidNotificationManagerImpl.class.getName());
@@ -102,6 +102,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private final SettingsManager settingsManager;
private final AndroidExecutor androidExecutor;
private final Context appContext;
private final Clock clock;
private final AtomicBoolean used = new AtomicBoolean(false);
// The following must only be accessed on the main UI thread
@@ -117,16 +118,18 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private boolean blockContacts = false, blockGroups = false;
private boolean blockForums = false, blockBlogs = false;
private boolean blockIntroductions = false;
private long lastSound = 0;
private volatile Settings settings = new Settings();
@Inject
AndroidNotificationManagerImpl(@DatabaseExecutor Executor dbExecutor,
SettingsManager settingsManager, AndroidExecutor androidExecutor,
Application app) {
Application app, Clock clock) {
this.dbExecutor = dbExecutor;
this.settingsManager = settingsManager;
this.androidExecutor = androidExecutor;
this.clock = clock;
appContext = app.getApplicationContext();
}
@@ -288,22 +291,19 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (contactTotal == 0) {
clearContactNotification();
} else if (settings.getBoolean(PREF_NOTIFY_PRIVATE, true)) {
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
BriarNotificationBuilder b =
new BriarNotificationBuilder(appContext);
b.setSmallIcon(R.drawable.notification_private_message);
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setColorRes(R.color.briar_primary);
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.private_message_notification_text, contactTotal,
contactTotal));
boolean sound = settings.getBoolean(PREF_NOTIFY_SOUND, true);
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (sound && !StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
b.setNumber(contactTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_MESSAGE, showOnLockScreen);
playSound(b);
if (contactCounts.size() == 1) {
// Touching the notification shows the relevant conversation
Intent i = new Intent(appContext, ConversationActivity.class);
@@ -326,21 +326,27 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
}
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_MESSAGE);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(PRIVATE_MESSAGE_NOTIFICATION_ID, b.build());
}
}
@UiThread
private void playSound(BriarNotificationBuilder b) {
boolean sound = settings.getBoolean(PREF_NOTIFY_SOUND, true);
if (!sound) return;
long currentTime = clock.currentTimeMillis();
if (currentTime - lastSound > SOUND_DELAY) {
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
lastSound = clock.currentTimeMillis();
}
}
@UiThread
private int getDefaults() {
int defaults = DEFAULT_LIGHTS;
@@ -387,21 +393,19 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (groupTotal == 0) {
clearGroupMessageNotification();
} else if (settings.getBoolean(PREF_NOTIFY_GROUP, true)) {
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
BriarNotificationBuilder b =
new BriarNotificationBuilder(appContext);
b.setSmallIcon(R.drawable.notification_private_group);
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setColorRes(R.color.briar_primary);
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.group_message_notification_text, groupTotal,
groupTotal));
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
b.setNumber(groupTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
playSound(b);
if (groupCounts.size() == 1) {
// Touching the notification shows the relevant group
Intent i = new Intent(appContext, GroupActivity.class);
@@ -425,15 +429,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
}
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(GROUP_MESSAGE_NOTIFICATION_ID, b.build());
@@ -474,21 +469,19 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (forumTotal == 0) {
clearForumPostNotification();
} else if (settings.getBoolean(PREF_NOTIFY_FORUM, true)) {
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
BriarNotificationBuilder b =
new BriarNotificationBuilder(appContext);
b.setSmallIcon(R.drawable.notification_forum);
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setColorRes(R.color.briar_primary);
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.forum_post_notification_text, forumTotal,
forumTotal));
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
b.setNumber(forumTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
playSound(b);
if (forumCounts.size() == 1) {
// Touching the notification shows the relevant forum
Intent i = new Intent(appContext, ForumActivity.class);
@@ -512,15 +505,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
}
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(FORUM_POST_NOTIFICATION_ID, b.build());
@@ -561,21 +545,19 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
if (blogTotal == 0) {
clearBlogPostNotification();
} else if (settings.getBoolean(PREF_NOTIFY_BLOG, true)) {
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
BriarNotificationBuilder b =
new BriarNotificationBuilder(appContext);
b.setSmallIcon(R.drawable.notification_blog);
b.setColor(ContextCompat.getColor(appContext,
R.color.briar_primary));
b.setColorRes(R.color.briar_primary);
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.blog_post_notification_text, blogTotal,
blogTotal));
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
b.setNumber(blogTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
playSound(b);
// Touching the notification shows the combined blog feed
Intent i = new Intent(appContext, NavDrawerActivity.class);
i.putExtra(INTENT_BLOGS, true);
@@ -585,15 +567,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addParentStack(NavDrawerActivity.class);
t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(BLOG_POST_NOTIFICATION_ID, b.build());
@@ -623,20 +597,17 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
@UiThread
private void updateIntroductionNotification() {
NotificationCompat.Builder b =
new NotificationCompat.Builder(appContext);
BriarNotificationBuilder b = new BriarNotificationBuilder(appContext);
b.setSmallIcon(R.drawable.notification_introduction);
b.setColor(ContextCompat.getColor(appContext, R.color.briar_primary));
b.setColorRes(R.color.briar_primary);
b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.introduction_notification_text, introductionTotal,
introductionTotal));
String ringtoneUri = settings.get(PREF_NOTIFY_RINGTONE_URI);
if (!StringUtils.isNullOrEmpty(ringtoneUri))
b.setSound(Uri.parse(ringtoneUri));
b.setDefaults(getDefaults());
b.setOnlyAlertOnce(true);
b.setAutoCancel(true);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_MESSAGE, showOnLockScreen);
playSound(b);
// Touching the notification shows the contact list
Intent i = new Intent(appContext, NavDrawerActivity.class);
i.putExtra(INTENT_CONTACTS, true);
@@ -646,15 +617,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addParentStack(NavDrawerActivity.class);
t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_MESSAGE);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID, b.build());

View File

@@ -2,6 +2,9 @@ package org.briarproject.briar.android;
import android.app.Application;
import android.content.Context;
import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy;
import android.os.StrictMode.VmPolicy;
import org.acra.ACRA;
import org.acra.ReportingInteractionMode;
@@ -33,6 +36,8 @@ import static org.acra.ReportField.REPORT_ID;
import static org.acra.ReportField.STACK_TRACE;
import static org.acra.ReportField.USER_APP_START_DATE;
import static org.acra.ReportField.USER_CRASH_DATE;
import static org.briarproject.briar.android.TestingConstants.DEFAULT_LOG_LEVEL;
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
@ReportsCrashes(
reportPrimerClass = BriarReportPrimer.class,
@@ -72,6 +77,9 @@ public class BriarApplicationImpl extends Application
@Override
public void onCreate() {
super.onCreate();
if (IS_DEBUG_BUILD) enableStrictMode();
Logger.getLogger("").setLevel(DEFAULT_LOG_LEVEL);
LOG.info("Created");
applicationComponent = DaggerAndroidComponent.builder()
@@ -85,6 +93,17 @@ public class BriarApplicationImpl extends Application
AndroidEagerSingletons.initEagerSingletons(applicationComponent);
}
private void enableStrictMode() {
ThreadPolicy.Builder threadPolicy = new ThreadPolicy.Builder();
threadPolicy.detectAll();
threadPolicy.penaltyLog();
StrictMode.setThreadPolicy(threadPolicy.build());
VmPolicy.Builder vmPolicy = new VmPolicy.Builder();
vmPolicy.detectAll();
vmPolicy.penaltyLog();
StrictMode.setVmPolicy(vmPolicy.build());
}
@Override
public AndroidComponent getApplicationComponent() {
return applicationComponent;

View File

@@ -10,13 +10,21 @@ import static java.util.logging.Level.OFF;
public interface TestingConstants {
/**
* Whether this is an alpha or beta build. This should be set to false for
* Whether this is a debug build.
*/
boolean IS_DEBUG_BUILD = BuildConfig.DEBUG;
/**
* Whether this is a beta build. This should be set to false for final
* release builds.
*/
boolean TESTING = BuildConfig.DEBUG;
boolean IS_BETA_BUILD = true;
/** Default log level. */
Level DEFAULT_LOG_LEVEL = TESTING ? INFO : OFF;
/**
* Default log level. Disable logging for final release builds.
*/
@SuppressWarnings("ConstantConditions")
Level DEFAULT_LOG_LEVEL = IS_DEBUG_BUILD || IS_BETA_BUILD ? INFO : OFF;
/**
* Whether to prevent screenshots from being taken. Setting this to true
@@ -24,5 +32,5 @@ public interface TestingConstants {
* Unfortunately this also prevents the user from taking screenshots
* intentionally.
*/
boolean PREVENT_SCREENSHOTS = !TESTING;
boolean PREVENT_SCREENSHOTS = !IS_DEBUG_BUILD;
}

View File

@@ -24,7 +24,6 @@ import org.briarproject.briar.android.forum.ForumModule;
import org.briarproject.briar.android.introduction.ContactChooserFragment;
import org.briarproject.briar.android.introduction.IntroductionActivity;
import org.briarproject.briar.android.introduction.IntroductionMessageFragment;
import org.briarproject.briar.android.invitation.AddContactActivity;
import org.briarproject.briar.android.keyagreement.IntroFragment;
import org.briarproject.briar.android.keyagreement.KeyAgreementActivity;
import org.briarproject.briar.android.keyagreement.ShowQrCodeFragment;
@@ -91,8 +90,6 @@ public interface ActivityComponent {
void inject(PanicPreferencesActivity activity);
void inject(AddContactActivity activity);
void inject(KeyAgreementActivity activity);
void inject(ConversationActivity activity);

View File

@@ -8,14 +8,16 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.blog.BaseController.BlogListener;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.api.blog.BlogPostHeader;
import javax.inject.Inject;
@UiThread
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class BlogPostFragment extends BasePostFragment {
public class BlogPostFragment extends BasePostFragment implements BlogListener {
private static final String TAG = BlogPostFragment.class.getName();
@@ -40,6 +42,7 @@ public class BlogPostFragment extends BasePostFragment {
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
blogController.setBlogListener(this);
}
@Override
@@ -59,4 +62,15 @@ public class BlogPostFragment extends BasePostFragment {
}
});
}
@Override
public void onBlogPostAdded(BlogPostHeader header, boolean local) {
// doesn't matter here
}
@Override
public void onBlogRemoved() {
finish();
}
}

View File

@@ -18,8 +18,8 @@ public class ReblogActivity extends BriarActivity implements
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setSceneTransitionAnimation();
super.onCreate(savedInstanceState);
Intent intent = getIntent();
byte[] groupId = intent.getByteArrayExtra(GROUP_ID);

View File

@@ -186,8 +186,8 @@ public class ConversationActivity extends BriarActivity
@SuppressWarnings("ConstantConditions")
@Override
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
setSceneTransitionAnimation();
super.onCreate(state);
Intent i = getIntent();
int id = i.getIntExtra(CONTACT_ID, -1);

View File

@@ -28,7 +28,6 @@ import org.briarproject.briar.android.threaded.ThreadItemAdapter;
import org.briarproject.briar.android.threaded.ThreadListActivity;
import org.briarproject.briar.android.threaded.ThreadListController;
import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumPostHeader;
import javax.annotation.Nullable;
import javax.inject.Inject;
@@ -41,7 +40,7 @@ import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_POST_BOD
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class ForumActivity extends
ThreadListActivity<Forum, ThreadItemAdapter<ForumItem>, ForumItem, ForumPostHeader>
ThreadListActivity<Forum, ForumItem, ThreadItemAdapter<ForumItem>>
implements ForumListener {
@Inject
@@ -53,7 +52,7 @@ public class ForumActivity extends
}
@Override
protected ThreadListController<Forum, ForumItem, ForumPostHeader> getController() {
protected ThreadListController<Forum, ForumItem> getController() {
return forumController;
}

View File

@@ -6,13 +6,11 @@ import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.threaded.ThreadListController;
import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumPostHeader;
@NotNullByDefault
interface ForumController
extends ThreadListController<Forum, ForumItem, ForumPostHeader> {
interface ForumController extends ThreadListController<Forum, ForumItem> {
interface ForumListener extends ThreadListListener<ForumPostHeader> {
interface ForumListener extends ThreadListListener<ForumItem> {
@UiThread
void onForumLeft(ContactId c);
}

View File

@@ -75,10 +75,10 @@ class ForumControllerImpl extends
super.eventOccurred(e);
if (e instanceof ForumPostReceivedEvent) {
ForumPostReceivedEvent pe = (ForumPostReceivedEvent) e;
if (pe.getGroupId().equals(getGroupId())) {
ForumPostReceivedEvent f = (ForumPostReceivedEvent) e;
if (f.getGroupId().equals(getGroupId())) {
LOG.info("Forum post received, adding...");
onForumPostHeaderReceived(pe.getForumPostHeader());
onForumPostReceived(f.getHeader(), f.getBody());
}
} else if (e instanceof ForumInvitationResponseReceivedEvent) {
ForumInvitationResponseReceivedEvent f =
@@ -90,10 +90,10 @@ class ForumControllerImpl extends
onForumInvitationAccepted(r.getContactId());
}
} else if (e instanceof ContactLeftShareableEvent) {
ContactLeftShareableEvent s = (ContactLeftShareableEvent) e;
if (s.getGroupId().equals(getGroupId())) {
ContactLeftShareableEvent c = (ContactLeftShareableEvent) e;
if (c.getGroupId().equals(getGroupId())) {
LOG.info("Forum left by contact");
onForumLeft(s.getContactId());
onForumLeft(c.getContactId());
}
}
}
@@ -195,11 +195,12 @@ class ForumControllerImpl extends
return new ForumItem(header, body);
}
private void onForumPostHeaderReceived(final ForumPostHeader h) {
private void onForumPostReceived(ForumPostHeader h, String body) {
final ForumItem item = buildItem(h, body);
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
listener.onHeaderReceived(h);
listener.onItemReceived(item);
}
});
}

View File

@@ -254,7 +254,7 @@ public class ForumListFragment extends BaseEventFragment implements
} else if (e instanceof ForumPostReceivedEvent) {
ForumPostReceivedEvent f = (ForumPostReceivedEvent) e;
LOG.info("Forum post added, updating item");
updateItem(f.getGroupId(), f.getForumPostHeader());
updateItem(f.getGroupId(), f.getHeader());
} else if (e instanceof ForumInvitationRequestReceivedEvent) {
LOG.info("Forum invitation received, reloading available forums");
loadAvailableForums();

View File

@@ -1,449 +0,0 @@
package org.briarproject.briar.android.invitation;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.invitation.InvitationListener;
import org.briarproject.bramble.api.invitation.InvitationState;
import org.briarproject.bramble.api.invitation.InvitationTask;
import org.briarproject.bramble.api.invitation.InvitationTaskFactory;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.api.android.ReferenceManager;
import java.util.logging.Logger;
import javax.inject.Inject;
import static android.widget.Toast.LENGTH_LONG;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH;
import static org.briarproject.briar.android.invitation.ConfirmationCodeView.ConfirmationState.CONNECTED;
import static org.briarproject.briar.android.invitation.ConfirmationCodeView.ConfirmationState.DETAILS;
import static org.briarproject.briar.android.invitation.ConfirmationCodeView.ConfirmationState.WAIT_FOR_CONTACT;
public class AddContactActivity extends BriarActivity
implements InvitationListener {
private static final Logger LOG =
Logger.getLogger(AddContactActivity.class.getName());
@Inject
CryptoComponent crypto;
@Inject
InvitationTaskFactory invitationTaskFactory;
@Inject
ReferenceManager referenceManager;
private AddContactView view = null;
private InvitationTask task = null;
private long taskHandle = -1;
private AuthorId localAuthorId = null;
private int localInvitationCode = -1, remoteInvitationCode = -1;
private int localConfirmationCode = -1, remoteConfirmationCode = -1;
private boolean connected = false, connectionFailed = false;
private boolean localCompared = false, remoteCompared = false;
private boolean localMatched = false, remoteMatched = false;
private String contactName = null;
// Fields that are accessed from background threads must be volatile
@Inject
volatile IdentityManager identityManager;
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
if (state == null) {
// This is a new activity
setView(new ChooseIdentityView(this));
} else {
// Restore the activity's state
byte[] b = state.getByteArray("briar.LOCAL_AUTHOR_ID");
if (b != null) localAuthorId = new AuthorId(b);
taskHandle = state.getLong("briar.TASK_HANDLE", -1);
task = referenceManager.getReference(taskHandle,
InvitationTask.class);
if (task == null) {
// No background task - we must be in an initial or final state
localInvitationCode = state.getInt("briar.LOCAL_CODE");
remoteInvitationCode = state.getInt("briar.REMOTE_CODE");
connectionFailed = state.getBoolean("briar.FAILED");
contactName = state.getString("briar.CONTACT_NAME");
if (contactName != null) {
localCompared = remoteCompared = true;
localMatched = remoteMatched = true;
}
// Set the appropriate view for the state
if (localInvitationCode == -1) {
setView(new ChooseIdentityView(this));
} else if (remoteInvitationCode == -1) {
setView(new InvitationCodeView(this));
} else if (connectionFailed) {
setView(new ErrorView(this, R.string.connection_failed,
R.string.could_not_find_contact));
} else if (contactName == null) {
setView(new ErrorView(this, R.string.codes_do_not_match,
R.string.interfering));
} else {
showToastAndFinish();
}
} else {
// A background task exists - listen to it and get its state
InvitationState s = task.addListener(this);
localInvitationCode = s.getLocalInvitationCode();
remoteInvitationCode = s.getRemoteInvitationCode();
localConfirmationCode = s.getLocalConfirmationCode();
remoteConfirmationCode = s.getRemoteConfirmationCode();
connected = s.getConnected();
connectionFailed = s.getConnectionFailed();
localCompared = s.getLocalCompared();
remoteCompared = s.getRemoteCompared();
localMatched = s.getLocalMatched();
remoteMatched = s.getRemoteMatched();
contactName = s.getContactName();
// Set the appropriate view for the state
if (localInvitationCode == -1) {
setView(new ChooseIdentityView(this));
} else if (remoteInvitationCode == -1) {
setView(new InvitationCodeView(this));
} else if (connectionFailed) {
setView(new ErrorView(AddContactActivity.this,
R.string.connection_failed,
R.string.could_not_find_contact));
} else if (connected && localConfirmationCode == -1) {
setView(new ConfirmationCodeView(this, CONNECTED));
} else if (localConfirmationCode == -1) {
setView(new InvitationCodeView(this, true));
} else if (!localCompared) {
setView(new ConfirmationCodeView(this));
} else if (!remoteCompared) {
setView(new ConfirmationCodeView(this, WAIT_FOR_CONTACT));
} else if (localMatched && remoteMatched) {
if (contactName == null) {
setView(new ConfirmationCodeView(this, DETAILS));
} else {
showToastAndFinish();
}
} else {
setView(new ErrorView(this, R.string.codes_do_not_match,
R.string.interfering));
}
}
}
}
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
private void showToastAndFinish() {
String format = getString(R.string.contact_added_toast);
String text = String.format(format, contactName);
Toast.makeText(this, text, LENGTH_LONG).show();
supportFinishAfterTransition();
}
@Override
public void onStart() {
super.onStart();
view.populate();
}
@Override
public void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
if (localAuthorId != null) {
byte[] b = localAuthorId.getBytes();
state.putByteArray("briar.LOCAL_AUTHOR_ID", b);
}
state.putInt("briar.LOCAL_CODE", localInvitationCode);
state.putInt("briar.REMOTE_CODE", remoteInvitationCode);
state.putBoolean("briar.FAILED", connectionFailed);
state.putString("briar.CONTACT_NAME", contactName);
if (task != null) state.putLong("briar.TASK_HANDLE", taskHandle);
}
@Override
public void onDestroy() {
super.onDestroy();
if (task != null) task.removeListener(this);
}
@Override
public void onActivityResult(int request, int result, Intent data) {
if (request == REQUEST_BLUETOOTH) {
if (result != RESULT_CANCELED) reset(new InvitationCodeView(this));
}
}
@SuppressWarnings("ConstantConditions")
void setView(AddContactView view) {
this.view = view;
view.init(this);
setContentView(view);
getSupportActionBar().setTitle(R.string.add_contact_title);
}
void reset(AddContactView view) {
// Don't reset localAuthorId
task = null;
taskHandle = -1;
localInvitationCode = -1;
localConfirmationCode = remoteConfirmationCode = -1;
connected = connectionFailed = false;
localCompared = remoteCompared = false;
localMatched = remoteMatched = false;
contactName = null;
setView(view);
}
void loadLocalAuthor() {
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
long now = System.currentTimeMillis();
LocalAuthor author = identityManager.getLocalAuthor();
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Loading author took " + duration + " ms");
setLocalAuthorId(author.getId());
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
void setLocalAuthorId(final AuthorId localAuthorId) {
runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
AddContactActivity.this.localAuthorId = localAuthorId;
}
});
}
int getLocalInvitationCode() {
if (localInvitationCode == -1)
localInvitationCode = crypto.generateBTInvitationCode();
return localInvitationCode;
}
int getRemoteInvitationCode() {
return remoteInvitationCode;
}
void remoteInvitationCodeEntered(int code) {
if (localAuthorId == null) throw new IllegalStateException();
if (localInvitationCode == -1) throw new IllegalStateException();
remoteInvitationCode = code;
// change UI to show a progress indicator
setView(new InvitationCodeView(this, true));
task = invitationTaskFactory.createTask(localInvitationCode, code);
taskHandle = referenceManager.putReference(task, InvitationTask.class);
task.addListener(AddContactActivity.this);
// Add a second listener so we can remove the first in onDestroy(),
// allowing the activity to be garbage collected if it's destroyed
task.addListener(new ReferenceCleaner(referenceManager, taskHandle));
task.connect();
}
int getLocalConfirmationCode() {
return localConfirmationCode;
}
void remoteConfirmationCodeEntered(int code) {
localCompared = true;
if (code == remoteConfirmationCode) {
localMatched = true;
if (remoteMatched) {
setView(new ConfirmationCodeView(this, DETAILS));
} else if (remoteCompared) {
setView(new ErrorView(this, R.string.codes_do_not_match,
R.string.interfering));
} else {
setView(new ConfirmationCodeView(this, WAIT_FOR_CONTACT));
}
task.localConfirmationSucceeded();
} else {
localMatched = false;
setView(new ErrorView(this, R.string.codes_do_not_match,
R.string.interfering));
task.localConfirmationFailed();
}
}
@Override
public void connectionSucceeded() {
runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
connected = true;
setView(new ConfirmationCodeView(AddContactActivity.this,
CONNECTED));
}
});
}
@Override
public void connectionFailed() {
runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
connectionFailed = true;
setView(new ErrorView(AddContactActivity.this,
R.string.connection_failed,
R.string.could_not_find_contact));
}
});
}
@Override
public void keyAgreementSucceeded(final int localCode,
final int remoteCode) {
runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
localConfirmationCode = localCode;
remoteConfirmationCode = remoteCode;
setView(new ConfirmationCodeView(AddContactActivity.this));
}
});
}
@Override
public void keyAgreementFailed() {
runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
connectionFailed = true;
setView(new ErrorView(AddContactActivity.this,
R.string.connection_failed,
R.string.could_not_find_contact));
}
});
}
@Override
public void remoteConfirmationSucceeded() {
runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
remoteCompared = true;
remoteMatched = true;
if (localMatched) {
setView(new ConfirmationCodeView(AddContactActivity.this,
DETAILS));
}
}
});
}
@Override
public void remoteConfirmationFailed() {
runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
remoteCompared = true;
remoteMatched = false;
if (localMatched) {
setView(new ErrorView(AddContactActivity.this,
R.string.codes_do_not_match, R.string.interfering));
}
}
});
}
@Override
public void pseudonymExchangeSucceeded(final String remoteName) {
runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
contactName = remoteName;
showToastAndFinish();
}
});
}
@Override
public void pseudonymExchangeFailed() {
runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
setView(new ErrorView(AddContactActivity.this,
R.string.connection_failed,
R.string.could_not_find_contact));
}
});
}
/**
* Cleans up the reference to the invitation task when the task completes.
* This class is static to prevent memory leaks.
*/
private static class ReferenceCleaner implements InvitationListener {
private final ReferenceManager referenceManager;
private final long handle;
private ReferenceCleaner(ReferenceManager referenceManager,
long handle) {
this.referenceManager = referenceManager;
this.handle = handle;
}
@Override
public void connectionSucceeded() {
// Wait for key agreement to succeed or fail
}
@Override
public void connectionFailed() {
referenceManager.removeReference(handle, InvitationTask.class);
}
@Override
public void keyAgreementSucceeded(int localCode, int remoteCode) {
// Wait for remote confirmation to succeed or fail
}
@Override
public void keyAgreementFailed() {
referenceManager.removeReference(handle, InvitationTask.class);
}
@Override
public void remoteConfirmationSucceeded() {
// Wait for the pseudonym exchange to succeed or fail
}
@Override
public void remoteConfirmationFailed() {
referenceManager.removeReference(handle, InvitationTask.class);
}
@Override
public void pseudonymExchangeSucceeded(String remoteName) {
referenceManager.removeReference(handle, InvitationTask.class);
}
@Override
public void pseudonymExchangeFailed() {
referenceManager.removeReference(handle, InvitationTask.class);
}
}
}

View File

@@ -1,22 +0,0 @@
package org.briarproject.briar.android.invitation;
import android.content.Context;
import android.widget.LinearLayout;
abstract class AddContactView extends LinearLayout {
static final public int CODE_LEN = 6;
protected AddContactActivity container = null;
AddContactView(Context ctx) {
super(ctx);
}
void init(AddContactActivity container) {
this.container = container;
populate();
}
abstract void populate();
}

View File

@@ -1,43 +0,0 @@
package org.briarproject.briar.android.invitation;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import org.briarproject.briar.R;
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
import static android.bluetooth.BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH;
class ChooseIdentityView extends AddContactView implements OnClickListener {
ChooseIdentityView(Context ctx) {
super(ctx);
}
@Override
void populate() {
removeAllViews();
Context ctx = getContext();
LayoutInflater inflater = (LayoutInflater) ctx.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.invitation_bluetooth_start, this);
Button continueButton = (Button) view.findViewById(R.id.continueButton);
continueButton.setOnClickListener(this);
container.loadLocalAuthor();
}
@Override
public void onClick(View view) {
Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
i.putExtra(EXTRA_DISCOVERABLE_DURATION, 120);
container.startActivityForResult(i, REQUEST_BLUETOOTH);
}
}

View File

@@ -1,121 +0,0 @@
package org.briarproject.briar.android.invitation;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import org.briarproject.briar.R;
import static android.content.Context.INPUT_METHOD_SERVICE;
class ConfirmationCodeView extends AddContactView {
public enum ConfirmationState { CONNECTED, ENTER_CODE, WAIT_FOR_CONTACT, DETAILS }
private ConfirmationState state;
ConfirmationCodeView(Context ctx) {
super(ctx);
this.state = ConfirmationState.ENTER_CODE;
}
ConfirmationCodeView(Context ctx, ConfirmationState state) {
super(ctx);
this.state = state;
}
@Override
void populate() {
removeAllViews();
Context ctx = getContext();
LayoutInflater inflater = (LayoutInflater) ctx.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.invitation_bluetooth_confirmation_code, this);
// local confirmation code
TextView code = (TextView) view.findViewById(R.id.codeView);
int localCode = container.getLocalConfirmationCode();
code.setText(String.format("%06d", localCode));
if (state != ConfirmationState.ENTER_CODE) {
// hide views we no longer need
view.findViewById(R.id.enterCodeTextView).setVisibility(View.GONE);
view.findViewById(R.id.codeEntryView).setVisibility(View.GONE);
view.findViewById(R.id.continueButton).setVisibility(View.GONE);
// show progress indicator
view.findViewById(R.id.progressBar).setVisibility(View.VISIBLE);
// show what we are waiting for
TextView connecting = (TextView) view.findViewById(R.id.waitingView);
int textId;
if (state == ConfirmationState.CONNECTED) {
textId = R.string.calculating_confirmation_code;
view.findViewById(R.id.yourConfirmationCodeView).setVisibility(View.GONE);
view.findViewById(R.id.codeView).setVisibility(View.GONE);
} else if (state == ConfirmationState.WAIT_FOR_CONTACT) {
textId = R.string.waiting_for_contact;
} else {
textId = R.string.exchanging_contact_details;
}
connecting.setText(ctx.getString(textId));
connecting.setVisibility(View.VISIBLE);
}
else {
// handle click on continue button
final EditText codeEntry = (EditText) view.findViewById(R.id.codeEntryView);
final Button continueButton = (Button) view.findViewById(R.id.continueButton);
continueButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
send(codeEntry);
}
});
// activate continue button only when we have a 6 digit (CODE_LEN) code
codeEntry.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
continueButton.setEnabled(codeEntry.getText().length() == CODE_LEN);
}
@Override
public void afterTextChanged(Editable s) {
}
});
codeEntry.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_GO && v.getText().length() == CODE_LEN) {
send(v);
return true;
}
return false;
}
});
}
}
private void send(TextView codeEntry) {
int code = Integer.parseInt(codeEntry.getText().toString());
container.remoteConfirmationCodeEntered(code);
// Hide the soft keyboard
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
((InputMethodManager) o).hideSoftInputFromWindow(codeEntry.getWindowToken(), 0);
}
}

View File

@@ -1,59 +0,0 @@
package org.briarproject.briar.android.invitation;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import org.briarproject.briar.R;
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
import static android.bluetooth.BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH;
class ErrorView extends AddContactView implements OnClickListener {
private final int error;
private final int explanation;
ErrorView(Context ctx) {
super(ctx);
this.error = R.string.connection_failed;
this.explanation = R.string.could_not_find_contact;
}
ErrorView(Context ctx, int error, int explanation) {
super(ctx);
this.error = error;
this.explanation = explanation;
}
@Override
void populate() {
removeAllViews();
Context ctx = getContext();
LayoutInflater inflater = (LayoutInflater) ctx.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.invitation_error, this);
TextView errorView = (TextView) view.findViewById(R.id.errorTextView);
errorView.setText(ctx.getString(error));
TextView explanationView = (TextView) view.findViewById(R.id.explanationTextView);
explanationView.setText(ctx.getString(explanation));
Button tryAgainButton = (Button) view.findViewById(R.id.tryAgainButton);
tryAgainButton.setOnClickListener(this);
}
@Override
public void onClick(View view) {
Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
i.putExtra(EXTRA_DISCOVERABLE_DURATION, 120);
container.startActivityForResult(i, REQUEST_BLUETOOTH);
}
}

View File

@@ -1,111 +0,0 @@
package org.briarproject.briar.android.invitation;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import org.briarproject.briar.R;
import static android.content.Context.INPUT_METHOD_SERVICE;
class InvitationCodeView extends AddContactView {
private boolean waiting;
InvitationCodeView(Context ctx, boolean waiting) {
super(ctx);
this.waiting = waiting;
}
InvitationCodeView(Context ctx) {
this(ctx, false);
}
@Override
void populate() {
removeAllViews();
Context ctx = getContext();
LayoutInflater inflater = (LayoutInflater) ctx.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.invitation_bluetooth_invitation_code, this);
// local invitation code
TextView code = (TextView) view.findViewById(R.id.codeView);
int localCode = container.getLocalInvitationCode();
code.setText(String.format("%06d", localCode));
if (waiting) {
// hide views we no longer need
view.findViewById(R.id.enterCodeTextView).setVisibility(View.GONE);
view.findViewById(R.id.codeEntryView).setVisibility(View.GONE);
view.findViewById(R.id.continueButton).setVisibility(View.GONE);
// show progress indicator
view.findViewById(R.id.progressBar).setVisibility(View.VISIBLE);
// show which code we are waiting for
TextView connecting = (TextView) view.findViewById(R.id.waitingView);
int remoteCode = container.getRemoteInvitationCode();
String format = container.getString(R.string.searching_format);
connecting.setText(String.format(format, remoteCode));
connecting.setVisibility(View.VISIBLE);
}
else {
// handle click on continue button
final EditText codeEntry = (EditText) view.findViewById(R.id.codeEntryView);
final Button continueButton = (Button) view.findViewById(R.id.continueButton);
continueButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
send(codeEntry);
}
});
// activate continue button only when we have a 6 digit (CODE_LEN) code
codeEntry.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
continueButton.setEnabled(codeEntry.getText().length() == CODE_LEN);
}
@Override
public void afterTextChanged(Editable s) {
}
});
codeEntry.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_GO && v.getText().length() == CODE_LEN) {
send(v);
return true;
}
return false;
}
});
}
}
private void send(TextView codeEntry) {
int code = Integer.parseInt(codeEntry.getText().toString());
container.remoteInvitationCodeEntered(code);
// Hide the soft keyboard
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
((InputMethodManager) o).hideSoftInputFromWindow(codeEntry.getWindowToken(), 0);
}
}

View File

@@ -0,0 +1,14 @@
package org.briarproject.briar.android.keyagreement;
import java.io.IOException;
class CameraException extends IOException {
CameraException(String message) {
super(message);
}
CameraException(Throwable cause) {
super(cause);
}
}

View File

@@ -7,6 +7,7 @@ import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.util.AttributeSet;
import android.view.Surface;
@@ -43,6 +44,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
private static final Logger LOG =
Logger.getLogger(CameraView.class.getName());
@Nullable
private Camera camera = null;
private PreviewConsumer previewConsumer = null;
private Surface surface = null;
@@ -82,14 +84,14 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
}
@UiThread
public void start() {
public void start() throws CameraException {
LOG.info("Opening camera");
try {
LOG.info("Opening camera");
camera = Camera.open();
} catch (RuntimeException e) {
LOG.log(WARNING, "Error opening camera", e);
return;
throw new CameraException(e);
}
if (camera == null) throw new CameraException("No back-facing camera");
setDisplayOrientation(0);
// Use barcode scene mode if it's available
Parameters params = camera.getParameters();
@@ -113,60 +115,81 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
}
@UiThread
public void stop() {
public void stop() throws CameraException {
if (camera == null) return;
stopPreview();
LOG.info("Releasing camera");
try {
LOG.info("Releasing camera");
camera.release();
} catch (RuntimeException e) {
LOG.log(WARNING, "Error releasing camera", e);
throw new CameraException(e);
}
camera = null;
}
@UiThread
private void startPreview(SurfaceHolder holder) {
private void startPreview(SurfaceHolder holder) throws CameraException {
LOG.info("Starting preview");
if (camera == null) throw new CameraException("Camera is null");
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
previewStarted = true;
startConsumer();
} catch (IOException | RuntimeException e) {
LOG.log(WARNING, "Error starting camera preview", e);
throw new CameraException(e);
}
}
@UiThread
private void stopPreview() {
private void stopPreview() throws CameraException {
LOG.info("Stopping preview");
if (camera == null) throw new CameraException("Camera is null");
try {
stopConsumer();
camera.stopPreview();
} catch (RuntimeException e) {
LOG.log(WARNING, "Error stopping camera preview", e);
throw new CameraException(e);
}
previewStarted = false;
}
@UiThread
private void startConsumer() {
if (autoFocus) camera.autoFocus(this);
private void startConsumer() throws CameraException {
if (camera == null) throw new CameraException("Camera is null");
if (autoFocus) {
try {
camera.autoFocus(this);
} catch (RuntimeException e) {
throw new CameraException(e);
}
}
previewConsumer.start(camera);
}
@UiThread
private void stopConsumer() {
if (autoFocus) camera.cancelAutoFocus();
private void stopConsumer() throws CameraException {
if (camera == null) throw new CameraException("Camera is null");
if (autoFocus) {
try {
camera.cancelAutoFocus();
} catch (RuntimeException e) {
throw new CameraException(e);
}
}
previewConsumer.stop();
}
@UiThread
private void setDisplayOrientation(int rotationDegrees) {
private void setDisplayOrientation(int rotationDegrees)
throws CameraException {
int orientation;
CameraInfo info = new CameraInfo();
Camera.getCameraInfo(0, info);
try {
Camera.getCameraInfo(0, info);
} catch (RuntimeException e) {
throw new CameraException(e);
}
if (info.facing == CAMERA_FACING_FRONT) {
orientation = (info.orientation + rotationDegrees) % 360;
orientation = (360 - orientation) % 360;
@@ -175,49 +198,70 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
}
if (LOG.isLoggable(INFO))
LOG.info("Display orientation " + orientation + " degrees");
if (camera == null) throw new CameraException("Camera is null");
try {
camera.setDisplayOrientation(orientation);
} catch (RuntimeException e) {
LOG.log(WARNING, "Error setting display orientation", e);
throw new CameraException(e);
}
displayOrientation = orientation;
}
@UiThread
private Parameters setSceneMode(Camera camera, Parameters params) {
private Parameters setSceneMode(Camera camera, Parameters params)
throws CameraException {
List<String> sceneModes = params.getSupportedSceneModes();
if (sceneModes == null) return params;
if (LOG.isLoggable(INFO)) LOG.info("Scene modes: " + sceneModes);
if (sceneModes.contains(SCENE_MODE_BARCODE)) {
params.setSceneMode(SCENE_MODE_BARCODE);
camera.setParameters(params);
return camera.getParameters();
try {
camera.setParameters(params);
return camera.getParameters();
} catch (RuntimeException e) {
throw new CameraException(e);
}
}
return params;
}
@UiThread
private Parameters disableFlash(Camera camera, Parameters params) {
private Parameters disableFlash(Camera camera, Parameters params)
throws CameraException {
params.setFlashMode(FLASH_MODE_OFF);
camera.setParameters(params);
return camera.getParameters();
try {
camera.setParameters(params);
return camera.getParameters();
} catch (RuntimeException e) {
throw new CameraException(e);
}
}
@UiThread
private Parameters disableSceneMode(Camera camera, Parameters params) {
private Parameters disableSceneMode(Camera camera, Parameters params)
throws CameraException {
params.setSceneMode(SCENE_MODE_AUTO);
camera.setParameters(params);
return camera.getParameters();
try {
camera.setParameters(params);
return camera.getParameters();
} catch (RuntimeException e) {
throw new CameraException(e);
}
}
@UiThread
private Parameters setBestParameters(Camera camera, Parameters params) {
private Parameters setBestParameters(Camera camera, Parameters params)
throws CameraException {
setVideoStabilisation(params);
setFocusMode(params);
params.setFlashMode(FLASH_MODE_OFF);
setPreviewSize(params);
camera.setParameters(params);
return camera.getParameters();
try {
camera.setParameters(params);
return camera.getParameters();
} catch (RuntimeException e) {
throw new CameraException(e);
}
}
@UiThread
@@ -286,9 +330,15 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
}
@UiThread
private void logCameraParameters() {
private void logCameraParameters() throws CameraException {
if (camera == null) throw new AssertionError();
if (LOG.isLoggable(INFO)) {
Parameters params = camera.getParameters();
Parameters params;
try {
params = camera.getParameters();
} catch (RuntimeException e) {
throw new CameraException(e);
}
if (Build.VERSION.SDK_INT >= 15) {
LOG.info("Video stabilisation enabled: "
+ params.getVideoStabilization());
@@ -306,13 +356,18 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
post(new Runnable() {
@Override
public void run() {
surfaceCreatedUi(holder);
try {
surfaceCreatedUi(holder);
} catch (CameraException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
@UiThread
private void surfaceCreatedUi(SurfaceHolder holder) {
private void surfaceCreatedUi(SurfaceHolder holder) throws CameraException {
LOG.info("Surface created");
if (surface != null && surface != holder.getSurface()) {
LOG.info("Releasing old surface");
@@ -329,13 +384,19 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
post(new Runnable() {
@Override
public void run() {
surfaceChangedUi(holder, w, h);
try {
surfaceChangedUi(holder, w, h);
} catch (CameraException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
@UiThread
private void surfaceChangedUi(SurfaceHolder holder, int w, int h) {
private void surfaceChangedUi(SurfaceHolder holder, int w, int h)
throws CameraException {
if (LOG.isLoggable(INFO)) LOG.info("Surface changed: " + w + "x" + h);
if (surface != null && surface != holder.getSurface()) {
LOG.info("Releasing old surface");
@@ -352,7 +413,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
camera.setParameters(params);
logCameraParameters();
} catch (RuntimeException e) {
LOG.log(WARNING, "Error setting preview size", e);
throw new CameraException(e);
}
startPreview(holder);
}

View File

@@ -22,6 +22,7 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
@SuppressWarnings("deprecation")
@MethodsNotNullByDefault
@@ -60,8 +61,12 @@ class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (camera == this.camera) {
Size size = camera.getParameters().getPreviewSize();
new DecoderTask(data, size.width, size.height).execute();
try {
Size size = camera.getParameters().getPreviewSize();
new DecoderTask(data, size.width, size.height).execute();
} catch (RuntimeException e) {
LOG.log(WARNING, "Error getting camera parameters.", e);
}
}
}
@@ -70,7 +75,7 @@ class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
private final byte[] data;
private final int width, height;
DecoderTask(byte[] data, int width, int height) {
private DecoderTask(byte[] data, int width, int height) {
this.data = data;
this.width = width;
this.height = height;

View File

@@ -56,6 +56,7 @@ import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.widget.Toast.LENGTH_LONG;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -143,6 +144,12 @@ public class ShowQrCodeFragment extends BaseEventFragment
public void onStart() {
super.onStart();
try {
cameraView.start();
} catch (CameraException e) {
logCameraExceptionAndFinish(e);
}
// Listen for changes to the Bluetooth state
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_STATE_CHANGED);
@@ -162,7 +169,6 @@ public class ShowQrCodeFragment extends BaseEventFragment
} else {
startListening();
}
cameraView.start();
}
@Override
@@ -170,7 +176,19 @@ public class ShowQrCodeFragment extends BaseEventFragment
super.onStop();
stopListening();
if (receiver != null) getActivity().unregisterReceiver(receiver);
cameraView.stop();
try {
cameraView.stop();
} catch (CameraException e) {
logCameraExceptionAndFinish(e);
}
}
@UiThread
private void logCameraExceptionAndFinish(CameraException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
Toast.makeText(getActivity(), R.string.camera_error,
LENGTH_LONG).show();
finish();
}
@UiThread
@@ -218,7 +236,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
statusView.setVisibility(VISIBLE);
status.setText(R.string.connecting_to_device);
task.connectAndRunProtocol(remotePayload);
} catch (IOException e) {
} catch (IOException | IllegalArgumentException e) {
// TODO show failure
Toast.makeText(getActivity(), R.string.qr_code_invalid,
LENGTH_LONG).show();

View File

@@ -29,7 +29,6 @@ import org.briarproject.briar.android.privategroup.memberlist.GroupMemberListAct
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
import org.briarproject.briar.android.threaded.ThreadListActivity;
import org.briarproject.briar.android.threaded.ThreadListController;
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
import org.briarproject.briar.api.privategroup.PrivateGroup;
import org.briarproject.briar.api.privategroup.Visibility;
@@ -44,7 +43,7 @@ import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class GroupActivity extends
ThreadListActivity<PrivateGroup, GroupMessageAdapter, GroupMessageItem, GroupMessageHeader>
ThreadListActivity<PrivateGroup, GroupMessageItem, GroupMessageAdapter>
implements GroupListener, OnClickListener {
@Inject
@@ -60,7 +59,7 @@ public class GroupActivity extends
}
@Override
protected ThreadListController<PrivateGroup, GroupMessageItem, GroupMessageHeader> getController() {
protected ThreadListController<PrivateGroup, GroupMessageItem> getController() {
return controller;
}
@@ -276,7 +275,7 @@ public class GroupActivity extends
public void onGroupDissolved() {
setGroupEnabled(false);
AlertDialog.Builder builder =
new AlertDialog.Builder(this, R.style.BriarDialogTheme);
new AlertDialog.Builder(this, R.style.BriarDialogTheme);
builder.setTitle(getString(R.string.groups_dissolved_dialog_title));
builder.setMessage(getString(R.string.groups_dissolved_dialog_message));
builder.setNeutralButton(R.string.ok, null);

View File

@@ -8,13 +8,11 @@ import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.android.threaded.ThreadListController;
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
import org.briarproject.briar.api.privategroup.PrivateGroup;
import org.briarproject.briar.api.privategroup.Visibility;
public interface GroupController
extends
ThreadListController<PrivateGroup, GroupMessageItem, GroupMessageHeader> {
extends ThreadListController<PrivateGroup, GroupMessageItem> {
void loadLocalAuthor(
ResultExceptionHandler<LocalAuthor, DbException> handler);
@@ -22,7 +20,8 @@ public interface GroupController
void isDissolved(
ResultExceptionHandler<Boolean, DbException> handler);
interface GroupListener extends ThreadListListener<GroupMessageHeader> {
interface GroupListener extends ThreadListListener<GroupMessageItem> {
@UiThread
void onContactRelationshipRevealed(AuthorId memberId,
ContactId contactId, Visibility v);

View File

@@ -80,14 +80,15 @@ class GroupControllerImpl extends
super.eventOccurred(e);
if (e instanceof GroupMessageAddedEvent) {
GroupMessageAddedEvent gmae = (GroupMessageAddedEvent) e;
if (!gmae.isLocal() && gmae.getGroupId().equals(getGroupId())) {
GroupMessageAddedEvent g = (GroupMessageAddedEvent) e;
if (!g.isLocal() && g.getGroupId().equals(getGroupId())) {
LOG.info("Group message received, adding...");
final GroupMessageHeader h = gmae.getHeader();
final GroupMessageItem item =
buildItem(g.getHeader(), g.getBody());
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
listener.onHeaderReceived(h);
listener.onItemReceived(item);
}
});
}

View File

@@ -15,6 +15,7 @@ import android.support.v7.preference.PreferenceFragmentCompat;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.Toast;
import org.acra.ACRA;
import org.briarproject.bramble.api.db.DbException;
@@ -46,6 +47,7 @@ import static android.media.RingtoneManager.EXTRA_RINGTONE_TITLE;
import static android.media.RingtoneManager.EXTRA_RINGTONE_TYPE;
import static android.media.RingtoneManager.TYPE_NOTIFICATION;
import static android.provider.Settings.System.DEFAULT_NOTIFICATION_URI;
import static android.widget.Toast.LENGTH_SHORT;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.PREF_BT_ENABLE;
@@ -400,10 +402,15 @@ public class SettingsFragment extends PreferenceFragmentCompat
} else {
// The user chose a ringtone other than the default
Ringtone r = RingtoneManager.getRingtone(getContext(), uri);
String name = r.getTitle(getContext());
s.putBoolean(PREF_NOTIFY_SOUND, true);
s.put(PREF_NOTIFY_RINGTONE_NAME, name);
s.put(PREF_NOTIFY_RINGTONE_URI, uri.toString());
if (r == null) {
Toast.makeText(getContext(), R.string.cannot_load_ringtone,
LENGTH_SHORT).show();
} else {
String name = r.getTitle(getContext());
s.putBoolean(PREF_NOTIFY_SOUND, true);
s.put(PREF_NOTIFY_RINGTONE_NAME, name);
s.put(PREF_NOTIFY_RINGTONE_URI, uri.toString());
}
}
storeSettings(s);
}

View File

@@ -4,9 +4,6 @@ import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy;
import android.os.StrictMode.VmPolicy;
import android.support.v7.preference.PreferenceManager;
import android.transition.Fade;
@@ -23,8 +20,6 @@ import java.util.logging.Logger;
import javax.inject.Inject;
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
import static org.briarproject.briar.android.TestingConstants.DEFAULT_LOG_LEVEL;
import static org.briarproject.briar.android.TestingConstants.TESTING;
public class SplashScreenActivity extends BaseActivity {
@@ -36,11 +31,6 @@ public class SplashScreenActivity extends BaseActivity {
@Inject
protected AndroidExecutor androidExecutor;
public SplashScreenActivity() {
Logger.getLogger("").setLevel(DEFAULT_LOG_LEVEL);
enableStrictMode();
}
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
@@ -81,19 +71,6 @@ public class SplashScreenActivity extends BaseActivity {
}
}
private void enableStrictMode() {
if (TESTING) {
ThreadPolicy.Builder threadPolicy = new ThreadPolicy.Builder();
threadPolicy.detectAll();
threadPolicy.penaltyLog();
StrictMode.setThreadPolicy(threadPolicy.build());
VmPolicy.Builder vmPolicy = new VmPolicy.Builder();
vmPolicy.detectAll();
vmPolicy.penaltyLog();
StrictMode.setVmPolicy(vmPolicy.build());
}
}
private void setPreferencesDefaults() {
androidExecutor.runOnBackgroundThread(new Runnable() {
@Override

View File

@@ -3,7 +3,9 @@ package org.briarproject.briar.android.threaded;
import android.support.annotation.UiThread;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.MessageTree;
import org.briarproject.briar.api.client.MessageTree.MessageNode;
import org.briarproject.briar.client.MessageTreeImpl;
import java.util.ArrayList;
@@ -13,8 +15,7 @@ import java.util.List;
@UiThread
@NotNullByDefault
public class NestedTreeList<T extends MessageTree.MessageNode>
implements Iterable<T> {
public class NestedTreeList<T extends MessageNode> implements Iterable<T> {
private final MessageTree<T> tree = new MessageTreeImpl<>();
private List<T> depthFirstCollection = new ArrayList<>();
@@ -38,14 +39,14 @@ public class NestedTreeList<T extends MessageTree.MessageNode>
return depthFirstCollection.get(index);
}
public int indexOf(T elem) {
return depthFirstCollection.indexOf(elem);
}
public int size() {
return depthFirstCollection.size();
}
public boolean contains(MessageId m) {
return tree.contains(m);
}
@Override
public Iterator<T> iterator() {
return depthFirstCollection.iterator();

View File

@@ -1,6 +1,5 @@
package org.briarproject.briar.android.threaded;
import android.os.Handler;
import android.support.annotation.UiThread;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@@ -28,7 +27,6 @@ public class ThreadItemAdapter<I extends ThreadItem>
protected final NestedTreeList<I> items = new NestedTreeList<>();
private final ThreadItemListener<I> listener;
private final LinearLayoutManager layoutManager;
private final Handler handler = new Handler();
private volatile int revision = 0;
@@ -104,6 +102,10 @@ public class ThreadItemAdapter<I extends ThreadItem>
return NO_POSITION; // Not found
}
boolean contains(MessageId m) {
return items.contains(m);
}
/**
* Highlights the item with the given {@link MessageId}
* and disables the highlight for a previously highlighted item, if any.
@@ -184,6 +186,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
}
static class UnreadCount {
final int top, bottom;
private UnreadCount(int top, int bottom) {
@@ -193,6 +196,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
}
public interface ThreadItemListener<I> {
void onUnreadItemVisible(I item);
void onReplyClick(I item);

View File

@@ -16,6 +16,7 @@ public class ThreadItemListImpl<I extends ThreadItem> extends ArrayList<I>
return bottomVisibleItemId;
}
@Override
public void setFirstVisibleId(@Nullable MessageId bottomVisibleItemId) {
this.bottomVisibleItemId = bottomVisibleItemId;
}

View File

@@ -32,7 +32,6 @@ import org.briarproject.briar.android.view.TextInputView;
import org.briarproject.briar.android.view.TextInputView.TextInputListener;
import org.briarproject.briar.android.view.UnreadMessageButton;
import org.briarproject.briar.api.client.NamedGroup;
import org.briarproject.briar.api.client.PostHeader;
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
import java.util.Collection;
@@ -49,9 +48,9 @@ import static org.briarproject.briar.android.threaded.ThreadItemAdapter.UnreadCo
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadItemAdapter<I>, I extends ThreadItem, H extends PostHeader>
public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadItem, A extends ThreadItemAdapter<I>>
extends BriarActivity
implements ThreadListListener<H>, TextInputListener, SharingListener,
implements ThreadListListener<I>, TextInputListener, SharingListener,
ThreadItemListener<I>, ThreadListDataSource {
protected static final String KEY_REPLY_ID = "replyId";
@@ -68,7 +67,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
@Nullable
private MessageId replyId;
protected abstract ThreadListController<G, I, H> getController();
protected abstract ThreadListController<G, I> getController();
@Inject
protected SharingController sharingController;
@@ -190,8 +189,8 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
if (items.isEmpty()) {
list.showData();
} else {
initList(items);
updateTextInput(replyId);
displayItems(items);
updateTextInput();
}
} else {
LOG.info("Concurrent update, reloading");
@@ -206,7 +205,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
});
}
private void initList(final ThreadItemList<I> items) {
private void displayItems(final ThreadItemList<I> items) {
adapter.setItems(items);
MessageId messageId = items.getFirstVisibleItemId();
if (messageId != null)
@@ -253,9 +252,8 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
ThreadItem replyItem = adapter.getHighlightedItem();
if (replyItem != null) {
outState.putByteArray(KEY_REPLY_ID, replyItem.getId().getBytes());
if (replyId != null) {
outState.putByteArray(KEY_REPLY_ID, replyId.getBytes());
}
}
@@ -273,7 +271,9 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
@Override
public void onBackPressed() {
if (adapter.getHighlightedItem() != null) {
updateTextInput(null);
textInput.setText("");
replyId = null;
updateTextInput();
} else {
super.onBackPressed();
}
@@ -289,7 +289,8 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
@Override
public void onReplyClick(final I item) {
updateTextInput(item.getId());
replyId = item.getId();
updateTextInput();
if (textInput.isKeyboardOpen()) {
scrollToItemAtTop(item);
} else {
@@ -339,15 +340,15 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
snackbar.show();
}
private void updateTextInput(@Nullable MessageId replyItemId) {
if (replyItemId != null) {
private void updateTextInput() {
if (replyId != null) {
textInput.setHint(R.string.forum_message_reply_hint);
textInput.requestFocus();
textInput.showSoftKeyboard();
} else {
textInput.setHint(R.string.forum_new_message_hint);
}
adapter.setHighlightedItem(replyItemId);
adapter.setHighlightedItem(replyId);
}
@Override
@@ -374,25 +375,15 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
getController().createAndStoreMessage(text, replyItem, handler);
textInput.hideSoftKeyboard();
textInput.setText("");
updateTextInput(null);
replyId = null;
updateTextInput();
}
protected abstract int getMaxBodyLength();
@Override
public void onHeaderReceived(H header) {
getController().loadItem(header,
new UiResultExceptionHandler<I, DbException>(this) {
@Override
public void onResultUi(final I result) {
addItem(result, false);
}
@Override
public void onExceptionUi(DbException exception) {
handleDbException(exception);
}
});
public void onItemReceived(I item) {
addItem(item, false);
}
@Override
@@ -400,8 +391,15 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
supportFinishAfterTransition();
}
protected void addItem(I item, boolean isLocal) {
private void addItem(I item, boolean isLocal) {
adapter.incrementRevision();
MessageId parent = item.getParentId();
if (parent != null && !adapter.contains(parent)) {
// We've incremented the adapter's revision, so the item will be
// loaded when its parent has been loaded
LOG.info("Ignoring item with missing parent");
return;
}
adapter.add(item);
if (isLocal) {

View File

@@ -12,14 +12,13 @@ import org.briarproject.briar.android.controller.ActivityLifecycleController;
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.api.client.NamedGroup;
import org.briarproject.briar.api.client.PostHeader;
import java.util.Collection;
import javax.annotation.Nullable;
@NotNullByDefault
public interface ThreadListController<G extends NamedGroup, I extends ThreadItem, H extends PostHeader>
public interface ThreadListController<G extends NamedGroup, I extends ThreadItem>
extends ActivityLifecycleController {
void setGroupId(GroupId groupId);
@@ -29,9 +28,8 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
void loadSharingContacts(
ResultExceptionHandler<Collection<ContactId>, DbException> handler);
void loadItem(H header, ResultExceptionHandler<I, DbException> handler);
void loadItems(ResultExceptionHandler<ThreadItemList<I>, DbException> handler);
void loadItems(
ResultExceptionHandler<ThreadItemList<I>, DbException> handler);
void markItemRead(I item);
@@ -42,9 +40,10 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
void deleteNamedGroup(ExceptionHandler<DbException> handler);
interface ThreadListListener<H> extends ThreadListDataSource {
interface ThreadListListener<I> extends ThreadListDataSource {
@UiThread
void onHeaderReceived(H header);
void onItemReceived(I item);
@UiThread
void onGroupRemoved();

View File

@@ -39,9 +39,9 @@ import static java.util.logging.Level.WARNING;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends ThreadItem, H extends PostHeader, M extends ThreadedMessage, L extends ThreadListListener<H>>
public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends ThreadItem, H extends PostHeader, M extends ThreadedMessage, L extends ThreadListListener<I>>
extends DbControllerImpl
implements ThreadListController<G, I, H>, EventListener {
implements ThreadListController<G, I>, EventListener {
private static final Logger LOG =
Logger.getLogger(ThreadListControllerImpl.class.getName());
@@ -203,35 +203,6 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
@DatabaseExecutor
protected abstract String loadMessageBody(H header) throws DbException;
@Override
public void loadItem(final H header,
final ResultExceptionHandler<I, DbException> handler) {
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
long now = System.currentTimeMillis();
String body;
if (!bodyCache.containsKey(header.getId())) {
body = loadMessageBody(header);
bodyCache.put(header.getId(), body);
} else {
body = bodyCache.get(header.getId());
}
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Loading item took " + duration + " ms");
I item = buildItem(header, body);
handler.onResult(item);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
handler.onException(e);
}
}
});
}
@Override
public void markItemRead(I item) {
markItemsRead(Collections.singletonList(item));

View File

@@ -0,0 +1,38 @@
package org.briarproject.briar.android.util;
import android.content.Context;
import android.os.Build;
import android.support.annotation.ColorRes;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.NotificationCompat;
import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
import static android.support.v4.app.NotificationCompat.VISIBILITY_PRIVATE;
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
public class BriarNotificationBuilder extends NotificationCompat.Builder {
public BriarNotificationBuilder(Context context) {
super(context);
setAutoCancel(true);
}
public BriarNotificationBuilder setColorRes(@ColorRes int res) {
setColor(ContextCompat.getColor(mContext, res));
return this;
}
public BriarNotificationBuilder setLockscreenVisibility(String category,
boolean show) {
if (Build.VERSION.SDK_INT >= 21) {
setCategory(category);
if (show)
setVisibility(VISIBILITY_PRIVATE);
else
setVisibility(VISIBILITY_SECRET);
}
return this;
}
}

View File

@@ -104,8 +104,9 @@ public class EmojiPageView extends FrameLayout {
emojiSize + 2 * pad));
view = emojiView;
}
String emoji = model.getEmoji()[position];
view.setEmoji(emoji);
view.setEmoji(model.getEmoji()[position]);
return view;
}
}

View File

@@ -32,7 +32,7 @@ public class RecentEmojiPageModel implements EmojiPageModel {
private static final Logger LOG =
Logger.getLogger(RecentEmojiPageModel.class.getName());
private static final String EMOJI_LRU_PREFERENCE = "pref_emoji_recent";
private static final String EMOJI_LRU_PREFERENCE = "pref_emoji_recent2";
private static final int EMOJI_LRU_SIZE = 50;
private final LinkedHashSet<String> recentlyUsed; // UI thread
@@ -98,12 +98,12 @@ public class RecentEmojiPageModel implements EmojiPageModel {
}
private String serialize(LinkedHashSet<String> emojis) {
return StringUtils.join(emojis, ";");
return StringUtils.join(emojis, "\t");
}
private LinkedHashSet<String> deserialize(@Nullable String serialized) {
if (serialized == null) return new LinkedHashSet<>();
String[] list = serialized.split(";");
String[] list = serialized.split("\t");
LinkedHashSet<String> result = new LinkedHashSet<>(list.length);
Collections.addAll(result, list);
return result;

View File

@@ -30,7 +30,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/expiryWarningClose"
android:text="@string/expiry_warning"
android:text="@plurals/expiry_warning"
android:textColor="@color/briar_text_primary_inverse"
android:textSize="@dimen/text_size_small"/>

View File

@@ -1,109 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/margin_activity_vertical"
android:paddingEnd="@dimen/margin_activity_horizontal"
android:paddingRight="@dimen/margin_activity_horizontal"
android:paddingStart="@dimen/margin_activity_horizontal"
android:paddingLeft="@dimen/margin_activity_horizontal"
android:paddingTop="@dimen/margin_activity_vertical">
<TextView
android:id="@+id/connectedView"
style="@style/BriarTextTitle"
android:textSize="@dimen/text_size_large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/connected_to_contact"
android:padding="@dimen/margin_medium"
android:layout_centerHorizontal="true"
android:drawableLeft="@drawable/navigation_accept"
android:drawableStart="@drawable/navigation_accept"
android:gravity="center_vertical"/>
<TextView
android:id="@+id/yourConfirmationCodeView"
style="@style/BriarTextBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/your_confirmation_code"
android:padding="@dimen/margin_medium"
android:layout_below="@+id/connectedView"
android:layout_centerHorizontal="true"/>
<TextView
android:id="@+id/codeView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/margin_medium"
android:textSize="50sp"
android:textColor="@color/briar_text_secondary"
android:layout_below="@+id/yourConfirmationCodeView"
android:layout_centerHorizontal="true"
tools:text="1337"/>
<TextView
android:id="@+id/waitingView"
style="@style/BriarTextBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/searching_format"
android:layout_gravity="center_horizontal"
android:padding="@dimen/margin_medium"
android:layout_below="@+id/codeView"
android:layout_centerHorizontal="true"
android:visibility="gone"
android:gravity="center_horizontal"/>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
android:layout_below="@+id/waitingView"
android:layout_centerHorizontal="true"
android:visibility="gone"/>
<TextView
android:id="@+id/enterCodeTextView"
style="@style/BriarTextBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/enter_confirmation_code"
android:layout_gravity="center_horizontal"
android:padding="@dimen/margin_medium"
android:layout_below="@+id/codeView"
android:layout_centerHorizontal="true"/>
<include
android:id="@+id/codeEntryView"
layout="@layout/view_code_entry"
android:layout_below="@+id/enterCodeTextView"
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_medium"/>
<Button
android:id="@+id/continueButton"
style="@style/BriarButton.Default"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/continue_button"
android:layout_gravity="center_horizontal"
android:enabled="false"
android:layout_below="@+id/codeEntryView"
android:layout_centerHorizontal="true"
android:layout_margin="@dimen/margin_medium"/>
</RelativeLayout>
</ScrollView>

View File

@@ -1,96 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/margin_activity_vertical"
android:paddingEnd="@dimen/margin_activity_horizontal"
android:paddingRight="@dimen/margin_activity_horizontal"
android:paddingStart="@dimen/margin_activity_horizontal"
android:paddingLeft="@dimen/margin_activity_horizontal"
android:paddingTop="@dimen/margin_activity_vertical">
<TextView
android:id="@+id/yourCodeView"
style="@style/BriarTextBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/your_invitation_code"
android:layout_marginTop="@dimen/margin_medium"
android:layout_centerHorizontal="true"/>
<TextView
android:id="@+id/codeView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_medium"
android:textSize="50sp"
android:textColor="@color/briar_text_secondary"
android:layout_below="@+id/yourCodeView"
android:layout_centerHorizontal="true"
tools:text="1337"/>
<TextView
android:id="@+id/waitingView"
style="@style/BriarTextBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/searching_format"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/margin_medium"
android:layout_below="@+id/codeView"
android:layout_centerHorizontal="true"
android:visibility="gone"
android:gravity="center_horizontal"/>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_medium"
android:indeterminate="true"
android:layout_below="@+id/waitingView"
android:layout_centerHorizontal="true"
android:visibility="gone"/>
<TextView
android:id="@+id/enterCodeTextView"
style="@style/BriarTextBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/enter_invitation_code"
android:layout_gravity="center_horizontal"
android:padding="@dimen/margin_medium"
android:layout_below="@+id/codeView"
android:layout_centerHorizontal="true"/>
<include
android:id="@+id/codeEntryView"
layout="@layout/view_code_entry"
android:layout_below="@+id/enterCodeTextView"
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_medium"/>
<Button
android:id="@+id/continueButton"
style="@style/BriarButton.Default"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/continue_button"
android:layout_gravity="center_horizontal"
android:enabled="false"
android:layout_below="@+id/codeEntryView"
android:layout_centerHorizontal="true"
android:layout_margin="@dimen/margin_medium"/>
</RelativeLayout>
</ScrollView>

View File

@@ -1,60 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/margin_activity_vertical"
android:paddingEnd="@dimen/margin_activity_horizontal"
android:paddingLeft="@dimen/margin_activity_horizontal"
android:paddingRight="@dimen/margin_activity_horizontal"
android:paddingStart="@dimen/margin_activity_horizontal"
android:paddingTop="@dimen/margin_activity_vertical">
<TextView
style="@style/BriarTextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/your_nickname"
android:visibility="gone"/>
<Spinner
android:id="@+id/spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/border_spinner"
android:layout_marginTop="@dimen/margin_medium"
android:spinnerMode="dropdown"
android:visibility="gone"/>
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_xlarge"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="@drawable/bluetooth"/>
<TextView
style="@style/BriarTextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_xlarge"
android:text="@string/face_to_face"/>
<Button
android:id="@+id/continueButton"
style="@style/BriarButton.Default"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/margin_medium"
android:text="@string/continue_button"/>
</LinearLayout>
</ScrollView>

View File

@@ -1,43 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/margin_activity_vertical"
android:paddingEnd="@dimen/margin_activity_horizontal"
android:paddingStart="@dimen/margin_activity_horizontal"
android:paddingTop="@dimen/margin_activity_vertical">
<TextView
android:id="@+id/errorTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/connection_failed"
android:layout_gravity="center_horizontal"
android:textSize="@dimen/text_size_large"
android:textColor="@color/briar_text_primary"
android:drawableStart="@drawable/alerts_and_states_error"
android:drawableLeft="@drawable/alerts_and_states_error"
android:gravity="center_vertical"
android:padding="@dimen/margin_medium"/>
<TextView
android:id="@+id/explanationTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/interfering"
android:textColor="@color/briar_text_primary"
android:layout_gravity="center_horizontal"
android:padding="@dimen/margin_medium"/>
<Button
android:id="@+id/tryAgainButton"
style="@style/BriarButton.Default"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/try_again_button"
android:layout_gravity="center_horizontal"
android:layout_margin="@dimen/margin_medium"/>
</LinearLayout>

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<EditText
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/codeEntryView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="number"
android:layout_gravity="center_horizontal"
android:textSize="@dimen/text_size_xlarge"
android:ems="4"
android:maxLines="1"
android:maxLength="6"
android:layout_margin="@dimen/margin_medium"
android:imeOptions="actionGo"
tools:text="123456"/>

View File

@@ -3,18 +3,18 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_group_member_list"
android:icon="@drawable/ic_group_white"
android:title="@string/groups_member_list"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_group_invite"
android:icon="@drawable/social_share_white"
android:title="@string/groups_invite_members"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_group_member_list"
android:icon="@drawable/ic_group_white"
android:title="@string/groups_member_list"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_group_reveal"
android:icon="@drawable/ic_visibility_white"

View File

@@ -2,9 +2,9 @@
<resources>
<!--Setup-->
<string name="setup_title">Briar einrichten</string>
<string name="setup_explanation">Dein Briar-Konto wird verschlüsselt auf Deinem Gerät gespeichert und nicht mit der \"Cloud\" synchronisiert. Wenn Du Briar deinstallierst oder Dein Passwort vergisst, können das Konto und Deine Daten nicht wiederhergestellt werden.</string>
<string name="choose_nickname">Wähle Deinen Benutzernamen</string>
<string name="choose_password">Wähle Dein Passwort</string>
<string name="setup_explanation">Dein Briar-Konto wird verschlüsselt auf deinem Gerät gespeichert und nicht mit der \"Cloud\" synchronisiert. Wenn du Briar deinstallierst oder dein Passwort vergisst, können das Konto und deine Daten nicht wiederhergestellt werden.</string>
<string name="choose_nickname">Wähle deinen Benutzernamen</string>
<string name="choose_password">Wähle dein Passwort</string>
<string name="confirm_password">Passwort bestätigen</string>
<string name="name_too_long">Name zu lang</string>
<string name="password_too_weak">Passwort zu schwach</string>
@@ -22,7 +22,11 @@
<string name="startup_failed_activity_title">Fehler beim Starten von Briar</string>
<string name="startup_failed_db_error">Deine Briar-Datenbank ist korrupt. Briar-Konto, Daten und alle Verbindungen zu Kontakten können nicht mehr wiederhergestellt werden. Deinstalliere Briar und erstelle nach Installation der aktuellen Briar-Version ein neues Konto.</string>
<string name="startup_failed_service_error">Briar konnte ein benötigtes Plugin nicht starten. Normalerweise kann das Problem durch eine Neuinstallation von Briar gelöst werden. Eine Neuinstallation führt jedoch zum Verlust des Kontos und aller dazugehörigen Daten, da Briar deine Daten nicht auf zentralen Servern speichert</string>
<string name="expiry_warning">Diese Version von Briar ist nicht mehr aktuell.\nBitte installiere eine neuere Version.</string>
<plurals name="expiry_warning">
<item quantity="one">Dies ist eine Beta-Version von Briar. Dein Konto läuft in %d Tag ab und kann nicht verlängert werden.</item>
<item quantity="other">Dies ist eine Beta-Version von Briar. Dein Konto läuft in %d Tagen ab und kann nicht verlängert werden.</item>
</plurals>
<string name="expiry_date_reached">Diese Software ist abgelaufen.\nDanke dass Du Briar getestet hast!</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Navigationsleiste öffnen</string>
<string name="nav_drawer_close_description">Navigationsleiste schliessen</string>
@@ -87,7 +91,7 @@
<!--Adding Contacts-->
<string name="add_contact_title">Kontakt hinzufügen</string>
<string name="your_nickname">Wähle die zu verwendende Identität:</string>
<string name="face_to_face">Um einen neuen Kontakt hinzufügen zu können, ist eine reale Begegnung erforderlich.\n\nDadurch wird eine betrügerische Impersonation und der unautorisierte Zugriff auf die Kommunikation verhindert.</string>
<string name="face_to_face">Um einen neuen Kontakt hinzuzufügen, ist es notwendig, dass sich beide Kontakte an einem Ort treffen.\n\nDadurch wird betrügerische Identitätsvortäuschung und unautorisierter Kommunikationszugriff verhindert.</string>
<string name="continue_button">Weiter</string>
<string name="your_invitation_code">Dein Einladungs-Code ist</string>
<string name="enter_invitation_code">Bitte gib den Einladungs-Code Deines Kontakts an:</string>
@@ -117,6 +121,7 @@
<string name="introduction_onboarding_text">Du kannst Deine Kontakte untereinander bekannt machen. So können sie sich über Briar verbinden, ohne sich persönlich treffen zu müssen.</string>
<string name="introduction_activity_title">Kontakt auswählen</string>
<string name="introduction_message_title">Kontakte untereinander bekannt machen</string>
<string name="introduction_message_hint">Nachricht hinzufügen (optional)</string>
<string name="introduction_button">Kontaktempfehlung abgeben</string>
<string name="introduction_sent">Deine Kontaktempfehlung wurde verschickt</string>
<string name="introduction_error">Es gab einen Fehler beim Versuch, die Kontaktempfehlung zu verschicken</string>
@@ -147,6 +152,7 @@
<string name="groups_create_group_title">Private Gruppe erstellen</string>
<string name="groups_create_group_button">Gruppe erstellen</string>
<string name="groups_create_group_invitation_button">Einladung schicken</string>
<string name="groups_create_group_hint">Wähle einen Namen für deine private Gruppe</string>
<string name="groups_invitation_sent">Gruppeneinladung versendet</string>
<string name="groups_message_sent">Nachricht gesendet</string>
<string name="groups_member_list">Mitglieder</string>
@@ -188,6 +194,8 @@
<string name="groups_reveal_invisible">Beziehung zum Kontakt ist für diese Gruppe nicht sichtbar</string>
<!--Forums-->
<string name="no_forums">Du hast noch keine Foren.\n\nWarum erstellst du nicht einfach selbst ein neues Forum, indem du auf das \"+\"-Symbol am oberen Bildschirmrand tippst?\n\nDu kannst auch deine Kontakte auffordern, Foren mit dir zu teilen.</string>
<string name="create_forum_title">Forum erstellen</string>
<string name="choose_forum_hint">Wähle einen Namen für Dein Forum</string>
<string name="create_forum_button">Forum erstellen</string>
<string name="forum_created_toast">Forum wurde erstellt</string>
<string name="no_forum_posts">Dieses Forum ist leer.\n\nBenutze das Stift-Icon am oberen Bildschirmrand um den ersten Beitrag zu verfassen.\n\nFühlst du dich einsam hier? Dann teile das Forum mit weiteren Kontakten!</string>
@@ -211,6 +219,7 @@
<string name="activity_share_toolbar_header">Kontakte auswählen</string>
<string name="no_contacts_selector">Du scheinst hier neu zu sein und noch keine Kontakte zu haben.\n\nBitte komm zurück, wenn du deinen ersten Kontakt hinzugefügt hast.</string>
<string name="forum_shared_snackbar">Forum mit gewählten Kontakten geteilt</string>
<string name="forum_share_message">Nachricht hinzufügen (optional)</string>
<string name="forum_share_error">Es gab einen Fehler beim Versuch, dieses Forum zu teilen.</string>
<string name="forum_invitation_received">%1$s hat das Forum \"%2$s\" mit dir geteilt.</string>
<string name="forum_invitation_sent">Du hast das Forum \"%1$s\" mit %2$s geteilt.</string>
@@ -241,12 +250,12 @@
<string name="blogs_blog_post_created">Blogbeitrag erstellt</string>
<string name="blogs_blog_post_received">Neuen Blogbeitrag empfangen</string>
<string name="blogs_blog_post_scroll_to">Scrolle zu</string>
<string name="blogs_feed_empty_state">Dies ist die globale Blog-Zeitleiste.\n\nOffensichtlich hat noch niemand etwas veröffentlicht.\n\nSei die oder der erste und tipp auf das Stift-Icon, um einen neuen Blogbeitrag zu verfassen.</string>
<string name="blogs_personal_blog">%ss persönliches Blog</string>
<string name="blogs_feed_empty_state">Dies ist die globale Blog-Zeitleiste.\n\nOffensichtlich hat noch niemand etwas veröffentlicht.\n\nSei die oder der Erste und tippe auf das Stift-Icon, um einen neuen Blogbeitrag zu verfassen.</string>
<string name="blogs_remove_blog">Blog entfernen</string>
<string name="blogs_remove_blog_dialog_message">Bist Du sicher, dass Du diesen Blog und alle dazugehörigen Beiträge löschen möchtest?\nBeachte, dass dies nicht den Blog auf Geräten anderer Leute löscht.</string>
<string name="blogs_remove_blog_ok">Blog entfernen</string>
<string name="blogs_blog_removed">Blog wurde entfernt</string>
<string name="blogs_reblog_comment_hint">Kommentar hinzufügen (optional)</string>
<string name="blogs_reblog_button">Rebloggen</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Blog teilen</string>
@@ -257,8 +266,8 @@
<string name="blogs_sharing_response_declined_sent">Du hast die Blogeinladung von %s abgelehnt.</string>
<string name="blogs_sharing_response_accepted_received">%s hat die Blogeinladung akzeptiert.</string>
<string name="blogs_sharing_response_declined_received">%s hat die Blogeinladung abgelehnt.</string>
<string name="blogs_sharing_invitation_received">%1$s hat das persönliche Blog von %2$s mit dir geteilt.</string>
<string name="blogs_sharing_invitation_sent">Du hast das persönliche Blog von %1$s mit %2$s geteilt.</string>
<string name="blogs_sharing_invitation_received">%1$shat den Blog \"%2$s\" mit Dir geteilt.</string>
<string name="blogs_sharing_invitation_sent">Du teilst den Blog \"%1$s\" mit %2$s.</string>
<string name="blogs_sharing_invitations_title">Blogeinladungen</string>
<string name="blogs_sharing_joined_toast">Blog abonniert</string>
<string name="blogs_sharing_declined_toast">Blogeinladung abgelehnt</string>
@@ -276,7 +285,7 @@
<string name="blogs_rss_remove_feed_dialog_message">Soll der Feed mit allen Posts wirklich gelöscht werden?\nGeteilte Posts werden dabei nicht von anderen Geräten gelöscht.</string>
<string name="blogs_rss_remove_feed_ok">Feed entfernen</string>
<string name="blogs_rss_feeds_manage_delete_error">Der Feed konnte nicht gelöscht werden!</string>
<string name="blogs_rss_feeds_manage_empty_state">Du hast bisher noch keine RSS Feeds importiert. Tippe auf das \"+\"-Icon am oberen Bildschirmrand um einen neuen Feed hinzuzufügen.</string>
<string name="blogs_rss_feeds_manage_empty_state">Du hast bisher noch keine RSS-Feeds importiert. Tippe auf das \"+\"-Icon am oberen Bildschirmrand, um einen neuen Feed hinzuzufügen.</string>
<string name="blogs_rss_feeds_manage_error">Es gab ein Problem beim Laden deiner Feeds. Bitte versuche es später erneut.</string>
<!--Settings Network-->
<string name="network_settings_title">Netzwerke</string>
@@ -311,7 +320,17 @@
<string name="uninstall_setting_summary">Diese Aktion benötigt manuelle Bestätigung im Falle eines Panik-Ereignisses</string>
<!--Settings Notifications-->
<string name="notification_settings_title">Benachrichtigungen</string>
<string name="notify_private_messages_setting_title">Private Nachrichten</string>
<string name="notify_private_messages_setting_summary">Zeige Benachrichtigungen für private Nachrichten</string>
<string name="notify_group_messages_setting_title">Gruppennachrichten</string>
<string name="notify_group_messages_setting_summary">Benachrichtigungen für Gruppennachrichten anzeigen</string>
<string name="notify_forum_posts_setting_title">Forenbeiträge</string>
<string name="notify_forum_posts_setting_summary">Benachrichtigungen für Forenbeiträge anzeigen</string>
<string name="notify_blog_posts_setting_title">Blogbeiträge</string>
<string name="notify_blog_posts_setting_summary">Benachrichtigungen für Blogbeiträge anzeigen</string>
<string name="notify_vibration_setting">Vibration</string>
<string name="notify_lock_screen_setting_title">Sperrbildschirm</string>
<string name="notify_lock_screen_setting_summary">Zeigt Benachrichtigungen auf dem Sperrbildschirm an</string>
<string name="notify_sound_setting">Tonsignal</string>
<string name="notify_sound_setting_default">Standardklingelton</string>
<string name="notify_sound_setting_disabled">Keine</string>
@@ -327,7 +346,7 @@
<!--Crash Reporter-->
<string name="crash_report_title">Briar-Absturzbericht</string>
<string name="briar_crashed">Es tut uns leid, Briar ist abgestürzt.</string>
<string name="not_your_fault">Das ist nicht Deine Schuld.</string>
<string name="not_your_fault">Das ist nicht deine Schuld.</string>
<string name="please_send_report">Bitte hilf uns, Briar zu verbessern, indem Du einen Absturzbericht sendest.</string>
<string name="report_is_encrypted">Wir versprechen, dass der Bericht verschlüsselt ist und über eine sichere Verbindung geschickt wird.</string>
<string name="feedback_title">Feedback</string>
@@ -343,4 +362,6 @@
<!--Sign Out-->
<string name="progress_title_logout">Von Briar abmelden ...</string>
<!--Screen Filters & Tapjacking-->
<string name="screen_filter_title">Bildschirmüberlagerung erkannt</string>
<string name="screen_filter_body">Eine andere App überlagert Briar. Um deine Sicherheit zu gewährleisten, reagiert Briar in diesem Fall nicht auf deine Eingaben.\n\nBeende deswegen die folgenden Apps während der Verwendung von Briar:\n\n%1$s</string>
</resources>

View File

@@ -22,7 +22,11 @@
<string name="startup_failed_activity_title">Fallo al iniciar Briar</string>
<string name="startup_failed_db_error">Por alguna razón, la base de datos de Briar ha sufrido daños irreparables. Tu cuenta, tus datos y todos tus contactos se han perdido. Desafortunadamente, tendrás que reinstalar Briar y registrar una nueva cuenta.</string>
<string name="startup_failed_service_error">Briar no pudo iniciar un complemento necesario. Reinstalar Briar suele solucionar el problema. Sin embargo, ten en cuenta que perderás tu cuenta y todos los datos asociados ya que Briar no almacena esta información en ningún servidor central.</string>
<string name="expiry_warning">Esta versión ha caducado.\nInstala una más reciente, por favor.</string>
<plurals name="expiry_warning">
<item quantity="one">Esta es una versión preliminar de Briar. Tu cuenta caducará en %d día y no podrá renovarse.</item>
<item quantity="other">Esta es una versión preliminar de Briar. Tu cuenta caducará en %d días y no podrá renovarse.</item>
</plurals>
<string name="expiry_date_reached">Esta versión ha caducado.\n¡Gracias por probarla!</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Abrir el panel de navegación</string>
<string name="nav_drawer_close_description">Cierra el panel de navegación</string>
@@ -76,7 +80,7 @@
<string name="text_too_long">El texto es demasiado largo</string>
<string name="show_onboarding">Mostrar diálogo de ayuda</string>
<!--Contacts and Private Conversations-->
<string name="no_contacts">Parece que eres nuevo por aquí y no tienes aún contactos.\n\nPulsa el signo + en la parte superior y sigue las instrucciones para añadir amigos a tu lista.\n\nPor favor, recuerda: sólo puedes añadir nuevos contactos cara a cara para evitar que nadie suplante tu identidad o lea tus mensajes en el futuro. </string>
<string name="no_contacts">Parece que eres nuevo por aquí y no tienes aún contactos.\n\nPulsa el signo + en la parte superior y sigue las instrucciones para añadir amigos a tu lista.\n\nPor favor, recuerda: sólo puedes añadir nuevos contactos cara a cara para evitar que nadie suplante tu identidad o lea tus mensajes en el futuro. </string>
<string name="date_no_private_messages">Sin mensajes.</string>
<string name="no_private_messages">Esta es la vista de conversación.\n\nParece que aún no hay ninguna.\n\nPulsa el campo de texto en la parte inferior para empezar la conversación.</string>
<string name="message_hint">Escribe un mensaje</string>
@@ -118,7 +122,7 @@
<string name="introduction_activity_title">Seleccionar contacto</string>
<string name="introduction_message_title">Presentar a dos contactos</string>
<string name="introduction_message_hint">Añade un mensaje (opcional)</string>
<string name="introduction_button">Realizar la presentación</string>
<string name="introduction_button">Presentar contactos</string>
<string name="introduction_sent">Tu presentación se ha mandado.</string>
<string name="introduction_error">Ocurrió un error realizando la presentación.</string>
<string name="introduction_response_error">Error al responder a la presentación</string>
@@ -136,7 +140,7 @@
<item quantity="other">%d nuevos contactos añadido</item>
</plurals>
<!--Private Groups-->
<string name="groups_list_empty">Aún no participas en ningún grupo.\n\nCrea un grupo pulsando en el signo + arriba o pide a tus contactos que te inviten a uno de sus grupos.</string>
<string name="groups_list_empty">Aún no participas en ningún grupo.\n\nCrea un grupo pulsando en el signo + arriba o pide a tus contactos que te inviten a uno de sus grupos.</string>
<string name="groups_created_by">Creado por %s</string>
<plurals name="messages">
<item quantity="one">%d mensaje</item>
@@ -189,7 +193,9 @@
<string name="groups_reveal_visible_revealed_by_contact">Las relaciones entre los contactos son visibles al grupo (las reveló %s)</string>
<string name="groups_reveal_invisible">Las relaciones entre los contactos no son visibles al grupo</string>
<!--Forums-->
<string name="no_forums">No tienes ningún foro aún.\n\n¿Por qué no creas uno pulsando el signo + de la parte superior?\n\nTambién puedes pedirle a tus contactos que compartan foros contigo.</string>
<string name="no_forums">No tienes ningún foro aún.\n\n¿Por qué no creas uno pulsando el signo + de la parte superior?\n\nTambién puedes pedirle a tus contactos que compartan foros contigo.</string>
<string name="create_forum_title">Crear foro</string>
<string name="choose_forum_hint">Elige un nombre para el foro</string>
<string name="create_forum_button">Crear foro</string>
<string name="forum_created_toast">Foro creado</string>
<string name="no_forum_posts">Este foro está vacío.\n\nUsa el signo del lápiz de la parte superior para redactar una primera publicación.\n\n¿No te sientes un poco solo aquí? ¡Comparte este foro con más contactos!</string>
@@ -245,7 +251,6 @@
<string name="blogs_blog_post_received">Recibido nuevo artículo del blog</string>
<string name="blogs_blog_post_scroll_to">Desplazarse hasta</string>
<string name="blogs_feed_empty_state">Esta es la lista global de entradas de blogs.\n\nParece que nadie ha publicado nada todavía.\n\nSé el primero: pulsa el signo del lápiz para escribir una nueva entrada de blog.</string>
<string name="blogs_personal_blog">Blog personal de %s</string>
<string name="blogs_remove_blog">Eliminar blog</string>
<string name="blogs_remove_blog_dialog_message">¿Seguro que quieres eliminar este blog y todos sus artículos?\nTen en cuenta que no se eliminará el blog de los dispositivos de otras personas.</string>
<string name="blogs_remove_blog_ok">Eliminar blog</string>
@@ -261,8 +266,8 @@
<string name="blogs_sharing_response_declined_sent">Rechazaste la invitación al blog de %s.</string>
<string name="blogs_sharing_response_accepted_received">%s aceptó la invitación al blog.</string>
<string name="blogs_sharing_response_declined_received">%s rechazó la invitación al blog.</string>
<string name="blogs_sharing_invitation_received">%1$s ha compartido el blog personal de %2$s contigo.</string>
<string name="blogs_sharing_invitation_sent">Has compartido el blog personal de %1$s con %2$s.</string>
<string name="blogs_sharing_invitation_received">%1$s ha compartido el blog \"%2$s\" contigo.</string>
<string name="blogs_sharing_invitation_sent">Has compartido el blog \"%1$s\" con %2$s.</string>
<string name="blogs_sharing_invitations_title">Invitaciones a blogs</string>
<string name="blogs_sharing_joined_toast">Suscrito al blog</string>
<string name="blogs_sharing_declined_toast">Rechazada invitación al blog</string>
@@ -284,7 +289,7 @@
<string name="blogs_rss_feeds_manage_error">Hubo un problema cargando tus canales RSS. Por favor, prueba más tarde.</string>
<!--Settings Network-->
<string name="network_settings_title">Redes</string>
<string name="bluetooth_setting">Connectar mediante Bluetooth</string>
<string name="bluetooth_setting">Conectar mediante Bluetooth</string>
<string name="bluetooth_setting_enabled">Cuando haya contactos cerca</string>
<string name="bluetooth_setting_disabled">Solo al añadir contactos</string>
<string name="tor_network_setting">Conectar a través de Tor</string>
@@ -324,6 +329,8 @@
<string name="notify_blog_posts_setting_title">Entradas de blog</string>
<string name="notify_blog_posts_setting_summary">Notificar entradas de blog</string>
<string name="notify_vibration_setting">Vibrar</string>
<string name="notify_lock_screen_setting_title">Pantalla de bloqueo</string>
<string name="notify_lock_screen_setting_summary">Notificaciones en la pantalla de bloqueo</string>
<string name="notify_sound_setting">Sonar</string>
<string name="notify_sound_setting_default">Tono de notificación predeterminado</string>
<string name="notify_sound_setting_disabled">Ninguna</string>

View File

@@ -2,66 +2,70 @@
<resources>
<!--Setup-->
<string name="setup_title">Configuration de Briar</string>
<string name="setup_explanation">Votre compte Briar est enregistré chiffré sur votre appareil et non sur le \"cloud\". Si vous désinstallez Briar ou si vous oubliez votre mot de passe, votre compte et vos données sont irrécupérables.</string>
<string name="choose_nickname">Choisissez votre surnom</string>
<string name="choose_password">Choisissez votre mot de passe</string>
<string name="confirm_password">Confirmez votre mot de passe</string>
<string name="setup_explanation">Votre compte Briar est enregistré chiffré sur votre appareil et non sur le nuage. Si vous désinstallez Briar ou oubliez votre mot de passe, votre compte et vos données sont irrécupérables.</string>
<string name="choose_nickname">Choisir votre pseudonyme</string>
<string name="choose_password">Choisir votre mot de passe</string>
<string name="confirm_password">Confirmer votre mot de passe</string>
<string name="name_too_long">Le nom est trop long</string>
<string name="password_too_weak">Le mot de passe est trop faible</string>
<string name="passwords_do_not_match">Les mots de passes ne correspondent pas</string>
<string name="passwords_do_not_match">Les mots de passe ne correspondent pas</string>
<string name="create_account_button">Créer un compte</string>
<!--Login-->
<string name="enter_password">Tapez votre mot de passe :</string>
<string name="try_again">Mot de passe incorrecte, essayer à nouveau</string>
<string name="sign_in_button">Se connecter</string>
<string name="enter_password">Saisir votre mot de passe :</string>
<string name="try_again">Le mot de passe est erroné, ressayez</string>
<string name="sign_in_button">Connexion</string>
<string name="forgotten_password">J\'ai oublié mon mot de passe</string>
<string name="dialog_title_lost_password">Mot de passe oublié</string>
<string name="dialog_message_lost_password">Votre compte Briar est enregistré chiffré sur votre appareil et non sur le \"cloud\", par conséquent, votre mot de passe ne peut être réinitialisé. Voulez-vous supprimer votre compte et démarrer à nouveau ?\n\nAttention : vos identités, contacts et messages seront définitivement perdus.</string>
<string name="startup_failed_notification_title">Briar n\'a pas pu démarrer</string>
<string name="startup_failed_notification_text">Vous devrez peut-être réinstaller Briar.</string>
<string name="dialog_message_lost_password">Votre compte Briar est enregistré chiffré sur votre appareil et non sur le nuage et nous ne pouvons donc pas réinitialiser votre mot de passe. Voulez-vous supprimer votre compte et recommencer?\n\nAttention : vos identités, contacts et messages seront perdus irrémédiablement.</string>
<string name="startup_failed_notification_title">Impossible de démarrer Briar</string>
<string name="startup_failed_notification_text">Il vous faudra peut-être réinstaller Briar.</string>
<string name="startup_failed_activity_title">Échec de démarrage de Briar</string>
<string name="startup_failed_db_error">Pour une raison indéterminée, votre base de donnée Briar est corrompue et irrécupérable. Vos comptes, données et contacts sont perdus. Vous devez malheureusement réinstaller Briar et configurer un nouveau compte.</string>
<string name="startup_failed_service_error">Briar n\'a pas pu démarrer un module nécessaire. Réinstaller Briar résout généralement ce problème. Veuillez noter que vous perdrez votre compte et toutes les données associées puisque Briar n\'utilise pas de serveurs centralisés pour enregistrer les données.</string>
<string name="expiry_warning">Ce logiciel est arrivé à expiration.\nVeuillez installer une version plus récente.</string>
<string name="startup_failed_db_error">Pour une raison indéterminée, votre base de données Briar est corrompue sans espoir de récupération. Vos comptes, données et contacts sont perdus. Vous devez malheureusement réinstaller Briar et configurer un nouveau compte.</string>
<string name="startup_failed_service_error">Briar n\'a pas pu démarrer un greffon exigé. Réinstaller Briar résout généralement ce problème. Veuillez cependant noter que vous perdrez votre compte et toutes données relatives puisque Briar n\'utilise pas de serveurs centralisés sur lesquels enregistrer vos données.</string>
<plurals name="expiry_warning">
<item quantity="one">Ceci est une version bêta de Briar. Votre compte arrivera à expiration dans %d jour et ne peut pas être renouvelé.</item>
<item quantity="other">Ceci est une version bêta de Briar. Votre compte arrivera à expiration dans %d jours et ne peut pas être renouvelé.</item>
</plurals>
<string name="expiry_date_reached">Ce logiciel est arrivé à expiration.\nMerci de l\'avoir testé!</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Ouvrir le panneau de navigation</string>
<string name="nav_drawer_close_description">Fermer le panneau de navigation</string>
<string name="nav_drawer_open_description">Ouvrir le tiroir de navigation</string>
<string name="nav_drawer_close_description">Fermer le tiroir de navigation</string>
<string name="contact_list_button">Contacts</string>
<string name="groups_button">Groupes privés</string>
<string name="forums_button">Forums</string>
<string name="blogs_button">Blogs</string>
<string name="blogs_button">Blogues</string>
<string name="settings_button">Paramètres</string>
<string name="sign_out_button">Se déconnecter</string>
<string name="sign_out_button">Déconnexion</string>
<!--Transports-->
<string name="transport_tor">Internet</string>
<string name="transport_bt">Bluetooth</string>
<string name="transport_lan">Wifi</string>
<string name="transport_lan">Wi-Fi</string>
<!--Notifications-->
<string name="ongoing_notification_title">Connecté à Briar</string>
<string name="ongoing_notification_text">Toucher pour ouvrir Briar.</string>
<plurals name="private_message_notification_text">
<item quantity="one">Nouveau message privé.</item>
<item quantity="other">%d Nouveaux messages privés.</item>
<item quantity="one">Un nouveau message privé.</item>
<item quantity="other">%d nouveaux messages privés.</item>
</plurals>
<plurals name="group_message_notification_text">
<item quantity="one">Nouveau message de groupe.</item>
<item quantity="one">Un nouveau message de groupe.</item>
<item quantity="other">%d nouveaux messages de groupe.</item>
</plurals>
<plurals name="forum_post_notification_text">
<item quantity="one">Nouveau post de forum.</item>
<item quantity="other">%d nouveaux messages de forum.</item>
<item quantity="one">Un nouvel article de forum.</item>
<item quantity="other">%d nouveaux articles de forum.</item>
</plurals>
<plurals name="blog_post_notification_text">
<item quantity="one">Nouveau post de blog.</item>
<item quantity="other">%d Nouveaux messages de blog.</item>
<item quantity="one">Un nouveau billet de blogue.</item>
<item quantity="other">%d nouveaux billets de blogue.</item>
</plurals>
<!--Misc-->
<string name="now">Maintenant</string>
<string name="now">maintenant</string>
<string name="show">Afficher</string>
<string name="hide">Cacher</string>
<string name="ok">OK</string>
<string name="cancel">Annuler</string>
<string name="got_it">Je l\'ai</string>
<string name="got_it">Compris</string>
<string name="delete">Supprimer</string>
<string name="accept">Accepter</string>
<string name="decline">Refuser</string>
@@ -74,161 +78,163 @@
<string name="no_data">Aucune donnée</string>
<string name="ellipsis">...</string>
<string name="text_too_long">Le texte saisi est trop long</string>
<string name="show_onboarding">Afficher la boite de dialogue d\'Aide</string>
<string name="show_onboarding">Afficher la fenêtre d\'aide</string>
<!--Contacts and Private Conversations-->
<string name="no_contacts">Vous semblez être nouveau ici et n\'avez aucun contact.\n\nTouchez l\'icône + en haut et suivez les instructions pour en ajouter.\n\nRappel : vous pouvez ajouter des contacts uniquement en les rencontrant face à face, ceci pour éviter le vol d\'identité et que vos messages ne soient lus par d\'autres.</string>
<string name="no_contacts">Il semble que soyez nouveau ici, sans encore aucun contact.\n\nTouchez l\'icône + en haut et suivez les instructions pour ajouter des amis à votre liste.\n\nVeuillez ne pas oublier que vous pouvez seulement ajouter des contacts en les rencontrant directement, afin d\'éviter que quelqu\'un se fasse passer pour vous et puisse lire vos messages à l\'avenir.</string>
<string name="date_no_private_messages">Aucun message.</string>
<string name="no_private_messages">Ceci est la vue de conversation.\n\nIl semble y avoir un manque de conversation.\n\nAppuyez sur le champ de saisie en bas pour en démarrer une. </string>
<string name="message_hint">Taper le message</string>
<string name="no_private_messages">Ceci est la vue des conversations.\n\nIl semble ne pas y en avoir.\n\nIl suffit de toucher le champ de saisie, en bas, pour en démarrer une.</string>
<string name="message_hint">Rédiger le message</string>
<string name="delete_contact">Supprimer le contact</string>
<string name="dialog_title_delete_contact">Confirmer la suppression du contact</string>
<string name="dialog_message_delete_contact">Êtes-vous sûr de vouloir supprimer ce contact et tous les messages échangés avec celui-ci ?</string>
<string name="contact_deleted_toast">Contact supprimé</string>
<string name="dialog_message_delete_contact">Voulez-vous vraiment supprimer ce contact et tous les messages associés?</string>
<string name="contact_deleted_toast">Le contact a été supprimé</string>
<!--Adding Contacts-->
<string name="add_contact_title">Ajouter un contact</string>
<string name="your_nickname">Choisissez l\'identité que vous souhaitez utiliser :</string>
<string name="face_to_face">Vous devez rencontrer la personne que vous voulez ajouter comme contact.\n\nCeci pour éviter le vol d\'identité et que vos messages ne soient lus par d\'autres.</string>
<string name="your_nickname">Choisir l\'identité que vous souhaitez utiliser :</string>
<string name="face_to_face">Vous devez rencontrer la personne que vous voulez ajouter comme contact, afin d\'éviter que quelqu\'un se fasse passer pour vous et puisse lire vos messages à l\'avenir.</string>
<string name="continue_button">Continuer</string>
<string name="your_invitation_code">Votre code d\'invitation est </string>
<string name="your_invitation_code">Votre code d\'invitation est</string>
<string name="enter_invitation_code">Veuillez saisir le code d\'invitation de votre contact :</string>
<string name="searching_format">Recherche de contacts avec le code d\'invitation %06d\u2026</string>
<string name="connection_failed">Échec de connexion</string>
<string name="could_not_find_contact">Briar n\'a pas trouvé de contacts à proximité</string>
<string name="try_again_button">Essayer à nouveau</string>
<string name="could_not_find_contact">Briar n\'a pas trouvé votre contact à proximité</string>
<string name="try_again_button">Ressayer</string>
<string name="connected_to_contact">Connecté au contact</string>
<string name="calculating_confirmation_code">Calcul du code de confirmation\u2026</string>
<string name="your_confirmation_code">Votre code de confirmation est </string>
<string name="your_confirmation_code">Votre code de confirmation est</string>
<string name="enter_confirmation_code">Veuillez saisir le code de confirmation de votre contact :</string>
<string name="waiting_for_contact">Attente du contact\u2026</string>
<string name="waiting_for_contact_to_scan">En attente de scan et de la connexion du contact\u2026</string>
<string name="exchanging_contact_details">Échange des détails du contact\u2026</string>
<string name="waiting_for_contact">En attente du contact\u2026</string>
<string name="waiting_for_contact_to_scan">En attente de recherche et de connexion du contact\u2026</string>
<string name="exchanging_contact_details">Échange des renseignements de contact\u2026</string>
<string name="codes_do_not_match">Les codes ne correspondent pas</string>
<string name="interfering">Il se pourrait que quelqu\'un essaye d\'interférer avec votre connexion</string>
<string name="interfering">Cela pourrait signer que quelqu\'un tente d\'interférer avec votre connexion</string>
<string name="contact_added_toast">Contact ajouté : %s</string>
<string name="contact_already_exists">Le contact %s existe déjà</string>
<string name="contact_exchange_failed">L\'échange de contact a échoué</string>
<string name="qr_code_invalid">Le code QR n\'est pas valide</string>
<string name="contact_exchange_failed">Échec d\'échange de contacts</string>
<string name="qr_code_invalid">Le code QR est invalide</string>
<string name="connecting_to_device">Connexion à l\'appareil\u2026</string>
<string name="authenticating_with_device">Autentification avec l\'appareil\u2026</string>
<string name="connection_aborted_local">Nous avons interrompu la connexion ! Il est possible que quelqu\'un tente d\'interférer avec celle-ci</string>
<string name="connection_aborted_remote">Votre contact a interrompu la connexion ! Il est possible que quelqu\'un tente d\'interférer avec celle-ci</string>
<string name="connection_aborted_local">Nous avons interrompu la connexion! Cela pourrait signer que quelqu\'un tente d\'interférer avec votre connexion</string>
<string name="connection_aborted_remote">Votre contact a interrompu la connexion! Cela pourrait signer que quelqu\'un tente d\'interférer avec votre connexion</string>
<!--Introductions-->
<string name="introduction_onboarding_title">Introduire vos contacts</string>
<string name="introduction_onboarding_text">Vous pouvez introduire vos contacts les uns les autres, ils n\'ont donc pas besoin de se rencontrer pour se connecter avec Briar.</string>
<string name="introduction_activity_title">Sélectionner contact </string>
<string name="introduction_message_title">Introduire des contacts</string>
<string name="introduction_message_hint">Ajouter un message (optionnel)</string>
<string name="introduction_button">Effectuer l\'introduction</string>
<string name="introduction_sent">Votre admission a été envoyée.</string>
<string name="introduction_error">Une erreur s\'est produite lors de l\'admission.</string>
<string name="introduction_response_error">Erreur lors de la réponse à l\'admission</string>
<string name="introduction_request_sent">Vous avez demandé à %2$s d\'admettre %1$s.</string>
<string name="introduction_request_received">%1$s vous a demandé l\'admission de %2$s. Souhaitez-vous ajouter %2$s à votre liste de contacts ?</string>
<string name="introduction_request_exists_received">%1$s vous a demandé l\'admission de %2$s mais %2$s est déjà dans votre liste de contacts. Puisque %1$s ne le sait pas, vous pouvez tout de même répondre : </string>
<string name="introduction_request_answered_received">%1$s a demandé l\'admission de %2$s.</string>
<string name="introduction_response_accepted_sent">Vous avez accepté l\'admission de %1$s.</string>
<string name="introduction_response_declined_sent">Vous avez refusé l\'admission de %1$s.</string>
<string name="introduction_response_accepted_received">%1$s a accepté l\'admission de %2$s.</string>
<string name="introduction_response_declined_received">%1$s a refusé l\'admission de %2$s.</string>
<string name="introduction_response_declined_received_by_introducee">%1$s annonce que %2$s a refusé l\'admission.</string>
<string name="introduction_onboarding_title">Présenter vos contacts</string>
<string name="introduction_onboarding_text">Vous pouvez présenter vos contacts mutuellement, afin qu\'ils n\'aient pas à se rencontrer en personne pour se connecter les uns aux autres avec Briar.</string>
<string name="introduction_activity_title">Sélectionner un contact </string>
<string name="introduction_message_title">Présenter des contacts</string>
<string name="introduction_message_hint">Ajouter un message (facultatif)</string>
<string name="introduction_button">Faire les présentations</string>
<string name="introduction_sent">Votre présentation a été envoyée.</string>
<string name="introduction_error">Une erreur est survenue lors de la présentation.</string>
<string name="introduction_response_error">Erreur de réponse à la présentation</string>
<string name="introduction_request_sent">Vous avez demandé de présenter %1$s à %2$s.</string>
<string name="introduction_request_received">%1$s a demandé de vous présenter à %2$s. Souhaitez-vous ajouter %2$s à votre liste de contacts?</string>
<string name="introduction_request_exists_received">%1$s a demandé de vous présenter à %2$s, mais %2$s est déjà dans votre liste de contacts. Puisque %1$s pourrait ne pas le savoir, vous pouvez tout de même répondre :</string>
<string name="introduction_request_answered_received">%1$s a demandé de vous présenter à %2$s.</string>
<string name="introduction_response_accepted_sent">Vous avez accepté d\'être présenté à %1$s.</string>
<string name="introduction_response_declined_sent">Vous avez refusé d\'être présenté à %1$s.</string>
<string name="introduction_response_accepted_received">%1$s a accepté d\'être présenté à %2$s.</string>
<string name="introduction_response_declined_received">%1$s a refusé d\'être présenté à %2$s.</string>
<string name="introduction_response_declined_received_by_introducee">%1$s annonce que %2$s a refusé la présentation.</string>
<plurals name="introduction_notification_text">
<item quantity="one">Nouveau contact ajouté.</item>
<item quantity="other">%d nouveaux contacts ajoutés.</item>
<item quantity="one">Un nouveau contact a été ajouté.</item>
<item quantity="other">%d nouveaux contacts ont été ajoutés.</item>
</plurals>
<!--Private Groups-->
<string name="groups_list_empty">Vous n\'êtes membres d\'aucun groupe.\n\nTouchez l\'icône + en haut pour en créer un ou demandez à vos contacts de vous inviter à l\'un de leurs groupes.</string>
<string name="groups_list_empty">Vous ne participez à aucun groupe.\n\nTouchez l\'icône +, en haut, pour en créer un ou demandez à vos contacts de vous inviter dans l\'un des leurs.</string>
<string name="groups_created_by">Créé par %s</string>
<plurals name="messages">
<item quantity="one">%d message</item>
<item quantity="other">%d messages</item>
</plurals>
<string name="groups_group_is_empty">Ce groupe est vide</string>
<string name="groups_group_is_dissolved">Ce groupe a été supprimé</string>
<string name="groups_group_is_dissolved">Ce groupe a été dissous</string>
<string name="groups_remove">Supprimer</string>
<string name="groups_create_group_title">Créer un groupe privé</string>
<string name="groups_create_group_button">Créer un groupe</string>
<string name="groups_create_group_invitation_button">Envoyer invitation</string>
<string name="groups_create_group_hint">Choisissez un nom pour votre groupe privé</string>
<string name="groups_invitation_sent">Invitation envoyée au groupe</string>
<string name="groups_message_sent">Message envoyé</string>
<string name="groups_member_list">Liste de participants</string>
<string name="groups_create_group_invitation_button">Envoyer une invitation</string>
<string name="groups_create_group_hint">Choisir un nom pour votre groupe privé</string>
<string name="groups_invitation_sent">L\'invitation de groupe a été envoyée</string>
<string name="groups_message_sent">Le message a été envoyé</string>
<string name="groups_member_list">Liste des participants</string>
<string name="groups_invite_members">Inviter des participants</string>
<string name="groups_member_created_you">Vous avez créé le groupe</string>
<string name="groups_member_created">%s a créé le groupe</string>
<string name="groups_member_joined_you">Vous avez rejoint le groupe</string>
<string name="groups_member_joined">%s a rejoint le groupe</string>
<string name="groups_member_joined_you">Vous vous êtes joint au groupe</string>
<string name="groups_member_joined">%s s\'est joint au groupe</string>
<string name="groups_leave">Quitter le groupe</string>
<string name="groups_leave_dialog_title">Confirmer la sortie du groupe</string>
<string name="groups_leave_dialog_message">Êtes-vous sûr de vouloir quitter ce groupe ?</string>
<string name="groups_dissolve">Supprimer le groupe</string>
<string name="groups_dissolve_dialog_title">Confirmer la suppression du groupe</string>
<string name="groups_dissolve_dialog_message">Êtes-vous sûr de vouloir supprimer ce groupe ?\n\nLes autres participants ne pourront plus continuer leur conversation et pourraient ne pas recevoir les derniers messages.</string>
<string name="groups_dissolve_button">Supprimer</string>
<string name="groups_dissolved_dialog_title">Le Groupe A Été Supprimé</string>
<string name="groups_dissolved_dialog_message">L\'initiateur de ce groupe l\'a supprimé.\n\nVous ne pouvez plus y écrire de messages et il se peut que vous n\'ayez pas reçu tout ceux qui y ont été publiés.</string>
<string name="groups_leave_dialog_message">Voulez-vous vraiment quitter ce groupe?</string>
<string name="groups_dissolve">Dissoudre le groupe</string>
<string name="groups_dissolve_dialog_title">Confirmer la dissolution du groupe</string>
<string name="groups_dissolve_dialog_message">Voulez-vous vraiment dissoudre ce groupe?\n\nLes autres participants ne pourront pas poursuivre leur conversation et ne recevront peut-être pas les derniers messages.</string>
<string name="groups_dissolve_button">Dissoudre</string>
<string name="groups_dissolved_dialog_title">Le groupe a été dissous</string>
<string name="groups_dissolved_dialog_message">Le créateur de ce groupe l\'a dissous.\n\nVous ne pouvez plus écrire de messages au groupe et ne recevrez peut-être pas tous ceux qui y ont été publiés.</string>
<!--Private Group Invitations-->
<string name="groups_invitations_title">Invitations du groupe</string>
<string name="groups_invitations_invitation_sent">Vous avez invité %1$s a rejoindre le groupe \"%2$s\".</string>
<string name="groups_invitations_invitation_received">%1$s vous a invité a rejoindre le groupe \"%2$s\".</string>
<string name="groups_invitations_joined">Groupe rejoint</string>
<string name="groups_invitations_declined">Invitation du groupe déclinée</string>
<string name="groups_invitations_title">Invitations de groupe</string>
<string name="groups_invitations_invitation_sent">Vous avez invité %1$s à se joindre au groupe « %2$s ».</string>
<string name="groups_invitations_invitation_received">%1$s vous a invité à vous joindre au groupe « %2$s ».</string>
<string name="groups_invitations_joined">S\'est joint au groupe</string>
<string name="groups_invitations_declined">L\'invitation de groupe a été refusée</string>
<plurals name="groups_invitations_open">
<item quantity="one">%d invitation de groupe en attente</item>
<item quantity="other">%d invitations de groupe en attente</item>
</plurals>
<string name="groups_invitations_response_accepted_sent">Vous avez accepté l\'invitation du groupe de %s.</string>
<string name="groups_invitations_response_declined_sent">Vous avez refusé l\'invitation du groupe de %s.</string>
<string name="groups_invitations_response_accepted_received">%s a accepté l\'invitation du groupe.</string>
<string name="groups_invitations_response_declined_received">%s a décliné l\'invitation du groupe.</string>
<string name="sharing_status_groups">Seul l\'initiateur du groupe peut inviter de nouveaux membres. Voici la liste des membres actuels.</string>
<string name="groups_invitations_response_accepted_sent">Vous avez accepté l\'invitation de groupe de %s.</string>
<string name="groups_invitations_response_declined_sent">Vous avez refusé l\'invitation de groupe de %s.</string>
<string name="groups_invitations_response_accepted_received">%s a accepté l\'invitation de groupe.</string>
<string name="groups_invitations_response_declined_received">%s a refusé l\'invitation de groupe.</string>
<string name="sharing_status_groups">Seul le créateur peut inviter de nouveaux participants. Voici la liste des membres actuels du groupe.</string>
<!--Private Groups Revealing Contacts-->
<string name="groups_reveal_contacts">Dévoiler les contacts</string>
<string name="groups_reveal_dialog_message">Vous pouvez choisir de dévoiler les contacts à tous les membres actuels et futurs de ce groupe.\n\nDévoiler les contacts vous assure une connexion au groupe plus rapide et plus fiable car vous pouvez communiquer avec les contacts dévoilés même si l\'initiateur du groupe est hors ligne.</string>
<string name="groups_reveal_dialog_message">Vous pouvez choisir de dévoiler les contacts à tous les participants actuels et futurs de ce groupe.\n\nDévoiler les contacts accélère et fiabilise votre connexion au groupe, car vous pouvez communiquer avec les contacts dévoilés même si le créateur du groupe est hors ligne.</string>
<string name="groups_reveal_visible">Votre lien avec le contact est visible par le groupe</string>
<string name="groups_reveal_visible_revealed_by_us">Votre lien avec le contact est visible par le groupe (dévoilé par vous-même)</string>
<string name="groups_reveal_visible_revealed_by_contact">Votre lien avec le contact est invisible par le groupe (dévoilé par %s)</string>
<string name="groups_reveal_invisible">Votre lien avec le contact est invisible par le groupe</string>
<string name="groups_reveal_visible_revealed_by_us">Votre lien avec le contact est visible par le groupe (dévoilé par vous)</string>
<string name="groups_reveal_visible_revealed_by_contact">Votre lien avec le contact est visible par le groupe (dévoilé par %s)</string>
<string name="groups_reveal_invisible">Votre lien avec le contact n\'est pas visible par le groupe</string>
<!--Forums-->
<string name="no_forums">Vous n\'avez pas de forums.\n\nPourquoi ne pas en créer un en touchant l\'icône + en haut ?\n\nVous pouvez aussi demander à vos contacts d\'en partager avec vous.</string>
<string name="no_forums">Vous n\'avez pas encore de forums.\n\nPourquoi ne pas en créer un en touchant l\'icône + située en haut?\n\nVous pouvez aussi demander à vos contacts d\'en partager avec vous.</string>
<string name="create_forum_title">Créer un forum</string>
<string name="choose_forum_hint">Choisir un nom pour votre forum </string>
<string name="create_forum_button">Créer un forum</string>
<string name="forum_created_toast">Forum créé</string>
<string name="no_forum_posts">Ce forum est vide.\n\nUtilisez l\'icône crayon, en haut pour écrire le premier message.\n\nVous sentez-vous seul, ici? Partager ce forum avec d\'autres contacts !</string>
<string name="no_posts">Aucun message</string>
<string name="forum_created_toast">Le forum a été créé</string>
<string name="no_forum_posts">Ce forum est vide.\n\nUtilisez l\'icône de crayon, en haut, pour rédiger le premier article.\n\nVous sentez-vous seul ici? Partagez ce forum avec vos contacts!</string>
<string name="no_posts">Aucun article</string>
<plurals name="posts">
<item quantity="one">%d message</item>
<item quantity="other">%d messages</item>
<item quantity="one">%d article</item>
<item quantity="other">%d articles</item>
</plurals>
<string name="forum_new_entry_posted">Message de forum saisi</string>
<string name="forum_new_entry_posted">L\'entrée de forum a été publiée</string>
<string name="forum_new_message_hint">Nouvelle entrée</string>
<string name="forum_message_reply_hint">Nouvelle réponse</string>
<string name="btn_reply">Répondre</string>
<string name="forum_leave">Quitter le forum</string>
<string name="dialog_title_leave_forum">Confirmer la sortie du forum</string>
<string name="dialog_message_leave_forum">Êtes-vous sûr de vouloir quitter ce forum ? Les contacts avec qui vous l\'avez partagé pourraient ne plus recevoir ses mises à jour.</string>
<string name="dialog_message_leave_forum">Voulez-vous vraiment quitter ce forum? Les contacts avec qui vous l\'avez partagé pourraient ne plus en recevoir les mises à jour.</string>
<string name="dialog_button_leave">Quitter</string>
<string name="forum_left_toast">Quitté le forum</string>
<string name="forum_left_toast">A quitté le forum</string>
<!--Forum Sharing-->
<string name="forum_share_button">Partagé le forum</string>
<string name="contacts_selected">Contacts sélectionnés</string>
<string name="activity_share_toolbar_header">Choisissez des contacts</string>
<string name="no_contacts_selector">Vous semblez être nouveau ici et n\'avez pas encore de contacts.\n\nRevenez lorsque vous aurez ajouté votre premier contact.</string>
<string name="forum_shared_snackbar">Forum partagé avec les contacts choisis</string>
<string name="forum_share_message">Ajouter un message (optionnel)</string>
<string name="forum_share_error">Une erreur s\'est produite lors du partage du forum.</string>
<string name="forum_invitation_received">%1$s a partagé le forum \"%2$s\" avec vous.</string>
<string name="forum_invitation_sent">Vous avez partagé le forum \"%1$s\" avec %2$s.</string>
<string name="forum_share_button">Partager le forum</string>
<string name="contacts_selected">Des contacts ont été sélectionnés</string>
<string name="activity_share_toolbar_header">Choisir des contacts</string>
<string name="no_contacts_selector">Il semble que soyez nouveau ici, sans encore aucun contact.\n\nRevenez ici après avoir ajouté votre premier contact.</string>
<string name="forum_shared_snackbar">Le forum a été partagé avec les contacts choisis</string>
<string name="forum_share_message">Ajouter un message (facultatif)</string>
<string name="forum_share_error">Une erreur est survenue lors du partage de ce forum.</string>
<string name="forum_invitation_received">%1$s a partagé le forum « %2$s » avec vous.</string>
<string name="forum_invitation_sent">Vous avez partagé le forum « %1$s » avec %2$s.</string>
<string name="forum_invitations_title">Invitations au forum</string>
<string name="forum_invitation_exists">Vous avez déjà accepté une invitation à ce forum. En acceptant d\'autre invitations, vous augmentez et renforcez la communication de ce forum.</string>
<string name="forum_joined_toast">Forum rejoint</string>
<string name="forum_declined_toast">Invitation au forum refusée</string>
<string name="forum_invitation_exists">Vous avez déjà accepté une invitation à ce forum. En acceptant d\'autres invitations, vous augmenterez et renforcerez la communication de ce forum.</string>
<string name="forum_joined_toast">Vous vous êtes joint au forum</string>
<string name="forum_declined_toast">L\'invitation au forum a été refusée</string>
<string name="shared_by_format">Partagé par %s</string>
<string name="forum_invitation_already_sharing">Déjà en cours de partage</string>
<string name="forum_invitation_response_accepted_sent">Vous avez accepté l\'invitation au forum de %s.</string>
<string name="forum_invitation_response_declined_sent">Vous avez refusé l\'invitation au forum de %s.</string>
<string name="forum_invitation_already_sharing">Le forum est déjà partagé</string>
<string name="forum_invitation_response_accepted_sent">Vous avez accepté l\'invitation de %s au forum.</string>
<string name="forum_invitation_response_declined_sent">Vous avez refusé l\'invitation de %s au forum.</string>
<string name="forum_invitation_response_accepted_received">%s a accepté l\'invitation au forum.</string>
<string name="forum_invitation_response_declined_received">%s a décliné l\'invitation au forum.</string>
<string name="sharing_status">Etat de partage</string>
<string name="sharing_status_forum">Tous les participants d\'un forum peuvent le partager avec leurs contacts. Vous partagez ce forum avec les contacts suivants. Il y a peut-être d\'autres participants que vous ne pouvez voir.</string>
<string name="forum_invitation_response_declined_received">%s a refusé l\'invitation au forum.</string>
<string name="sharing_status">État de partage</string>
<string name="sharing_status_forum">Tous les participants d\'un forum peuvent le partager avec leurs contacts. Vous partagez ce forum avec les contacts suivants. Il peut aussi y avoir d\'autres participants que vous ne pouvez pas voir.</string>
<string name="shared_with">Partagé avec %1$d (%2$d en ligne)</string>
<plurals name="forums_shared">
<item quantity="one">%d forum partagé par des contacts</item>
@@ -236,125 +242,126 @@
</plurals>
<string name="nobody">Personne</string>
<!--Blogs-->
<string name="blogs_other_blog_empty_state">Ce blog est actuellement vide.\n\nSoit l\'auteur n\'a encore rien écrit, soit la personne qui l\'a partagé avec vous doit venir en ligne pour que les messages soient synchronisés.</string>
<string name="read_more">Lire la suite</string>
<string name="blogs_write_blog_post">Écrire un message de blog</string>
<string name="blogs_write_blog_post_body_hint">Saisir ici votre message de blog</string>
<string name="blogs_other_blog_empty_state">Ce blogue est actuellement vide.\n\nSoit l\'auteur n\'a encore rien écrit, soit la personne qui l\'a partagé avec vous doit se connecter pour que les articles soient synchronisés.</string>
<string name="read_more">en lire davantage</string>
<string name="blogs_write_blog_post">Écrire un article de blogue</string>
<string name="blogs_write_blog_post_body_hint">Saisir votre message de blogue ici</string>
<string name="blogs_publish_blog_post">Publier</string>
<string name="blogs_blog_post_created">Message de blog créé</string>
<string name="blogs_blog_post_received">Nouveau message de blog reçu</string>
<string name="blogs_blog_post_scroll_to">Défiler jusqu\'à</string>
<string name="blogs_feed_empty_state">Ceci est le flux global des blogs.\n\nIl semble que personne n\'ait encore rien écrit.\n\nSoyez le premier et touchez l\'icône stylet pour écrire un message de blog.</string>
<string name="blogs_personal_blog">Blog personnel de %s</string>
<string name="blogs_remove_blog">Supprimer le blog</string>
<string name="blogs_remove_blog_dialog_message">Êtes-vous sûr de vouloir supprimer ce blog et tous ses messages ?\nNotez que ceci ne supprimera pas le blog sur les appareils des autres personnes.</string>
<string name="blogs_remove_blog_ok">Supprimer le blog</string>
<string name="blogs_blog_removed">Blog supprimé</string>
<string name="blogs_reblog_comment_hint">Ajouter un commentaire (optionnel)</string>
<string name="blogs_reblog_button">Reblog</string>
<string name="blogs_blog_post_created">L\'article de blogue a été créé</string>
<string name="blogs_blog_post_received">Un nouvel article de blogue a été reçu</string>
<string name="blogs_blog_post_scroll_to">Faire défiler jusqu\'à</string>
<string name="blogs_feed_empty_state">Ceci est le flux global des blogues.\n\nIl semble que personne n\'ait encore rien écrit.\n\nSoyez le premier et touchez l\'icône de crayon pour rédiger un nouvel article de blogue.</string>
<string name="blogs_remove_blog">Supprimer le blogue</string>
<string name="blogs_remove_blog_dialog_message">Voulez-vous vraiment supprimer ce blogue et tous ses messages?\nNotez que cela ne le supprimera pas des appareils d\'autrui.</string>
<string name="blogs_remove_blog_ok">Supprimer le blogue</string>
<string name="blogs_blog_removed">Le blogue a été supprimé</string>
<string name="blogs_reblog_comment_hint">Ajouter un commentaire (facultatif)</string>
<string name="blogs_reblog_button">Rebloguer</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Partager le blog</string>
<string name="blogs_sharing_error">Une erreur s\'est produite lors du partage du blog.</string>
<string name="blogs_sharing_button">Partager le blog</string>
<string name="blogs_sharing_snackbar">Blog partagé avec les contacts choisis</string>
<string name="blogs_sharing_response_accepted_sent">Vous avez accepté l\'invitation au blog de %s.</string>
<string name="blogs_sharing_response_declined_sent">Vous avez refusé l\'invitation au blog de %s.</string>
<string name="blogs_sharing_response_accepted_received">%s a accepté l\'invitation au blog.</string>
<string name="blogs_sharing_response_declined_received">%s a décliné l\'invitation au blog.</string>
<string name="blogs_sharing_invitation_received">%1$s a partagé le blog personnel de %2$s avec vous.</string>
<string name="blogs_sharing_invitation_sent">Vous avez partagé le blog personnel de %1$s avec %2$s.</string>
<string name="blogs_sharing_invitations_title">Invitations au blog</string>
<string name="blogs_sharing_joined_toast">Abonné au Blog</string>
<string name="blogs_sharing_declined_toast">Invitation au blog refusée</string>
<string name="sharing_status_blog">Quiconque est abonné à un blog peut le partager avec ses contacts. Vous partagez ce blog avec les contacts suivants. Il y a peut-être d\'autres abonnés que vous ne pouvez voir.</string>
<string name="blogs_sharing_share">Partager le blogue</string>
<string name="blogs_sharing_error">Une erreur est survenue lors du partage du blogue.</string>
<string name="blogs_sharing_button">Partager le blogue</string>
<string name="blogs_sharing_snackbar">Le blogue a été partagé avec les contacts choisis</string>
<string name="blogs_sharing_response_accepted_sent">Vous avez accepté l\'invitation de %s au blogue.</string>
<string name="blogs_sharing_response_declined_sent">Vous avez refusé l\'invitation de %s au blogue.</string>
<string name="blogs_sharing_response_accepted_received">%s a accepté l\'invitation au blogue.</string>
<string name="blogs_sharing_response_declined_received">%s a refusé l\'invitation au blogue.</string>
<string name="blogs_sharing_invitation_received">%1$s a partagé le blogue « %2$s » avec vous.</string>
<string name="blogs_sharing_invitation_sent">Vous avez partagé le blogue « %1$s » avec %2$s.</string>
<string name="blogs_sharing_invitations_title">Invitations au blogue</string>
<string name="blogs_sharing_joined_toast">Est abonné au blogue</string>
<string name="blogs_sharing_declined_toast">L\'invitation au blogue a été refusée</string>
<string name="sharing_status_blog">Quiconque est abonné à un blogue peut le partager avec ses contacts. Vous partagez ce blogue avec les contacts suivants. Il peut aussi y avoir d\'autres abonnés que vous ne pouvez pas voir.</string>
<!--RSS Feeds-->
<string name="blogs_rss_feeds_import">Import de flux RSS</string>
<string name="blogs_rss_feeds_import">Importer un flux RSS</string>
<string name="blogs_rss_feeds_import_button">Importer</string>
<string name="blogs_rss_feeds_import_hint">Saisir l\'URL du Flux RSS</string>
<string name="blogs_rss_feeds_import_error">Désolé, une erreur s\'est produite lors de limportation du flux.</string>
<string name="blogs_rss_feeds_manage">Gérer le flux RSS</string>
<string name="blogs_rss_feeds_manage_imported">Importé :</string>
<string name="blogs_rss_feeds_import_hint">Saisir l\'URL du flux RSS</string>
<string name="blogs_rss_feeds_import_error">Nous sommes désolé! Une erreur est survenue lors de limportation de votre flux.</string>
<string name="blogs_rss_feeds_manage">Gérer les flux RSS</string>
<string name="blogs_rss_feeds_manage_imported">Importés :</string>
<string name="blogs_rss_feeds_manage_author">Auteur :</string>
<string name="blogs_rss_feeds_manage_updated">Dernière mise à jour :</string>
<string name="blogs_rss_remove_feed">Supprimer le flux</string>
<string name="blogs_rss_remove_feed_dialog_message">Êtes-vous sûr de vouloir supprimer ce flux et tous ses messages ?\nLes messages que vous avez partagés ne seront pas supprimés des appareils des autres personnes.</string>
<string name="blogs_rss_remove_feed_dialog_message">Voulez-vous vraiment supprimer ce flux et tous ses messages ?\nLes articles que vous avez partagés ne seront pas supprimés des appareils d\'autrui.</string>
<string name="blogs_rss_remove_feed_ok">Supprimer le flux</string>
<string name="blogs_rss_feeds_manage_delete_error">Le flux n\'a pas pu être supprimé !</string>
<string name="blogs_rss_feeds_manage_empty_state">Vous n\'avez importé aucun flux RSS.\n\nPourquoi ne pas cliquer le plus dans le coin en haut à droite pour ajouter votre premier ?</string>
<string name="blogs_rss_feeds_manage_error">Problème lors du chargement de vos flux. essayer ultérieurement.</string>
<string name="blogs_rss_feeds_manage_delete_error">Impossible de supprimer le flux!</string>
<string name="blogs_rss_feeds_manage_empty_state">Vous n\'avez pas encore importé de flux RSS.\n\nPourquoi ne pas cliquer sur l\'icône +, en haut à droite, pour ajouter votre premier flux?</string>
<string name="blogs_rss_feeds_manage_error">Un problème est survenu lors du chargement de vos flux. Veuillez ressayer ultérieurement.</string>
<!--Settings Network-->
<string name="network_settings_title">Réseaux </string>
<string name="bluetooth_setting">Connecter par Bluetooth</string>
<string name="network_settings_title">Réseaux</string>
<string name="bluetooth_setting">Se connecter par Bluetooth</string>
<string name="bluetooth_setting_enabled">Lorsque des contacts sont à proximité</string>
<string name="bluetooth_setting_disabled">Uniquement lors de l\'ajout de contacts</string>
<string name="tor_network_setting">Connecter via Tor</string>
<string name="tor_network_setting">Se connecter avec Tor</string>
<string name="tor_network_setting_never">Jamais</string>
<string name="tor_network_setting_wifi">Seulement par Wi-Fi</string>
<string name="tor_network_setting_always">Par Wi-Fi ou données mobiles</string>
<string name="tor_network_setting_always">En utilisant le Wi-Fi ou le service mobile de données</string>
<!--Settings Security and Panic-->
<string name="security_settings_title">Sécurité</string>
<string name="change_password">Modifier le mot de passe</string>
<string name="current_password">Tapez le mot de passe actuel :</string>
<string name="choose_new_password">Choisir le nouveau mot de passe :</string>
<string name="confirm_new_password">Confirmer le nouveau mot de passe :</string>
<string name="change_password">Changer le mot de passe</string>
<string name="current_password">Saisir votre mot de passe actuel :</string>
<string name="choose_new_password">Choisir votre nouveau mot de passe :</string>
<string name="confirm_new_password">Confirmer votre nouveau mot de passe :</string>
<string name="password_changed">Le mot de passe a été changé.</string>
<string name="panic_setting">Paramètres du bouton Panique</string>
<string name="panic_setting_title">Bouton Panique</string>
<string name="panic_setting_hint">Configurer Briar pour l\'utilisation de l\'application du bouton Panique</string>
<string name="panic_app_setting_title">Application bouton Panique</string>
<string name="unknown_app">une application inconnue</string>
<string name="panic_app_setting_summary">Aucune application définie</string>
<string name="panic_setting">Paramètres du bouton d\'urgence</string>
<string name="panic_setting_title">Bouton d\'urgence</string>
<string name="panic_setting_hint">Configurer la réaction de Briar lorsque vous utilisez une application de bouton d\'urgence</string>
<string name="panic_app_setting_title">Application de bouton d\'urgence</string>
<string name="unknown_app">une appli inconnue</string>
<string name="panic_app_setting_summary">Aucune appli n\'a été définie</string>
<string name="panic_app_setting_none">Aucune</string>
<string name="dialog_title_connect_panic_app">Confirmer l\'application Panique</string>
<string name="dialog_message_connect_panic_app">Êtes-vous sûr de vouloir autoriser %1$s à déclencher les actions destructives du bouton Panique ?</string>
<string name="lock_setting_title">Se déconnecter</string>
<string name="lock_setting_summary">Se déconnecter de Briar lorsque le bouton Panique est pressé</string>
<string name="dialog_title_connect_panic_app">Confirmer l\'application d\'urgence</string>
<string name="dialog_message_connect_panic_app">Voulez-vous vraiment autoriser %1$s à déclencher les actions destructrices du bouton d\'urgence?</string>
<string name="lock_setting_title">Déconnexion</string>
<string name="lock_setting_summary">Se déconnecter de Briar lorsqu\'on appuie sur un bouton d\'urgence</string>
<string name="purge_setting_title">Supprimer le compte</string>
<string name="purge_setting_summary">Supprimer votre compte lorsqu\'un bouton Panique est pressé. Attention : ceci supprimera définitivement vos identités, contacts et messages.</string>
<string name="purge_setting_summary">Supprimer votre compte Briar lorsqu\'on appuie sur un bouton d\'urgence. Attention : cela supprimera définitivement vos identités, contacts et messages</string>
<string name="uninstall_setting_title">Désinstaller Briar</string>
<string name="uninstall_setting_summary">Ceci nécessite une confirmation manuelle en cas d\'événement de panique</string>
<string name="uninstall_setting_summary">Une confirmation manuelle est exigée en cas d\'événement d\'urgence</string>
<!--Settings Notifications-->
<string name="notification_settings_title">Notifications</string>
<string name="notify_private_messages_setting_title">Messages privés</string>
<string name="notify_private_messages_setting_summary">Afficher les notifications pour les messages privés</string>
<string name="notify_private_messages_setting_summary">Afficher des alertes pour les messages privés</string>
<string name="notify_group_messages_setting_title">Messages de groupe</string>
<string name="notify_group_messages_setting_summary">Afficher les notifications pour les messages de groupe</string>
<string name="notify_forum_posts_setting_title">Messages de forum</string>
<string name="notify_forum_posts_setting_summary">Afficher les notifications des messages de forum</string>
<string name="notify_blog_posts_setting_title">Messages de blog</string>
<string name="notify_blog_posts_setting_summary">Afficher les notifications pour les messages de blog</string>
<string name="notify_vibration_setting">Vibration</string>
<string name="notify_sound_setting">Sonnerie</string>
<string name="notify_group_messages_setting_summary">Afficher des alertes pour les messages de groupe</string>
<string name="notify_forum_posts_setting_title">Articles de forum</string>
<string name="notify_forum_posts_setting_summary">Afficher des alertes pour les articles de forum</string>
<string name="notify_blog_posts_setting_title">Articles de blogue</string>
<string name="notify_blog_posts_setting_summary">Afficher des alertes pour pour les articles de blogue</string>
<string name="notify_vibration_setting">Vibrer</string>
<string name="notify_lock_screen_setting_title">Écran de verrouillage</string>
<string name="notify_lock_screen_setting_summary">Afficher les notifications sur l\'écran de verrouillage</string>
<string name="notify_sound_setting">Son</string>
<string name="notify_sound_setting_default">Sonnerie par défaut</string>
<string name="notify_sound_setting_disabled">Aucune</string>
<string name="choose_ringtone_title">Choisir la sonnerie</string>
<string name="notify_sound_setting_disabled">Aucun</string>
<string name="choose_ringtone_title">Choisir une sonnerie</string>
<!--Settings Feedback-->
<string name="feedback_settings_title">Commentaires</string>
<string name="send_feedback">Envoyer vos commentaires</string>
<string name="feedback_settings_title">Rétroaction</string>
<string name="send_feedback">Envoyer une rétroaction</string>
<!--Link Warning-->
<string name="link_warning_title">Avertissement de lien</string>
<string name="link_warning_intro">Vous êtes sur le point d\'ouvrir le lien suivant via une application externe.</string>
<string name="link_warning_text">Ceci pourrait être utilisé pour vous identifier. Évaluez la confiance que vous avez en la personne qui vous a envoyé ce lien et envisagez de l\'ouvrir avec Orfox.</string>
<string name="link_warning_intro">Vous êtes sur le point d\'ouvrir le lien suivant avec une appli externe.</string>
<string name="link_warning_text">Cela pourrait être utilisé pour vous identifier. Décidez si vous faites confiance à la personne qui vous a envoyé ce lien et envisagez de l\'ouvrir avec Orfox.</string>
<string name="link_warning_open_link">Ouvrir le lien</string>
<!--Crash Reporter-->
<string name="crash_report_title">Rapport de plantage Briar</string>
<string name="crash_report_title">Rapport de plantage de Briar</string>
<string name="briar_crashed">Désolé, Briar a planté.</string>
<string name="not_your_fault">Vous n\'y êtes pour rien.</string>
<string name="please_send_report">Aidez-nous à améliorer Briar en nous envoyant un rapport de plantage.</string>
<string name="please_send_report">Veuillez nous aider à améliorer Briar en nous envoyant un rapport de plantage.</string>
<string name="report_is_encrypted">Nous promettons que le rapport est chiffré et envoyé en toute sécurité. </string>
<string name="feedback_title">Commentaires</string>
<string name="describe_crash">Décrivez ce qui s\'est produit (optionnel)</string>
<string name="enter_feedback">Saisir vos commentaires</string>
<string name="optional_contact_email">Votre adresse émail (optionnel)</string>
<string name="include_debug_report_crash">Ajouter des données anonymes concernant le plantage</string>
<string name="include_debug_report_feedback">Ajouter des données anonymes concernant cet appareil</string>
<string name="feedback_title">Rétroaction</string>
<string name="describe_crash">Décrivez ce qui s\'est produit (facultatif)</string>
<string name="enter_feedback">Saisir votre rétroaction</string>
<string name="optional_contact_email">Votre adresse courriel (facultatif)</string>
<string name="include_debug_report_crash">Inclure des données anonymes concernant le plantage</string>
<string name="include_debug_report_feedback">Inclure des données anonymes concernant cet appareil</string>
<string name="could_not_load_report_data">Impossible de charger les données du rapport.</string>
<string name="send_report">Envoyer le rapport</string>
<string name="close">Fermer</string>
<string name="dev_report_saved">Rapport enregistré. Il sera envoyé lors de votre prochaine connexion à Briar.</string>
<string name="dev_report_saved">Le rapport a été enregistré. Il sera envoyé lors de votre prochaine connexion à Briar.</string>
<!--Sign Out-->
<string name="progress_title_logout">Déconnexion de Briar ...</string>
<string name="progress_title_logout">Déconnexion de Briar</string>
<!--Screen Filters & Tapjacking-->
<string name="screen_filter_title">Superposition d\'écran détectée</string>
<string name="screen_filter_body">Une autre app s\'affiche par dessus Briar. Pour protéger votre sécurité, Briar ne répond pas au toucher lorsqu\'une autre app s\'affiche par dessus.\n\nEssayer de fermer les applications suivantes lorsque vous utilisez Briar :\n\n%1$s</string>
<string name="screen_filter_title">Une superposition d\'écran a été détectée</string>
<string name="screen_filter_body">Une autre appli s\'affiche par dessus Briar. Pour protéger votre sécurité, Briar ne répondra pas au toucher si autre appli s\'affiche par dessus.\n\nEssayez de fermer les applis suivantes lorsque vous utilisez Briar :\n\n%1$s</string>
</resources>

View File

@@ -22,7 +22,11 @@
<string name="startup_failed_activity_title">Fallimento Avvio Briar</string>
<string name="startup_failed_db_error">Per alcune reagioni, il tuo database Briar si è corrotto in modo irreparabile. Il tuo account, i tuoi dati e tutte le connessioni ai tuoi contatti sono andati persi. Sfortunatamente, devi reinstallare Briar per creare un nuovo account.</string>
<string name="startup_failed_service_error">Briar non è stato in grado di caricare un plugin richiesto. Reinstallare Briar di solito sistema questo problema. Però ricorda che perderai il tuo account e tutti i dati ad esso associati poichè Briar non usa server centralizzati per mantenere i tuoi dati.</string>
<string name="expiry_warning">Questo software è scaduto.\nSi prega di installare una nuova versione.</string>
<plurals name="expiry_warning">
<item quantity="one">Questa è una versione beta di Briar. Il tuo account scadrà in %d giorno e non può essere rinnovato.</item>
<item quantity="other">Questa è una versione beta di Briar. Il tuo account scadrà in %d giorni e non può essere rinnovato.</item>
</plurals>
<string name="expiry_date_reached">Questo software è scaduto.\nGrazie per il test!</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Apri la barra di navigazione</string>
<string name="nav_drawer_close_description">Chiudi la barra di navigazione</string>
@@ -117,6 +121,7 @@
<string name="introduction_onboarding_text">Puoi presentare i tuoi contatti fra di loro, così non hanno bisogno di incontrarsi di persona per connettersi a Briar.</string>
<string name="introduction_activity_title">Seleziona Contatto</string>
<string name="introduction_message_title">Introduzione Contatti</string>
<string name="introduction_message_hint">Aggiungi un messaggio (facoltativo)</string>
<string name="introduction_button">Crea l\'introduzione</string>
<string name="introduction_sent">La tua introduzione è stata inviata.</string>
<string name="introduction_error">C\'è stato un errore nella creazione dell\'introduzione</string>
@@ -147,6 +152,7 @@
<string name="groups_create_group_title">Crea gruppo privato</string>
<string name="groups_create_group_button">Crea gruppo</string>
<string name="groups_create_group_invitation_button">Invia invito</string>
<string name="groups_create_group_hint">Scegli un nome per il tuo gruppi privato</string>
<string name="groups_invitation_sent">Invito a partecipare al gruppo spedito</string>
<string name="groups_message_sent">Messaggio inviato</string>
<string name="groups_member_list">Lista membri</string>
@@ -188,6 +194,8 @@
<string name="groups_reveal_invisible">La relazione fra i contatti non è visibile al gruppo</string>
<!--Forums-->
<string name="no_forums">Non hai ancora alcun forum.\n\nPerchè non ne crei uno nuovo tu premendo l\' icona + in alto?\n\nPuoi anche chiedere ai tuoi contatti di condividere forums con te.</string>
<string name="create_forum_title">Crea Forum</string>
<string name="choose_forum_hint">Scegli un nome per il tuo forum</string>
<string name="create_forum_button">Crea Forum</string>
<string name="forum_created_toast">Forum creato</string>
<string name="no_forum_posts">Questo forum è vuoto.\n\nUsa l\' icona penna in alto per comporre il primo post.\n\nTi senti solo qui? Condividi questo forum con i tuoi contatti!</string>
@@ -199,7 +207,7 @@
<string name="forum_new_entry_posted">Nuova inserzione sul forum</string>
<string name="forum_new_message_hint">Nuova inserzione</string>
<string name="forum_message_reply_hint">Nuova Risposta</string>
<string name="btn_reply">Rispondere</string>
<string name="btn_reply">Rispondi</string>
<string name="forum_leave">Lascia Forum</string>
<string name="dialog_title_leave_forum">Conferma l\'abbandono del forum</string>
<string name="dialog_message_leave_forum">Sei sicuro di voler lasciare questo forum? I contatti che hai condiviso in questo forum potrebbero essere tagliati fuori dalla ricezione di aggiornamenti di questo forum.</string>
@@ -211,6 +219,7 @@
<string name="activity_share_toolbar_header">Scegli Contatti</string>
<string name="no_contacts_selector">Sembra che tu sia nuovo qui e non hai alcun contatto ancora.\n\nPer favore torna dopo aver aggiunto il tuo primo contatto.</string>
<string name="forum_shared_snackbar">Forum condiviso con i contatti scelti</string>
<string name="forum_share_message">Aggiungi un messaggio (facoltativo)</string>
<string name="forum_share_error">C\'è stato un errore nella condivisione di questo forum.</string>
<string name="forum_invitation_received">%1$s ha condiviso il forum \"%2$s\" con te.</string>
<string name="forum_invitation_sent">Hai condiviso il forum \"%1$s\" con %2$s.</string>
@@ -242,11 +251,11 @@
<string name="blogs_blog_post_received">Ricevuto nuovo post del blog</string>
<string name="blogs_blog_post_scroll_to">Scorri a</string>
<string name="blogs_feed_empty_state">Questo è il feed globale del blog.\n\nSembra che nessuno abbia ancora scritto nulla.\n\nSii il primo e tocca l\'icona della penna per scrivere un nuovo post sul blog.</string>
<string name="blogs_personal_blog">Il blog personale di %s</string>
<string name="blogs_remove_blog">Rimuovi Blog</string>
<string name="blogs_remove_blog_dialog_message">Sei sicuro di voler rimuovere questo blog e tutti i post?\nTieni presente che questo non rimuoverà il blog dai dispositivi degli altri.</string>
<string name="blogs_remove_blog_ok">Rimuovi Blog</string>
<string name="blogs_blog_removed">Blog Rimosso</string>
<string name="blogs_reblog_comment_hint">Aggiungi un commento (facoltativo)</string>
<string name="blogs_reblog_button">Reblog</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Condividi Blog</string>
@@ -257,8 +266,8 @@
<string name="blogs_sharing_response_declined_sent">Hai declinato l\'invito al blog da %s.</string>
<string name="blogs_sharing_response_accepted_received">%s ha accettato l\'invito al blog.</string>
<string name="blogs_sharing_response_declined_received">%s ha declinato l\'invito al blog.</string>
<string name="blogs_sharing_invitation_received">%1$s ha condiviso il blog personale di %2$s con te.</string>
<string name="blogs_sharing_invitation_sent">Hai condiviso il tuo blog personale di %1$s con %2$s.</string>
<string name="blogs_sharing_invitation_received">%1$s ha condiviso il blog \"%2$s\" con te.</string>
<string name="blogs_sharing_invitation_sent">Hai condiviso il blog \"%1$s\" con %2$s.</string>
<string name="blogs_sharing_invitations_title">Inviti Blog</string>
<string name="blogs_sharing_joined_toast">Iscritto al Blog</string>
<string name="blogs_sharing_declined_toast">Invito al blog declinato</string>
@@ -311,7 +320,17 @@
<string name="uninstall_setting_summary">Questo richiede una conferma manuale in un evento panico</string>
<!--Settings Notifications-->
<string name="notification_settings_title">Notifiche</string>
<string name="notify_private_messages_setting_title">Messaggi privati</string>
<string name="notify_private_messages_setting_summary">Mostra avvisi per i messaggi privati</string>
<string name="notify_group_messages_setting_title">Messaggi di gruppo</string>
<string name="notify_group_messages_setting_summary">Mostra avvisi per messaggi di gruppo</string>
<string name="notify_forum_posts_setting_title">Post di forum</string>
<string name="notify_forum_posts_setting_summary">Mostra avvisi per i post di forum</string>
<string name="notify_blog_posts_setting_title">Post di blog</string>
<string name="notify_blog_posts_setting_summary">Mostra avvisi per post di blog</string>
<string name="notify_vibration_setting">Vibrazione</string>
<string name="notify_lock_screen_setting_title">Blocca schermo</string>
<string name="notify_lock_screen_setting_summary">Mostra notifiche sul blocca schermo</string>
<string name="notify_sound_setting">Suono</string>
<string name="notify_sound_setting_default">Suoneria di default</string>
<string name="notify_sound_setting_disabled">Nessuno</string>
@@ -343,4 +362,6 @@
<!--Sign Out-->
<string name="progress_title_logout">Uscire da Briar ...</string>
<!--Screen Filters & Tapjacking-->
<string name="screen_filter_title">È stata rilevata un\'overlay sullo schermo</string>
<string name="screen_filter_body">Un\'altra app sta disegnando sopra a Briar. Per proteggere la tua sicurezza, Briar non risponderà ai tocchi quando un\'altra app vi starà disegnando sopra.\n\nProva a spegnere le seguenti app mentre usi Briar:\n\n%1$s</string>
</resources>

View File

@@ -0,0 +1,367 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<!--Setup-->
<string name="setup_title">Oppsett av Briar</string>
<string name="setup_explanation">Din Briar-konto er lagret kryptert på din enhet, ikke i skyen. Hvis du avinstallerer Briar eller glemmer passordet ditt, går det ikke an å gjenopprette kontoen og dataen din.</string>
<string name="choose_nickname">Velg kallenavn</string>
<string name="choose_password">Velg passord</string>
<string name="confirm_password">Bekreft passord</string>
<string name="name_too_long">For langt navn</string>
<string name="password_too_weak">Passordet er for svakt</string>
<string name="passwords_do_not_match">Passordene samsvarer ikke</string>
<string name="create_account_button">Opprett konto</string>
<!--Login-->
<string name="enter_password">Skriv inn passord:</string>
<string name="try_again">Feil passord, prøv igjen</string>
<string name="sign_in_button">Logg inn</string>
<string name="forgotten_password">Jeg har glemt passordet mitt</string>
<string name="dialog_title_lost_password">Tapt passord</string>
<string name="dialog_message_lost_password">Din Briar-konto er lagret kryptert på din enhet, ikke i skyen, så du kan ikke tilbakestille passordet ditt. Ønsker du å slette kontoen og starte igjen?\n\nMerk: Identitetene dine, kontaktene og meldingene vil gå tapt for alltid.</string>
<string name="startup_failed_notification_title">Briar kunne ikke starte</string>
<string name="startup_failed_notification_text">Det kan hende du må reinstallere Briar.</string>
<string name="startup_failed_activity_title">Oppstartsfeil med Briar</string>
<string name="startup_failed_db_error">Av ukjent brunn, er din Briar-database skadet og kan ikke repareres. Kontoen din, alle data, og din kontakter har gått tapt for alltid. Beklageligvis må du reinstallere Briar og sette opp en ny konto.</string>
<string name="startup_failed_service_error">Briar kunne ikke starte det nødvendige programtillegget. Reinstallasjon av Briar fikser vanligvis dette problemet. Merk deg dog at kontoen og all data tilknyttet den vil gå tapt for godt siden Briar ikke bruker sentrale tjenere å lagre dataen din på.</string>
<plurals name="expiry_warning">
<item quantity="one">Dette er en betaversjon av Briar. Din konto vil utløpe om %d dag og kan ikke fornyes.</item>
<item quantity="other">Dette er en betaversjon av Briar. Din konto vil utløpe om %d dager og kan ikke fornyes.</item>
</plurals>
<string name="expiry_date_reached">Denne programvaren har utløpt.\nTakk for at du testet den.</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Åpne navigasjonsskuffen</string>
<string name="nav_drawer_close_description">Lukk navigasjonsskuffen</string>
<string name="contact_list_button">Kontakter</string>
<string name="groups_button">Private grupper</string>
<string name="forums_button">Forum</string>
<string name="blogs_button">Blogger</string>
<string name="settings_button">Innstillinger</string>
<string name="sign_out_button">Logg ut</string>
<!--Transports-->
<string name="transport_tor">Internett</string>
<string name="transport_bt">Blåtann</string>
<string name="transport_lan">Wi-Fi</string>
<!--Notifications-->
<string name="ongoing_notification_title">Logget inn på Briar</string>
<string name="ongoing_notification_text">Trykk for å åpne Briar.</string>
<plurals name="private_message_notification_text">
<item quantity="one">Ny privat melding.</item>
<item quantity="other">%d nye private meldinger.</item>
</plurals>
<plurals name="group_message_notification_text">
<item quantity="one">Ny gruppemelding.</item>
<item quantity="other">%d nye gruppemelindger.</item>
</plurals>
<plurals name="forum_post_notification_text">
<item quantity="one">Ny forumpost.</item>
<item quantity="other">%d nye forumposter.</item>
</plurals>
<plurals name="blog_post_notification_text">
<item quantity="one">Ny bloggpost.</item>
<item quantity="other">%d nye bloggposter.</item>
</plurals>
<!--Misc-->
<string name="now"></string>
<string name="show">Vis</string>
<string name="hide">Skjul</string>
<string name="ok">OK</string>
<string name="cancel">Avbryt</string>
<string name="got_it">Skjønner</string>
<string name="delete">Slett</string>
<string name="accept">Godta</string>
<string name="decline">Avslå</string>
<string name="options">Valg</string>
<string name="online">Pålogget</string>
<string name="offline">Frakoblet</string>
<string name="send">Send</string>
<string name="allow">Tillat</string>
<string name="open">Åpne</string>
<string name="no_data">Ingen data</string>
<string name="ellipsis"></string>
<string name="text_too_long">Innskrevet tekst er for lang</string>
<string name="show_onboarding">Vis hjelpedialogvindu</string>
<!--Contacts and Private Conversations-->
<string name="no_contacts">Det later til at du er ny her og ikke har noen kontakter enda.\n\nTrykk på \"+\"-ikonet på toppen og følg instruksjonene for å legge til noen venner på listen din.\n\nHusk: Du kan bare legge til nye kontakter ansikt-til-ansikt for å forhindre noen fra å etterligne deg eller fra å lese meldingene dine i framtiden.</string>
<string name="date_no_private_messages">Ingen meldinger.</string>
<string name="no_private_messages">Dette er samtaleoversikten.\n\nDet ser ut til at det er mangel på samtaler.\n\nTrykk på inndatafeltet på bunnen for å starte en samtale.</string>
<string name="message_hint">Skriv melding</string>
<string name="delete_contact">Slett kontakt</string>
<string name="dialog_title_delete_contact">Bekreft sletting av kontakt</string>
<string name="dialog_message_delete_contact">Er du sikker på at du vil fjerne denne kontakten og alle meldinger utvekslet med denne kontakten?</string>
<string name="contact_deleted_toast">Kontakt slettet</string>
<!--Adding Contacts-->
<string name="add_contact_title">Legg til en kontakt</string>
<string name="your_nickname">Velg identiteten du ønsker å bruke:</string>
<string name="face_to_face">Du må møte personen du ønsker å legge til som kontakt.\n\nDette vil forhindre folk fra å etterligne deg eller lese meldingene dine i fremtiden.</string>
<string name="continue_button">Fortsett</string>
<string name="your_invitation_code">Din invitasjonskode er</string>
<string name="enter_invitation_code">Skriv inn invitasjonskoden fra din kontakt:</string>
<string name="searching_format">Søker etter kontakt med invitasjonskode %06d\u2026</string>
<string name="connection_failed">Tilkobling mislyktes</string>
<string name="could_not_find_contact">Briar kunne ikke finne kontakten din nærheten</string>
<string name="try_again_button">Prøv igjen</string>
<string name="connected_to_contact">Tilkoblet kontakt</string>
<string name="calculating_confirmation_code">Regner ut bekreftelseskode\u2026</string>
<string name="your_confirmation_code">Din bekreftelseskode er</string>
<string name="enter_confirmation_code">Skriv inn din kontakts bekreftelseskode:</string>
<string name="waiting_for_contact">Venter på kontakt\u2026</string>
<string name="waiting_for_contact_to_scan">Venter på at kontakten skal skanne og koble til\u2026</string>
<string name="exchanging_contact_details">Utveksler kontaktdetaljer\u2026</string>
<string name="codes_do_not_match">Kodene samsvarer ikke</string>
<string name="interfering">Dette kan bety at noen prøver å tukle med tilkoblingen</string>
<string name="contact_added_toast">Kontakt lagt til: %s</string>
<string name="contact_already_exists">Kontakten %s finnes allerede</string>
<string name="contact_exchange_failed">Kontaktutveksling mislyktes</string>
<string name="qr_code_invalid">QR-koden er ugyldig</string>
<string name="connecting_to_device">Kobler til enhet\u2026</string>
<string name="authenticating_with_device">Autentiserer med enhet\u2026</string>
<string name="connection_aborted_local">Tilkoblingen ble avbrutt på din side! Dette kan bety at noen prøver å tukle med tilkoblingen din</string>
<string name="connection_aborted_remote">Tilkobling avbrutt av din kontakt! Dette kan bety at noen prøver å tukle med vedkommendes tilkobling</string>
<!--Introductions-->
<string name="introduction_onboarding_title">Introduser kontaktene dine</string>
<string name="introduction_onboarding_text">Du kan introdusere dine kontakter til hverandre, slik at de ikke trenger å møtes personlig for å treffes på Briar.</string>
<string name="introduction_activity_title">Velg kontakt</string>
<string name="introduction_message_title">Introduser kontakter</string>
<string name="introduction_message_hint">Legg til melding (valgfritt)</string>
<string name="introduction_button">Introduser ovenfor hverandre</string>
<string name="introduction_sent">Din introduksjon har blitt sendt.</string>
<string name="introduction_error">Feil under utstedelse av introduksjon.</string>
<string name="introduction_response_error">Feil under svar på introduksjon</string>
<string name="introduction_request_sent">Du har spurt %1$s om å bli introdusert ovenfor %2$s.</string>
<string name="introduction_request_received">%1$s har spurt om å introdusere deg ovenfor %2$s. Ønsker du å legge til %2$s på din kontaktliste?</string>
<string name="introduction_request_exists_received">%1$s har spurt om å introdusere deg ovenfor %2$s, men %2$s er allerede på din kontaktliste. Siden %1$s kanskje ikke vet dette, kan du fremdeles svare:</string>
<string name="introduction_request_answered_received">%1$s har spurt om å introdusere deg ovenfor %2$s.</string>
<string name="introduction_response_accepted_sent">Du er nå bekjent %1$s.</string>
<string name="introduction_response_declined_sent">Du avslo introduksjonen med %1$s.</string>
<string name="introduction_response_accepted_received">%1$s er nå bekjent %2$s.</string>
<string name="introduction_response_declined_received">%1$s avslo introduksjonen med %2$s.</string>
<string name="introduction_response_declined_received_by_introducee">%1$s sier at %2$s avslo introduksjonen.</string>
<plurals name="introduction_notification_text">
<item quantity="one">Ny kontakt lagt til.</item>
<item quantity="other">%d nye kontakter lagt til.</item>
</plurals>
<!--Private Groups-->
<string name="groups_list_empty">Du deltar ikke i noen grupper.\n\nTrykk på \"+\"-ikonet på toppen for å opprette en gruppe selv, eller spørre noen av kontaktene dine om å bli invitert inn i en av deres grupper.</string>
<string name="groups_created_by">Opprettet av %s</string>
<plurals name="messages">
<item quantity="one">%d melding</item>
<item quantity="other">%d meldinger</item>
</plurals>
<string name="groups_group_is_empty">Denne gruppen er tom</string>
<string name="groups_group_is_dissolved">Denne gruppen har blitt oppløst</string>
<string name="groups_remove">Fjern</string>
<string name="groups_create_group_title">Opprett privat gruppe</string>
<string name="groups_create_group_button">Opprett gruppe</string>
<string name="groups_create_group_invitation_button">Send invitasjon</string>
<string name="groups_create_group_hint">Velg et navn for din private gruppe</string>
<string name="groups_invitation_sent">Gruppeinvitasjon sendt</string>
<string name="groups_message_sent">Melding sendt</string>
<string name="groups_member_list">Medlemsliste</string>
<string name="groups_invite_members">Inviter medlemmer</string>
<string name="groups_member_created_you">Du opprettet gruppen</string>
<string name="groups_member_created">%s opprettet gruppen</string>
<string name="groups_member_joined_you">Du tok del i gruppen</string>
<string name="groups_member_joined">%s tok del i gruppen</string>
<string name="groups_leave">Forlat gruppe</string>
<string name="groups_leave_dialog_title">Bekreft at du vil forlate gruppen</string>
<string name="groups_leave_dialog_message">Er du sikker på at du vil forlate denne gruppen?</string>
<string name="groups_dissolve">Oppløs gruppe</string>
<string name="groups_dissolve_dialog_title">Bekreft oppløsning av gruppe</string>
<string name="groups_dissolve_dialog_message">Er du sikker på at du vil oppløse denne gruppen?\n\nAlle andre medlemmer vil ikke kunne fortsette deres samtaler, og vil kanskje ikke motta de seneste meldingene.</string>
<string name="groups_dissolve_button">Oppløs</string>
<string name="groups_dissolved_dialog_title">Gruppen har blitt oppløst</string>
<string name="groups_dissolved_dialog_message">Oppretteren av denne gruppen har oppløst den.\n\nDu kan ikke lenger skrive meldinger til gruppen, og vil kanskje ikke få alle poster som har blitt skrevet.</string>
<!--Private Group Invitations-->
<string name="groups_invitations_title">Gruppeinvitasjoner</string>
<string name="groups_invitations_invitation_sent">Du har invitert %1$s til å ta del i gruppen \"%2$s\".</string>
<string name="groups_invitations_invitation_received">%1$s har inviter deg til å ta del i gruppen \"%2$s\".</string>
<string name="groups_invitations_joined">Tok del i gruppe</string>
<string name="groups_invitations_declined">Gruppeinvitasjon avslått</string>
<plurals name="groups_invitations_open">
<item quantity="one">%d åpen gruppeinvitasjon</item>
<item quantity="other">%d åpne gruppeinvitasjoner</item>
</plurals>
<string name="groups_invitations_response_accepted_sent">Du godtok gruppeinvitasjonen fra %s.</string>
<string name="groups_invitations_response_declined_sent">Du avslo gruppeinvitasjoner fra %s.</string>
<string name="groups_invitations_response_accepted_received">%s godtok gruppeinvitasjonen.</string>
<string name="groups_invitations_response_declined_received">%s avslo gruppeinvitasjonen</string>
<string name="sharing_status_groups">Bare grunnleggeren kan invitere nye medlemmer til gruppen. Nedenfor har du alle medlemmene i den.</string>
<!--Private Groups Revealing Contacts-->
<string name="groups_reveal_contacts">Avslør kontakter</string>
<string name="groups_reveal_dialog_message">Du kan velge hvorvidt du vil tilkjennegi kontakter ovenfor alle nåværende og fremtidige medlemmer av denne gruppen.\n\nDette gjør tilkoblingen til gruppen raskere og mer pålitelig, fordi du kan kommunisere med tilkjennegitte kontakter selv om grunnleggeren av gruppen er frakoblet.</string>
<string name="groups_reveal_visible">Kontaktforhold er synlige for gruppen</string>
<string name="groups_reveal_visible_revealed_by_us">Kontaktforhold er synlige for gruppen (avslørt av deg)</string>
<string name="groups_reveal_visible_revealed_by_contact">Kontaktforhold er synlige for gruppen (avslørt av %s)</string>
<string name="groups_reveal_invisible">Kontaktforhold er ikke synlige for gruppen</string>
<!--Forums-->
<string name="no_forums">Du har ingen forum enda.\n\nHvorfor oppretter du ikke et selv ved å trukke på \"+\"-ikonet på toppen?\n\nDu kan også spørre dine kontakter om å dele forum med deg.</string>
<string name="create_forum_title">Opprett forum</string>
<string name="choose_forum_hint">Velg et navn for ditt forum</string>
<string name="create_forum_button">Opprett forum</string>
<string name="forum_created_toast">Forum opprettet</string>
<string name="no_forum_posts">Dette forumet er tomt.\n\nBruk penneikonet på toppen for å føre den første posten i pennen.\n\nEr det ensomt her? Del dette forumet med flere av dine kontakter!</string>
<string name="no_posts">Ingen poster</string>
<plurals name="posts">
<item quantity="one">%d post</item>
<item quantity="other">%d poster</item>
</plurals>
<string name="forum_new_entry_posted">Forumoppføring postet</string>
<string name="forum_new_message_hint">Ny oppføring</string>
<string name="forum_message_reply_hint">Nytt svar</string>
<string name="btn_reply">Svar</string>
<string name="forum_leave">Forlat forum</string>
<string name="dialog_title_leave_forum">Bekreft forlating av forum</string>
<string name="dialog_message_leave_forum">Er du sikker på at du vil forlate dette forumet? Kontakter du har delt dette forumet med kan bli forhindret fra å motta oppdateringer fra dette forumet.</string>
<string name="dialog_button_leave">Forlat</string>
<string name="forum_left_toast">Forlot forum</string>
<!--Forum Sharing-->
<string name="forum_share_button">Del forum</string>
<string name="contacts_selected">Kontakter valgt</string>
<string name="activity_share_toolbar_header">Velg kontakter</string>
<string name="no_contacts_selector">Det later til at du er ny her og ikke har noen kontakter enda.\n\nKom tilbake hit etter at du har lagt til din første kontakt.</string>
<string name="forum_shared_snackbar">Forum delt med valgte kontakter</string>
<string name="forum_share_message">Legg til melding (valgfritt)</string>
<string name="forum_share_error">Feil ved deling av dette forumet.</string>
<string name="forum_invitation_received">%1$s har delt forumet \"%2$s\" med deg.</string>
<string name="forum_invitation_sent">Du har det forumet \"%1$s\" med %2$s.</string>
<string name="forum_invitations_title">Foruminvitasjoner</string>
<string name="forum_invitation_exists">Du godtok invitasjonen til dette forumet allerede. Å godta flere kontakter vil bidra til og styrke kommunikasjonen på forumet.</string>
<string name="forum_joined_toast">Tok del i forumet</string>
<string name="forum_declined_toast">Foruminvitasjon avslått</string>
<string name="shared_by_format">Delt av %s</string>
<string name="forum_invitation_already_sharing">Deler allerede</string>
<string name="forum_invitation_response_accepted_sent">Du godtok foruminvitasjonen fra %s.</string>
<string name="forum_invitation_response_declined_sent">Du avslo foruminvitasjonen fra %s.</string>
<string name="forum_invitation_response_accepted_received">%s godtok foruminvitasjonen.</string>
<string name="forum_invitation_response_declined_received">%s avslo foruminvitasjonen.</string>
<string name="sharing_status">Delingsstatus</string>
<string name="sharing_status_forum">Ethvert medlem av et forum kan dele det med sine kontakter. Du deler dette forumet med følgende kontakter. Det kan også være andre medlemmer du ikke kan se.</string>
<string name="shared_with">Delt med %1$d (%2$d pålogget)</string>
<plurals name="forums_shared">
<item quantity="one">%d forum delt av kontakter</item>
<item quantity="other">%d forum delt av kontakter</item>
</plurals>
<string name="nobody">Ingen</string>
<!--Blogs-->
<string name="blogs_other_blog_empty_state">Denne bloggen er foreløpig tom.\n\nEnten har forfatteren ikke skrevet noe enda, eller så må personen som delte denne bloggen med deg logge på, slik at postene kan synkroniseres.</string>
<string name="read_more">les mer</string>
<string name="blogs_write_blog_post">Skriv bloggpost</string>
<string name="blogs_write_blog_post_body_hint">Skriv din bloggpost her</string>
<string name="blogs_publish_blog_post">Offentliggjør</string>
<string name="blogs_blog_post_created">Bloggpost opprettet</string>
<string name="blogs_blog_post_received">Ny bloggpost opprettet</string>
<string name="blogs_blog_post_scroll_to">Rull til</string>
<string name="blogs_feed_empty_state">Dette er en verdensomspennende bloggstrøm.\n\nDet ser ikke ut til at noen har blogget noe enda.\n\nVær den første til å trykke på pennesymbolet for å føre en blogg i pennen.</string>
<string name="blogs_remove_blog">Fjern blogg</string>
<string name="blogs_remove_blog_dialog_message">Er du sikker på at du ønsker å fjerne denne bloggen og alle bloggposter?\nMerk at dette ikke vil fjerne bloggen fra andre folks enheter.</string>
<string name="blogs_remove_blog_ok">Fjern blogg</string>
<string name="blogs_blog_removed">Blogg fjernet</string>
<string name="blogs_reblog_comment_hint">Legg til en kommentar (valgfritt)</string>
<string name="blogs_reblog_button">Blogg dette</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Del blogg</string>
<string name="blogs_sharing_error">Feil under deling av denne bloggen.</string>
<string name="blogs_sharing_button">Del blogg</string>
<string name="blogs_sharing_snackbar">Blogg delt med valgte kontakter</string>
<string name="blogs_sharing_response_accepted_sent">Du godtok blogg-invitasjonen fra %s.</string>
<string name="blogs_sharing_response_declined_sent">Du avslo blogg-invitasjonen fra %s.</string>
<string name="blogs_sharing_response_accepted_received">%s godtok blogg-invitasjonen.</string>
<string name="blogs_sharing_response_declined_received">%s avslo blogg-invitasjonen.</string>
<string name="blogs_sharing_invitation_received">%1$s har delt bloggen \"%2$s\" med deg.</string>
<string name="blogs_sharing_invitation_sent">Du har delt bloggen \"%1$s\" med %2$s.</string>
<string name="blogs_sharing_invitations_title">Blogginvitasjoner</string>
<string name="blogs_sharing_joined_toast">Abonnerte på blogg</string>
<string name="blogs_sharing_declined_toast">Blogginvitasjon avslått</string>
<string name="sharing_status_blog">Enhver som abonnerer på bloggen kan dele den med sine kontakter. Du deler denne bloggen med følgende kontakter. Det kan også være andre abonnementer du ikke kan se.</string>
<!--RSS Feeds-->
<string name="blogs_rss_feeds_import">Importer RSS-strøm</string>
<string name="blogs_rss_feeds_import_button">Importer</string>
<string name="blogs_rss_feeds_import_hint">Skriv inn nettadresse for RSS-strøm</string>
<string name="blogs_rss_feeds_import_error">Vi beklager! Feil under importering av strøm.</string>
<string name="blogs_rss_feeds_manage">Behandle RSS-strømmer</string>
<string name="blogs_rss_feeds_manage_imported">Importert:</string>
<string name="blogs_rss_feeds_manage_author">Forfatter:</string>
<string name="blogs_rss_feeds_manage_updated">Sist oppdatert:</string>
<string name="blogs_rss_remove_feed">Fjern strøm</string>
<string name="blogs_rss_remove_feed_dialog_message">Er du sikker på at du vil fjerne denne strømmen og alle dens poster?\nAlle poster du har delt vil ikke fjernes fra andre folks enheter.</string>
<string name="blogs_rss_remove_feed_ok">Fjern strøm</string>
<string name="blogs_rss_feeds_manage_delete_error">Strømmen kunne ikke fjernes!</string>
<string name="blogs_rss_feeds_manage_empty_state">Du har ikke importert noen RSS-strømmer enda.\n\nKlikk på \"+\"-tegnet øverst til høyre på skjermen for å legge til din først.</string>
<string name="blogs_rss_feeds_manage_error">Feil ved lasting av dine strømmer. Prøv igjen senere.</string>
<!--Settings Network-->
<string name="network_settings_title">Nettverk</string>
<string name="bluetooth_setting">Koble til via Blåtann</string>
<string name="bluetooth_setting_enabled">Når kontakter er i nærheten</string>
<string name="bluetooth_setting_disabled">Bare når kontakter legges til</string>
<string name="tor_network_setting">Koble til via Tor</string>
<string name="tor_network_setting_never">Aldri</string>
<string name="tor_network_setting_wifi">Bare på Wi-Fi</string>
<string name="tor_network_setting_always">Når Wi-Fi eller mobildata brukes</string>
<!--Settings Security and Panic-->
<string name="security_settings_title">Sikkerhet</string>
<string name="change_password">Endre passord</string>
<string name="current_password">Skriv inn nåværende passord:</string>
<string name="choose_new_password">Velg ditt nye passord:</string>
<string name="confirm_new_password">Bekreft ditt nye passord:</string>
<string name="password_changed">Passordet har blitt endret.</string>
<string name="panic_setting">Oppsett av panikk-knapp</string>
<string name="panic_setting_title">Panikk-knapp</string>
<string name="panic_setting_hint">Sett opp hvordan Briar vil reagere når du bruker panikk-knapp-programmet</string>
<string name="panic_app_setting_title">Panikk-knapp-program</string>
<string name="unknown_app">et ukjent program</string>
<string name="panic_app_setting_summary">Inget program har blitt satt</string>
<string name="panic_app_setting_none">Inget</string>
<string name="dialog_title_connect_panic_app">Bekreft panikk-knapp-program</string>
<string name="dialog_message_connect_panic_app">Er du sikker på at du vil tillate %1$s å utløse destruktive panikk-knapp-handlinger?</string>
<string name="lock_setting_title">Logg ut</string>
<string name="lock_setting_summary">Logg ut av Briar hvis panikk-knappen trykkes</string>
<string name="purge_setting_title">Slett konto</string>
<string name="purge_setting_summary">Slett Briar-kontoen din hvis panikk-knappen trykkes. Advarsel: Dette vil slette dine identiteter, kontakter og meldinger for godt.</string>
<string name="uninstall_setting_title">Avinstaller Briar</string>
<string name="uninstall_setting_summary">Dette krever manuell bekreftelse i panikkfall</string>
<!--Settings Notifications-->
<string name="notification_settings_title">Merknader</string>
<string name="notify_private_messages_setting_title">Private meldinger</string>
<string name="notify_private_messages_setting_summary">Vis varsler for private meldinger</string>
<string name="notify_group_messages_setting_title">Gruppemeldinger</string>
<string name="notify_group_messages_setting_summary">Vis varsler for gruppemeldinger</string>
<string name="notify_forum_posts_setting_title">Forumposter</string>
<string name="notify_forum_posts_setting_summary">Vis varsler for forumposter</string>
<string name="notify_blog_posts_setting_title">Bloggposter</string>
<string name="notify_blog_posts_setting_summary">Vis varsler for bloggposter</string>
<string name="notify_vibration_setting">Vibrer</string>
<string name="notify_lock_screen_setting_title">Låseskjerm</string>
<string name="notify_lock_screen_setting_summary">Vis merknader på låseskjermen</string>
<string name="notify_sound_setting">Lyd</string>
<string name="notify_sound_setting_default">Forvalgt ringetone</string>
<string name="notify_sound_setting_disabled">Ingen</string>
<string name="choose_ringtone_title">Velg ringetone</string>
<!--Settings Feedback-->
<string name="feedback_settings_title">Tilbakemelding</string>
<string name="send_feedback">Send tilbakemelding</string>
<!--Link Warning-->
<string name="link_warning_title">Lenkeadvarsel</string>
<string name="link_warning_intro">Du er i ferd med å følge en lenke med et eksternt program.</string>
<string name="link_warning_text">Dette kan brukes for å identifisere deg. Tenk deg om hvorvidt du stoler på personen som sendte denne lenken til deg, og overvei å åpne den i Orfox.</string>
<string name="link_warning_open_link">Åpne lenke</string>
<!--Crash Reporter-->
<string name="crash_report_title">Kræsjrapport for Briar</string>
<string name="briar_crashed">Beklager, Briar har kræsjet.</string>
<string name="not_your_fault">Dette er ikke din feil.</string>
<string name="please_send_report">Hjelp til med å forbedre Briar ved å sende en kræsjrapport.</string>
<string name="report_is_encrypted">Kræsjrapporten vil sendes kryptert og sikkert.</string>
<string name="feedback_title">Tilbakemelding</string>
<string name="describe_crash">Beskriv hva som skjedde (valgfritt)</string>
<string name="enter_feedback">Skriv inn din tilbakemelding</string>
<string name="optional_contact_email">Din e-postadresse (valgfri)</string>
<string name="include_debug_report_crash">Inkluder anonym data om kræsjet</string>
<string name="include_debug_report_feedback">Inkluder anonym data om denne enheten</string>
<string name="could_not_load_report_data">Kunne ikke laste inn kræsjrapportdata.</string>
<string name="send_report">Send kræsjrapport</string>
<string name="close">Lukk</string>
<string name="dev_report_saved">Kræsjrapport lagret. Den vil bli sendt neste gang du logger inn i Briar.</string>
<!--Sign Out-->
<string name="progress_title_logout">Logger ut av Briar…</string>
<!--Screen Filters & Tapjacking-->
<string name="screen_filter_title">Skjermoverlag oppdaget</string>
<string name="screen_filter_body">Et annet program tegner på toppen av Briar. For å beskytte din sikkerhet, vil Briar ikke reagere på trykk når et annet program tegner over det.\n\nPrøv å skru av følgende programmer når du bruker Briar.\n\n%1$s</string>
</resources>

View File

@@ -0,0 +1,369 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<!--Setup-->
<string name="setup_title">Configuracion de Briar</string>
<string name="setup_explanation">Vòstre compte Briar es salvat e chifrat sus vòstre aparelh, non pas sus en un servidor alunhat \"cloud\". Se desinstalletz Briar o oblidetz vòstre senhal, vòstre compte e vòstras donadas seràn irrecuperables.</string>
<string name="choose_nickname">Causir un escais-nom</string>
<string name="choose_password">Causir un senhal</string>
<string name="confirm_password">Confirmar lo senhal</string>
<string name="name_too_long">Lescais es tròp long</string>
<string name="password_too_weak">Lo senhal es tròp feble</string>
<string name="passwords_do_not_match">Los senhals correspondon pas</string>
<string name="create_account_button">Crear un compte</string>
<!--Login-->
<string name="enter_password">Picatz vòstre senhal:</string>
<string name="try_again">Senhal incorècte, tornatz ensajar</string>
<string name="sign_in_button">Se connectar</string>
<string name="forgotten_password">Senhal oblidat</string>
<string name="dialog_title_lost_password">Senhal oblidat</string>
<string name="dialog_message_lost_password">Vòstre compte Briar es salvat e chifrat sus vòstre aparelh, non pas sus en un servidor alunhat \"cloud\". De fècte podèm pas reïnicializar vòstre senhal.
Volètz suprimir vòstre compte e ne crear un nòu?\n
\nMèfi:vòstra identitat, vòstres contactes e messatges seràn perduts per totjorn.</string>
<string name="startup_failed_notification_title">Briar a pas pogut aviar</string>
<string name="startup_failed_notification_text">Benlèu que vos cal tornar installar Briar.</string>
<string name="startup_failed_activity_title">Fracàs de laviada de Briar.</string>
<string name="startup_failed_db_error">Per una rason desconeguda vòstra basa de donadas Briar es pas utilizabla e irrecuperabla. Vòstres comptes, donadas e contactes son perduts. Per malastre vos cal tornar installar Briar e configurar un compte nòu.</string>
<string name="startup_failed_service_error">Briar a pas pogut aviar un modul necessari. Tornar installar Briar pòt resolver aquò. Que aquò siá dich:perdretz vòstre compte e totas las donadas ligadas a aqueste. Briar utiliza pas de servidor centralizat per salvar sas donadas.</string>
<plurals name="expiry_warning">
<item quantity="one">Aquò es una version Beta de Briar. Vòstre compte sacabarà dins %d jorn e poirà pas èsser renovat.</item>
<item quantity="other">Aquò es una version Beta de Briar. Vòstre compte sacabarà dins %d jorns e poirà pas èsser renovat.</item>
</plurals>
<string name="expiry_date_reached">Vòstre logicial sacabèt.\nMercés daver ensajat!</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Dobrir lo panèl de navigacion</string>
<string name="nav_drawer_close_description">Tampar lo panèl de navigacion</string>
<string name="contact_list_button">Contactes</string>
<string name="groups_button">Grops privats</string>
<string name="forums_button">Fòrums</string>
<string name="blogs_button">Blòges</string>
<string name="settings_button">Paramètres</string>
<string name="sign_out_button">Se desconnectar</string>
<!--Transports-->
<string name="transport_tor">Internet</string>
<string name="transport_bt">Bluetooth</string>
<string name="transport_lan">Wifi</string>
<!--Notifications-->
<string name="ongoing_notification_title">Connectat a Briar</string>
<string name="ongoing_notification_text">Tocatz per dobrir</string>
<plurals name="private_message_notification_text">
<item quantity="one">Novèl messatge privat.</item>
<item quantity="other">%d novèls messatges privats</item>
</plurals>
<plurals name="group_message_notification_text">
<item quantity="one">Novèl messatge gropat</item>
<item quantity="other">%d novèls messatges gropats</item>
</plurals>
<plurals name="forum_post_notification_text">
<item quantity="one">Novèla publicacion al fòrum</item>
<item quantity="other">%d novèlas publicacions al fòrum</item>
</plurals>
<plurals name="blog_post_notification_text">
<item quantity="one">Novèl article sul blòg</item>
<item quantity="other">%d novèls article sul blòg</item>
</plurals>
<!--Misc-->
<string name="now">Ara</string>
<string name="show">Mostrar</string>
<string name="hide">Amagar</string>
<string name="ok">Dacòrdi</string>
<string name="cancel">Anullar</string>
<string name="got_it">Comprés</string>
<string name="delete">Suprimir</string>
<string name="accept">Acceptar</string>
<string name="decline">Refusar</string>
<string name="options">Opcions</string>
<string name="online">En linha</string>
<string name="offline">Fòra linha</string>
<string name="send">Mandar</string>
<string name="allow">Autorizar</string>
<string name="open">Dobrir</string>
<string name="no_data">Pas donada</string>
<string name="ellipsis">...</string>
<string name="text_too_long">Lo tèxte picat es tròp long</string>
<string name="show_onboarding">Mostrar lajuda</string>
<!--Contacts and Private Conversations-->
<string name="no_contacts">Sembla que sètz novèl aquí e quavètz pas cap de contacte.\n\nQuichatz licòna ennaut e seguètz las instruccions per napondre. Informacion:podètz pas quapondre lo monde se se tròban en fàcia de vos, aquò per evitar quòm vos raube lidentitat e que vòstres messatges sián legits per dautres.</string>
<string name="date_no_private_messages">Cap messatge</string>
<string name="no_private_messages">Aquò es la vista de las conversacions.\n\nSembla que ni a pas fòrça.\n\nQuichatz lo camp tèxte per ne començar una.</string>
<string name="message_hint">Picatz lo messatge</string>
<string name="delete_contact">Suprimir lo contacte</string>
<string name="dialog_title_delete_contact">Confirmatz la supression del contacte</string>
<string name="dialog_message_delete_contact">Sètz segur de voler suprimir lo contacte e los messatges ligats a el?</string>
<string name="contact_deleted_toast">Contacte suprimit</string>
<!--Adding Contacts-->
<string name="add_contact_title">Ajustar un contacte</string>
<string name="your_nickname">Triatz lidentitat que volètz utilizar</string>
<string name="face_to_face">Vos cal vos amassar amb la persona que volètz apondre als contactes.\n\n Aquò es fach per evitar quòm vos raube lidentitat e que vòstres messatges sián legits per dautres.</string>
<string name="continue_button">Contunhar</string>
<string name="your_invitation_code">Vòstre còdi de convidacion es</string>
<string name="enter_invitation_code">Mercés de marcar lo còdi de convidacion de vòstre contacte:</string>
<string name="searching_format">Recercar de contactes amb lo còdi de convidacion %06d\u2026</string>
<string name="connection_failed">Fracàs de la connexion</string>
<string name="could_not_find_contact">Briar a pas trobar degun a proximitat</string>
<string name="try_again_button">Tornar ensajar</string>
<string name="connected_to_contact">Connectat al contacte</string>
<string name="calculating_confirmation_code">Calcul del còdi de confirmacion\u2026</string>
<string name="your_confirmation_code">Vòstre còdi de confirmacion es </string>
<string name="enter_confirmation_code">Mercés de marcar lo còdi de confirmacion de vòstre contacte:</string>
<string name="waiting_for_contact">En espèra del contacte\u2026</string>
<string name="waiting_for_contact_to_scan">En espèra de la numerizacion e de la connexion al contacte\u2026</string>
<string name="exchanging_contact_details">Escambi dels detalhs del contacte\u2026</string>
<string name="codes_do_not_match">Los còdis correspondon pas</string>
<string name="interfering">Poriá arribar que qualquun ensajar dinterferir dins la connexion</string>
<string name="contact_added_toast">Contacte apondut:%s</string>
<string name="contact_already_exists">Lo contacte %s existís ja</string>
<string name="contact_exchange_failed">Lescambi de contacte a fracassat</string>
<string name="qr_code_invalid">Lo QR còdi es invalid</string>
<string name="connecting_to_device">Connexion a laparelh\u2026</string>
<string name="authenticating_with_device">Autentificacion amb laparelh\u2026</string>
<string name="connection_aborted_local">Avèm copat la connexion!Poiriá arribar que qualquun ensage dinterferir amb aquela</string>
<string name="connection_aborted_remote">Vòstre contacte a copat la connexion!Poiriá arribar que qualquun ensage dinterferir amb aquela</string>
<!--Introductions-->
<string name="introduction_onboarding_title">Presentatz vòstres contactes</string>
<string name="introduction_onboarding_text">Podètz presentar vòstres contactes, lor caldrà pas se veire per sapondre e se connectar a Briar.</string>
<string name="introduction_activity_title">Seleccionar contacte</string>
<string name="introduction_message_title">Presentar de contactes</string>
<string name="introduction_message_hint">Apondre un messatge (opcional)</string>
<string name="introduction_button">Escafar lintroduccion</string>
<string name="introduction_sent">Vòstra admission es enviada.</string>
<string name="introduction_error">Una error ses produsida pendent ladmission</string>
<string name="introduction_response_error">Error pendent la responsa dadmission.</string>
<string name="introduction_request_sent">Avètz demandat a %2$s dapondre %1$s</string>
<string name="introduction_request_received">%1$s vos prepausa dapondre %2$s. Volètz apondre %2$s a vòstres contactes?</string>
<string name="introduction_request_exists_received">%1$s vos demandèt dapondre %2$s mas %2$s es ja dins vòstra tièra de contactes. Del moment que %1$s o sap pas, podètz çaquelà respondre:</string>
<string name="introduction_request_answered_received">%1$s a demandat dapondre %2$s.</string>
<string name="introduction_response_accepted_sent">Avètz acceptat dapondre %1$s.</string>
<string name="introduction_response_declined_sent">Avètz refusat dapondre %1$s.</string>
<string name="introduction_response_accepted_received">%1$s a acceptat dapondre %2$s.</string>
<string name="introduction_response_declined_received">%1$s a refusat dapondre %2$s.</string>
<string name="introduction_response_declined_received_by_introducee">%1$s anóncia que %2$s a refusat la convidacion.</string>
<plurals name="introduction_notification_text">
<item quantity="one">Nòu contacte ajustat.</item>
<item quantity="other">%d nòus contactes ajustats.</item>
</plurals>
<!--Private Groups-->
<string name="groups_list_empty">Sètz pas sòci de cap grop.\n\nTocatz licòna + ennaut per ne crear un o demandatz a vòstres contactes de vos convidar a un de lors grops.</string>
<string name="groups_created_by">Fondat per %s</string>
<plurals name="messages">
<item quantity="one">1 messatge</item>
<item quantity="other">%d messatges</item>
</plurals>
<string name="groups_group_is_empty">Aqueste grop es void</string>
<string name="groups_group_is_dissolved">Aqueste grop es estat suprimit</string>
<string name="groups_remove">Suprimir</string>
<string name="groups_create_group_title">Crear un grop privat</string>
<string name="groups_create_group_button">Crear un grop</string>
<string name="groups_create_group_invitation_button">Enviar de convidacions</string>
<string name="groups_create_group_hint">Donatz un nom al grop privat</string>
<string name="groups_invitation_sent">Convidacion enviada al grop</string>
<string name="groups_message_sent">Messatge mandat</string>
<string name="groups_member_list">Tièra de membres</string>
<string name="groups_invite_members">Convidar mai de monde</string>
<string name="groups_member_created_you">Avètz creat lo grop</string>
<string name="groups_member_created">%s a fondat lo grop</string>
<string name="groups_member_joined_you">Sètz marcat dins lo grop</string>
<string name="groups_member_joined">%s es marcat dins lo grop</string>
<string name="groups_leave">Quitar lo grop</string>
<string name="groups_leave_dialog_title">Confirmar que volètz quitar lo grop</string>
<string name="groups_leave_dialog_message">Sètz segur de voler quitar lo grop?</string>
<string name="groups_dissolve">Suprimir lo grop</string>
<string name="groups_dissolve_dialog_title">Confirmar la supression del grop</string>
<string name="groups_dissolve_dialog_message">Sètz segur de voler suprimir lo grop? \n\nLa rèsta dels participants poiran pas tenir de charrat e poiriá arribar quajan pas los darrièrs messatges.</string>
<string name="groups_dissolve_button">Suprimir</string>
<string name="groups_dissolved_dialog_title">Lo grop es estat suprimit</string>
<string name="groups_dissolved_dialog_message">Lo fondador daqueste grop la suprimit.\n\nPodètz pas mai escriure de messatges e benlèu quavètz pas recebuts totes los messatges.</string>
<!--Private Group Invitations-->
<string name="groups_invitations_title">Convidacion de grop</string>
<string name="groups_invitations_invitation_sent">Avètz convidat %1$s a venir al grop « %2$vs»</string>
<string name="groups_invitations_invitation_received">%1$s vos a convidat a anar al grop « %2$s»</string>
<string name="groups_invitations_joined">Ajustat al grop</string>
<string name="groups_invitations_declined">Convidacion al grop refusada</string>
<plurals name="groups_invitations_open">
<item quantity="one">%d convidacion de grop en espèra</item>
<item quantity="other">%d convidacions de grop en espèra</item>
</plurals>
<string name="groups_invitations_response_accepted_sent">Avètz acceptat la convidacion del grop a %s.</string>
<string name="groups_invitations_response_declined_sent">Avètz refusat la convidacion del grop a %s.</string>
<string name="groups_invitations_response_accepted_received">%s a acceptat la convidacion del grop.</string>
<string name="groups_invitations_response_declined_received">%s a refusat la convidacion del grop.</string>
<string name="sharing_status_groups">Pas que lo fondador del grop pòt convidar mai de monde. Vaquí la tièra dels membres actuals.</string>
<!--Private Groups Revealing Contacts-->
<string name="groups_reveal_contacts">Revelar los contactes</string>
<string name="groups_reveal_dialog_message">Podètz causir de revelar los contactes a totes los membres actuals e venents daqueste grop.\n\nEn revelar los contactes auretz una melhora connexion al grop e mai fiabla perque poiretz comunicar amb los contactes revelats quand lo fondator es fòra linha.</string>
<string name="groups_reveal_visible">Vòstre ligam amb lo contacte es visible pel grop</string>
<string name="groups_reveal_visible_revealed_by_us">Vòstre ligam revelat per vos amb lo contacte es visible pel grop </string>
<string name="groups_reveal_visible_revealed_by_contact">Vòstre ligam revelat per %s amb lo contacte es invisible pel grop</string>
<string name="groups_reveal_invisible">Vòstre ligam amb lo contacte es invisible pel grop</string>
<!--Forums-->
<string name="no_forums">Avètz pas de fòrum\n\n Perque pas ne crear un en tocar licòna + ennaut?\n\nPodètz tanben demandar a vòstres contactes de partejar de fòrums amb vos.</string>
<string name="create_forum_title">Crear un fòrum</string>
<string name="choose_forum_hint">Donar un nom al fòrum</string>
<string name="create_forum_button">Crear un fòrum</string>
<string name="forum_created_toast">Fòrum creat</string>
<string name="no_forum_posts">Aqueste fòrum es void.\n\nUtilizatz licòna del gredon ennaut per escriure la primièra publicacion.\n\n Vos sentez soleton aquí? Partejatz aqueste fòrum amb dautres contactes!</string>
<string name="no_posts">Cap publicacion</string>
<plurals name="posts">
<item quantity="one">%d publicacion</item>
<item quantity="other">%d publicacions</item>
</plurals>
<string name="forum_new_entry_posted">Publicacions enviadas</string>
<string name="forum_new_message_hint">Nòva publicacion</string>
<string name="forum_message_reply_hint">Nòva responsa</string>
<string name="btn_reply">Respondre</string>
<string name="forum_leave">Quitar lo fòrum</string>
<string name="dialog_title_leave_forum">Confirmar la sortida del fòrum</string>
<string name="dialog_message_leave_forum">Sètz segur de voler quitar lo fòrum? Los contactes quavètz convidats aquí auràn pas mai de mesa a jorn.</string>
<string name="dialog_button_leave">Quitar</string>
<string name="forum_left_toast">Fòrum daissat</string>
<!--Forum Sharing-->
<string name="forum_share_button">Partejar lo fòrum</string>
<string name="contacts_selected">Contactes seleccionats</string>
<string name="activity_share_toolbar_header">Causissètz de contactes</string>
<string name="no_contacts_selector">Semblatz novèl aquí, avètz pas encara de contactes.\n\nTornatz aquí quand auretz apondut vòstre primièr contacte.</string>
<string name="forum_shared_snackbar">Fòrom partejat amb los contactes causits</string>
<string name="forum_share_message">Apondre un messatge (opcional)</string>
<string name="forum_share_error">Error en partejar lo fòrum</string>
<string name="forum_invitation_received">%1$s a partejat lo fòrom « %2$s» amb vos.</string>
<string name="forum_invitation_sent">Avètz partejat lo fòrom « %1$s» amb %2$s.</string>
<string name="forum_invitations_title">Convidacion al fòrum</string>
<string name="forum_invitation_exists">Avètz acceptat una convidacion a aqueste fòrum. En acceptar mai convidacion, fasètz mai fòrta la comunicacion e visibilitat del fòrum.</string>
<string name="forum_joined_toast">Fòrums que participi</string>
<string name="forum_declined_toast">Convidacions refusadas</string>
<string name="shared_by_format">Partejat per %s</string>
<string name="forum_invitation_already_sharing">Ja a partejar</string>
<string name="forum_invitation_response_accepted_sent">Avètz acceptat la convidacion al fòrum de %s.</string>
<string name="forum_invitation_response_declined_sent">Avètz refusat la convidacion al fòrum de %s.</string>
<string name="forum_invitation_response_accepted_received">%s a acceptat la convidacion al fòrum.</string>
<string name="forum_invitation_response_declined_received">%s a refusat la convidacion al fòrum.</string>
<string name="sharing_status">Estat del partatge</string>
<string name="sharing_status_forum">Totes los participants dun fòrum pòdon lo partejar amb lors contactes. Partejatz aqueste fòrum amb los contactes seguents. I a benlèu mai de monde que podètz pas veire.</string>
<string name="shared_with">Partejat amb %1$d (%2$d en linha)</string>
<plurals name="forums_shared">
<item quantity="one">%d fòrum partejat amb de contactes</item>
<item quantity="other">%d fòrums partejats amb de contactes</item>
</plurals>
<string name="nobody">Degun</string>
<!--Blogs-->
<string name="blogs_other_blog_empty_state">Aqueste blòg es void pel moment. Doas possibilitats:lautor a pas encara escrich quicòm o alara la persona que vos a marcat al fòrum es pas en linha e la sincronizacion es pas encara realizada.</string>
<string name="read_more">Legir la seguida</string>
<string name="blogs_write_blog_post">Escriure un article de blòg</string>
<string name="blogs_write_blog_post_body_hint">Marcatz aquí vòstre messatge pel blòg</string>
<string name="blogs_publish_blog_post">Publicar</string>
<string name="blogs_blog_post_created">Article salvat</string>
<string name="blogs_blog_post_received">Nòu article recebut</string>
<string name="blogs_blog_post_scroll_to">Anar a</string>
<string name="blogs_feed_empty_state">Aquò es lo flux global dels blògs.\n\n Sembla que degun aja escrich aquí.\n\n Siatz lo primièr, tocatz licòna de lestilò per escriure un article al blòg.</string>
<string name="blogs_remove_blog">Suprimir lo blòg</string>
<string name="blogs_remove_blog_dialog_message">Sètz segur de voler suprimir aqueste blòg e totes sos messatges?\n Aquò suprimirà pas lo blòg suls aparelhs dels autres.</string>
<string name="blogs_remove_blog_ok">Suprimir lo blòg</string>
<string name="blogs_blog_removed">Blòg mandat al diable</string>
<string name="blogs_reblog_comment_hint">Apondre un comentari (opcional)</string>
<string name="blogs_reblog_button">Partejar</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Partejar lo blòg</string>
<string name="blogs_sharing_error">Error en partejar lo blòg</string>
<string name="blogs_sharing_button">Fai virar</string>
<string name="blogs_sharing_snackbar">Blòg partejat amb los contactes seleccionats</string>
<string name="blogs_sharing_response_accepted_sent">Avètz acceptat la convidacion al blòg a %s.</string>
<string name="blogs_sharing_response_declined_sent">Avètz refusat la convidacion al blòg a %s.</string>
<string name="blogs_sharing_response_accepted_received">%s a acceptat la convidacion al blòg.</string>
<string name="blogs_sharing_response_declined_received">%s a refusat la convidacion al blòg.</string>
<string name="blogs_sharing_invitation_received">%1$s a partejat lo blòg « %2$s» amb vos.</string>
<string name="blogs_sharing_invitation_sent">Avètz partejat lo blòg « %1$s» amb %2$s.</string>
<string name="blogs_sharing_invitations_title">Convidacion al blòg</string>
<string name="blogs_sharing_joined_toast">Seguidors del blòg</string>
<string name="blogs_sharing_declined_toast">Convidacions al blòg refusadas</string>
<string name="sharing_status_blog">Qual que siá seguidor dun blòg pòt lo partejar amb sos contactes. Partejatz lo blòg amb los contactes seguents. I a benlèu mai de monde quo seguen que vesètz pas.</string>
<!--RSS Feeds-->
<string name="blogs_rss_feeds_import">Importar de fluxes RSS</string>
<string name="blogs_rss_feeds_import_button">Importar</string>
<string name="blogs_rss_feeds_import_hint">Marcar lURL del flux RSS</string>
<string name="blogs_rss_feeds_import_error">Una error ses produisida en importar lo flux</string>
<string name="blogs_rss_feeds_manage">Gerir lo flux RSS</string>
<string name="blogs_rss_feeds_manage_imported">Importat:</string>
<string name="blogs_rss_feeds_manage_author">Autor:</string>
<string name="blogs_rss_feeds_manage_updated">Darrièra mesa a jorn:</string>
<string name="blogs_rss_remove_feed">Suprimir lo flux</string>
<string name="blogs_rss_remove_feed_dialog_message">Sètz segur de voler suprimir aqueste flux e sos messatges?\nLos messatges quavètz partejat seràn pas suprimits dels aparelhs dels autres.</string>
<string name="blogs_rss_remove_feed_ok">Suprimir lo flux</string>
<string name="blogs_rss_feeds_manage_delete_error">Impossible de suprimir lo flux!</string>
<string name="blogs_rss_feeds_manage_empty_state">Avètz pas importat cap flux RSS.\n\nPerque pas napondre un en clicar sul pichon + a man dreita ennaut?</string>
<string name="blogs_rss_feeds_manage_error">Error en cargar vòstres fluxes. Ensajatz mai tard.</string>
<!--Settings Network-->
<string name="network_settings_title">Ret</string>
<string name="bluetooth_setting">Se connectar per Bluetooth</string>
<string name="bluetooth_setting_enabled">Quand de contactes son prèp</string>
<string name="bluetooth_setting_disabled">Solament en apondre de contctes</string>
<string name="tor_network_setting">Se connectar via Tor</string>
<string name="tor_network_setting_never">Pas jamai</string>
<string name="tor_network_setting_wifi">Solament per wifi</string>
<string name="tor_network_setting_always">Per wifi o donadas mobil</string>
<!--Settings Security and Panic-->
<string name="security_settings_title">Seguretat</string>
<string name="change_password">Modificar lo senhal</string>
<string name="current_password">Marcatz lo senhal actual:</string>
<string name="choose_new_password">Causissètz lo novèl senhal:</string>
<string name="confirm_new_password">Confirmatz lo novèl senhal:</string>
<string name="password_changed">Lo senhal a cambiat</string>
<string name="panic_setting">Paramètres del boton urgéncia</string>
<string name="panic_setting_title">Boton urgéncia</string>
<string name="panic_setting_hint">Configurar Briat per lutilizacion de laplicacion del boton durgéncia</string>
<string name="panic_app_setting_title">Aplicacion boton urgéncia</string>
<string name="unknown_app">Aplicacion desconeguda</string>
<string name="panic_app_setting_summary">Cap aplicacion causida</string>
<string name="panic_app_setting_none">Cap</string>
<string name="dialog_title_connect_panic_app">Confirmar laplicacion Urgéncia</string>
<string name="dialog_message_connect_panic_app">Sètz segur de voler autorizar %1$s a aviar las accions de destruccion del boton Urgéncia?</string>
<string name="lock_setting_title">Se desconnectar</string>
<string name="lock_setting_summary">Se desconnectar de Briar en apiejant lo boton Urgéncia?</string>
<string name="purge_setting_title">Suprimir lo compte</string>
<string name="purge_setting_summary">Suprimir vòstre compte en apiejant lo boton Urgéncia. Mèfi,aquò escafarà per totjorn vòstra identitat, vòstres contactes e messatges.</string>
<string name="uninstall_setting_title">Desinstallar Briar</string>
<string name="uninstall_setting_summary">Aquò demanda una confirmacion manuala.</string>
<!--Settings Notifications-->
<string name="notification_settings_title">Notificacions</string>
<string name="notify_private_messages_setting_title">Messatges privats</string>
<string name="notify_private_messages_setting_summary">Notificar los messatges privats</string>
<string name="notify_group_messages_setting_title">Messatges gropats</string>
<string name="notify_group_messages_setting_summary">Notificar los messatges gropats</string>
<string name="notify_forum_posts_setting_title">Publicacions al fòrum</string>
<string name="notify_forum_posts_setting_summary">Notificar las publicacions al fòrum</string>
<string name="notify_blog_posts_setting_title">Articles de blòg</string>
<string name="notify_blog_posts_setting_summary">Notificar los articles de blòg</string>
<string name="notify_vibration_setting">Vibracion</string>
<string name="notify_lock_screen_setting_title">Verrolhar lecran</string>
<string name="notify_lock_screen_setting_summary">Mostrar las notificacions sus lecran verrolhat</string>
<string name="notify_sound_setting">Sonariá</string>
<string name="notify_sound_setting_default">Sonariá per defaut</string>
<string name="notify_sound_setting_disabled">Cap</string>
<string name="choose_ringtone_title">Causir la sonariá</string>
<!--Settings Feedback-->
<string name="feedback_settings_title">Comentaris</string>
<string name="send_feedback">Enviar vòstres comentaris</string>
<!--Link Warning-->
<string name="link_warning_title">Avertiment de ligam</string>
<string name="link_warning_intro">Sètz a man de dobrir lo ligam seguent amb una aplicacion extèrna.</string>
<string name="link_warning_text">Aquò poiriá èsser utilizat per vos identificar. Estimatz la fisança quavètz de la persona que vos a enviat lo ligam e pensatz lo dobrir amb Orfox.</string>
<string name="link_warning_open_link">Dobrir lo ligam</string>
<!--Crash Reporter-->
<string name="crash_report_title">Senhalar lo fracàs de Briar</string>
<string name="briar_crashed">Malastre, Briar a arrestat de foncionar.</string>
<string name="not_your_fault">Es pas vòstra fauta.</string>
<string name="please_send_report">Ajudatz-nos a melhorar Briar en nos enviant un rapòrt de fracàs.</string>
<string name="report_is_encrypted">Asseguram que lo rapòrt es chifrat e enviat en tota seguretat.</string>
<string name="feedback_title">Comentaris</string>
<string name="describe_crash">Describètz çò que ses passat</string>
<string name="enter_feedback">Marcatz vòstres comentaris</string>
<string name="optional_contact_email">Vòstre corrièl (opcional)</string>
<string name="include_debug_report_crash">Ajustar de donas anonimas tocant lo fracàs</string>
<string name="include_debug_report_feedback">Ajustar de donas anonimas tocant aqueste periferic</string>
<string name="could_not_load_report_data">Impossible de cargar las donadas del rapòrt.</string>
<string name="send_report">Enviar lo rapòrt</string>
<string name="close">Tampar</string>
<string name="dev_report_saved">Rapòrt salvat. Serà enviat a vòstra connexion venenta a Briar.</string>
<!--Sign Out-->
<string name="progress_title_logout">Desconnecion de Briar...</string>
<!--Screen Filters & Tapjacking-->
<string name="screen_filter_title">Superposicion detectada</string>
<string name="screen_filter_body">Una autra aplicacion saficha per dessús Briar. Per protegir vòstre seguretat Briar respond pas quand una autra aplicacion saficha par dessús.\n\nEnsajatz de tampar las aplicacions seguentas quand utilizatz Briar:\n\n%1$s</string>
</resources>

View File

@@ -22,7 +22,7 @@
<string name="startup_failed_activity_title">Inicialização do Briar falhou</string>
<string name="startup_failed_db_error">Por alguma razão, seus dados do Briar estão corrompidos e não podem ser reparados. Sua conta, seus dados e todas suas conexões com contatos estão perdidas. Infelizmente você terá que resintalar o Briar e criar uma nova conta.</string>
<string name="startup_failed_service_error">O Briar não pode iniciar devido a um plugin. Reinstalar o Briar geralmente resolve esse problema. Porém, note que ao fazer isso você perderá sua conta e todos os dados associados a ela, já que o Briar não usa um servidor central para armazenar seus dados.</string>
<string name="expiry_warning">Esse programa expirou.\nPor favor instale uma versão mais recente.</string>
<string name="expiry_date_reached">Este software expirou.\nObrigado por testar!</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Abrir aba de navegação</string>
<string name="nav_drawer_close_description">Fechar aba de navegação</string>
@@ -117,6 +117,7 @@
<string name="introduction_onboarding_text">Você pode apresentar seus contatos entre si, assim eles não precisam se encontrar pessoalmente para se comunicar no Briar.</string>
<string name="introduction_activity_title">Selecionar contato</string>
<string name="introduction_message_title">Apresentar contatos</string>
<string name="introduction_message_hint">Adicione uma mensagem (opcional)</string>
<string name="introduction_button">Fazer apresentação</string>
<string name="introduction_sent">Sua Introdução foi enviada.</string>
<string name="introduction_error">Houve um erro ao fazer a apresentação</string>
@@ -147,6 +148,7 @@
<string name="groups_create_group_title">Criar Grupo Privado</string>
<string name="groups_create_group_button">Criar Grupo</string>
<string name="groups_create_group_invitation_button">Enviar Convite</string>
<string name="groups_create_group_hint">Escolha um nome para o seu grupo privado</string>
<string name="groups_invitation_sent">Convite do Grupo enviado </string>
<string name="groups_message_sent">Mensagem enviada</string>
<string name="groups_member_list">Lista de membros</string>
@@ -178,6 +180,7 @@
<string name="groups_invitations_response_declined_sent">Você recusou o convite do Grupo de %s.</string>
<string name="groups_invitations_response_accepted_received">%s aceitou o convite do Grupo.</string>
<string name="groups_invitations_response_declined_received">%s recusou o convite do Grupo.</string>
<string name="sharing_status_groups">Apenas o criador pode convidar novas pessoas para o grupo. Abaixo estão os atuais membros do grupo.</string>
<!--Private Groups Revealing Contacts-->
<string name="groups_reveal_contacts">Revelar contatos</string>
<string name="groups_reveal_dialog_message">Você pode escolher revelar seus contatos para todos os membros presentes e futuros desse Grupo.\n\nRevelar seus contatos torna sua conexão com o grupo mais rápida e confiável, porque você pode comunicar com os contatos revelados mesmo se o criador do Grupo está offline</string>
@@ -187,6 +190,8 @@
<string name="groups_reveal_invisible">Relação de contatos não visível para o Grupo </string>
<!--Forums-->
<string name="no_forums">Você não tem nenhum fórum.\nPor que não cria um novo você mesmo, Clicando o ícone + no topo?\nVocê também pode pedir que seus contatos compartilhem fóruns com você.</string>
<string name="create_forum_title">Criar Fórum</string>
<string name="choose_forum_hint">Escolha um nome para o seu fórum</string>
<string name="create_forum_button">Criar fórum</string>
<string name="forum_created_toast">Fórum criado</string>
<string name="no_forum_posts">Esse fórum está vazio.\nUse o ícone da caneta no topo para escrever o primeiro Post.\nSe sentido sozinho aqui? Compartilhe esse fórum com seus contatos!</string>
@@ -210,6 +215,7 @@
<string name="activity_share_toolbar_header">Escolher contatos</string>
<string name="no_contacts_selector">Parece que você é novo aqui e não tem nenhum contato ainda.\nPor favor volte aqui depois de adicionar um contato.</string>
<string name="forum_shared_snackbar">Fòrum compartilhado com os contatos escolhidos</string>
<string name="forum_share_message">Adicionar uma mensagem (opcional)</string>
<string name="forum_share_error">Houve um erro em compartilhar esse fórum.</string>
<string name="forum_invitation_received">%1$s compartilhou o fórum \"%2$s\" com você.</string>
<string name="forum_invitation_sent">Você compartilhou o fórum \"%1$s\" com %2$s.</string>
@@ -224,6 +230,7 @@
<string name="forum_invitation_response_accepted_received">%s aceitou o convite de fórum.</string>
<string name="forum_invitation_response_declined_received">%s recusou o convite de fórum.</string>
<string name="sharing_status">Status de compartilhamento</string>
<string name="sharing_status_forum">Qualquer membro do fórum pode compartilhá-lo com seus contatos. Você está compartilhando este fórum com os seguintes contatos. Também podem existir outros membros que você não consegue visualizar.</string>
<string name="shared_with">Compartilhado com %1$d (%2$d online)</string>
<plurals name="forums_shared">
<item quantity="one">%d fórum compartilhado por contatos</item>
@@ -240,11 +247,11 @@
<string name="blogs_blog_post_received">Novo Post de Blog recebido</string>
<string name="blogs_blog_post_scroll_to">Role Para</string>
<string name="blogs_feed_empty_state">Esse é o feed de blog global.\nParece que ninguém postou nada, ainda.\nSeja o primeiro a postar e clique no ícone da caneta para escrever um novo Post pro Blog.</string>
<string name="blogs_personal_blog">Blog pessoal do(a) %s</string>
<string name="blogs_remove_blog">Remover Blog</string>
<string name="blogs_remove_blog_dialog_message">Você ter certeza que quer remover esse Blog e todos seus Posts?\nNote que isso não irá remover o Blog dos dispositivos de outras pessoas. </string>
<string name="blogs_remove_blog_ok">Remover Blog</string>
<string name="blogs_blog_removed">Blog Deletado</string>
<string name="blogs_reblog_comment_hint">Adicionar um comentário (opcional)</string>
<string name="blogs_reblog_button">Reblog</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Compartilhar Blog</string>
@@ -255,11 +262,12 @@
<string name="blogs_sharing_response_declined_sent">Você recusou o convite de Blog de %s.</string>
<string name="blogs_sharing_response_accepted_received">%s aceitou o convite de Blog.</string>
<string name="blogs_sharing_response_declined_received">%s recusou o convite de Fórum.</string>
<string name="blogs_sharing_invitation_received">%1$s compartilhou o blog %2$s com você.</string>
<string name="blogs_sharing_invitation_sent">Você compartilhou o blog de %1$s com %2$s.</string>
<string name="blogs_sharing_invitation_received">%1$s compartilhou o blog \"%2$s\" com você.</string>
<string name="blogs_sharing_invitation_sent">Você compartilhou o blog \"%1$s\" com %2$s.</string>
<string name="blogs_sharing_invitations_title">Convites para Fóruns</string>
<string name="blogs_sharing_joined_toast">Inscrito neste Blog</string>
<string name="blogs_sharing_declined_toast">Convite do Blog recusado</string>
<string name="sharing_status_blog">Qualquer pessoa que se inscreva em um blog pode compartilhá-lo com seus contatos. Você está compartilhando este blog com os seguintes contatos. Também podem haver outras pessoas inscritas que você não consegue visualizar.</string>
<!--RSS Feeds-->
<string name="blogs_rss_feeds_import">Importar Feed RSS</string>
<string name="blogs_rss_feeds_import_button">Importar</string>
@@ -269,6 +277,9 @@
<string name="blogs_rss_feeds_manage_imported">Importado:</string>
<string name="blogs_rss_feeds_manage_author">Autor:</string>
<string name="blogs_rss_feeds_manage_updated">Última Atualização:</string>
<string name="blogs_rss_remove_feed">Remover Feed</string>
<string name="blogs_rss_remove_feed_dialog_message">Você tem certeza que quer remover este feed e todas suas postagens?\nAs postagens que você compartilhou não serão removidas dos dispositivos de outras pessoas.</string>
<string name="blogs_rss_remove_feed_ok">Remover Feed</string>
<string name="blogs_rss_feeds_manage_delete_error">O Feed não pode ser deletado!</string>
<string name="blogs_rss_feeds_manage_empty_state">Você ainda não importou nenhum feed RSS. Por que não clica no símbolo de mais no canto superior direito para adicionar o primeiro? </string>
<string name="blogs_rss_feeds_manage_error">Houve um problema ao carregar seus Feeds. Por favor tente novamente.</string>
@@ -277,6 +288,10 @@
<string name="bluetooth_setting">Conectar via Bluetooth</string>
<string name="bluetooth_setting_enabled">Sempre que estiver perto de contatos</string>
<string name="bluetooth_setting_disabled">Somente quando adicionando contatos</string>
<string name="tor_network_setting">Conectar via Tor</string>
<string name="tor_network_setting_never">Nunca</string>
<string name="tor_network_setting_wifi">Apenas ao usar Wi-Fi</string>
<string name="tor_network_setting_always">Quando usar Wi-Fi ou dados móveis</string>
<!--Settings Security and Panic-->
<string name="security_settings_title">Segurança</string>
<string name="change_password">Mudar Senha</string>
@@ -301,7 +316,17 @@
<string name="uninstall_setting_summary">Isso requer configuração manual em um evento de pânico</string>
<!--Settings Notifications-->
<string name="notification_settings_title">Notificações</string>
<string name="notify_private_messages_setting_title">Mensagens privadas</string>
<string name="notify_private_messages_setting_summary">Mostrar alertas para mensagens privadas</string>
<string name="notify_group_messages_setting_title">Mensagens de Grupo</string>
<string name="notify_group_messages_setting_summary">Mostrar alertas para mensagens de Grupos</string>
<string name="notify_forum_posts_setting_title">Postagens do fórum</string>
<string name="notify_forum_posts_setting_summary">Mostrar alertas para postagens nos fóruns</string>
<string name="notify_blog_posts_setting_title">Posts do blog</string>
<string name="notify_blog_posts_setting_summary">Mostrar alertas para postagens nos blogs</string>
<string name="notify_vibration_setting">Vibrar</string>
<string name="notify_lock_screen_setting_title">Bloquear a tela</string>
<string name="notify_lock_screen_setting_summary">Mostrar notificações enquanto a tela estiver bloqueada</string>
<string name="notify_sound_setting">Som</string>
<string name="notify_sound_setting_default">Toque padrão</string>
<string name="notify_sound_setting_disabled">Nenhum</string>
@@ -333,4 +358,5 @@
<!--Sign Out-->
<string name="progress_title_logout">Saindo do Briar…</string>
<!--Screen Filters & Tapjacking-->
<string name="screen_filter_body">Outro aplicativo está rodando por cima do Briar. Para proteger sua segurança, o Briar não irá responder por toques quando outro aplicativo estiver por cima.\n\nTente desligar os seguintes aplicativos quando for usar o Briar:\n\n %1$s</string>
</resources>

View File

@@ -0,0 +1,388 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<!--Setup-->
<string name="setup_title">Настройка Briar</string>
<string name="setup_explanation">Ваша учетная запись Briar хранится в зашифрованном виде только на устройстве. Если вы удалите Briar или забудете пароль, то не сможете восстановить свою учетную запись и данные.</string>
<string name="choose_nickname">Выберите псевдоним</string>
<string name="choose_password">Выберите пароль</string>
<string name="confirm_password">Подтвердите пароль</string>
<string name="name_too_long">Слишком длинное имя</string>
<string name="password_too_weak">Пароль слишком слабый</string>
<string name="passwords_do_not_match">Пароли не совпадают</string>
<string name="create_account_button">Создать учетную запись</string>
<!--Login-->
<string name="enter_password">Введите пароль:</string>
<string name="try_again">Неверный пароль, повторите попытку</string>
<string name="sign_in_button">Войти</string>
<string name="forgotten_password">Я забыл свой пароль</string>
<string name="dialog_title_lost_password">Потерянный пароль</string>
<string name="dialog_message_lost_password">Ваша учетная запись Briar хранится в зашифрованном виде только на устройстве, поэтому мы не можем сбросить пароль. Удалить учетную запись и начать заново?\n\nВнимание: ваши идентификаторы, контакты и сообщения будут потеряны навсегда.</string>
<string name="startup_failed_notification_title">Не удалось запустить Briar</string>
<string name="startup_failed_notification_text">Возможно, потребуется переустановить Briar.</string>
<string name="startup_failed_activity_title">Сбой при запуске Briar</string>
<string name="startup_failed_db_error">По какой-то причине, ваша база данных Briar повреждена без возможности восстановления. Ваша учетная запись, ваши данные и все ваши связи с контактами потеряны.
К сожалению, необходимо переустановить Briar и настроить новую учетную запись.</string>
<string name="startup_failed_service_error">Briar не смог запустить требуемый подключаемый модуль. Переустановка Briar обычно решает эту проблему. Однако обратите внимание, что после этого вы потеряете свою учетную запись и все связанные с ней данные, поскольку Briar не использует центральных серверов для хранения данных.</string>
<plurals name="expiry_warning">
<item quantity="one">Это бета-версия Briar. Срок действия вашей учетной записи истекает через %d день и не может быть продлен.</item>
<item quantity="few">Это бета-версия Briar. Срок действия вашей учетной записи истекает через %d дней и не может быть продлен.</item>
<item quantity="many">Это бета-версия Briar. Срок действия вашей учетной записи истекает через %d дней и не может быть продлен.</item>
<item quantity="other">Это бета-версия Briar. Срок действия вашей учетной записи истекает через %d дней и не может быть продлен.</item>
</plurals>
<string name="expiry_date_reached">Срок действия этого программного обеспечения истек.\nСпасибо за тестирование!</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Открыть навигационное меню</string>
<string name="nav_drawer_close_description">Закрыть навигационное меню</string>
<string name="contact_list_button">Контакты</string>
<string name="groups_button">Приватные группы</string>
<string name="forums_button">Форумы</string>
<string name="blogs_button">Блоги</string>
<string name="settings_button">Настройки</string>
<string name="sign_out_button">Выйти</string>
<!--Transports-->
<string name="transport_tor">Интернет</string>
<string name="transport_bt">Bluetooth</string>
<string name="transport_lan">Wi-Fi</string>
<!--Notifications-->
<string name="ongoing_notification_title">Выполнен вход в Briar</string>
<string name="ongoing_notification_text">Коснитесь, чтобы открыть Briar</string>
<plurals name="private_message_notification_text">
<item quantity="one">Новое личное сообщение.</item>
<item quantity="few">%d новых личных сообщений.</item>
<item quantity="many">%d новых личных сообщений.</item>
<item quantity="other">%d новых личных сообщений.</item>
</plurals>
<plurals name="group_message_notification_text">
<item quantity="one">Новое групповое сообщение</item>
<item quantity="few">%d новых групповых сообщений.</item>
<item quantity="many">%d новых групповых сообщений.</item>
<item quantity="other">%d новых групповых сообщений.</item>
</plurals>
<plurals name="forum_post_notification_text">
<item quantity="one">Новый пост на форуме</item>
<item quantity="few">%d новых постов на форуме.</item>
<item quantity="many">%d новых постов на форуме.</item>
<item quantity="other">%d новых постов на форуме.</item>
</plurals>
<plurals name="blog_post_notification_text">
<item quantity="one">Новый пост в блоге.</item>
<item quantity="few">%d новых постов в блоге.</item>
<item quantity="many">%d новых постов в блоге.</item>
<item quantity="other">%d новых постов в блоге.</item>
</plurals>
<!--Misc-->
<string name="now">сейчас</string>
<string name="show">Показать</string>
<string name="hide">Скрыть</string>
<string name="ok">OK</string>
<string name="cancel">Отмена</string>
<string name="got_it">Получено</string>
<string name="delete">Удалить</string>
<string name="accept">Принять</string>
<string name="decline">Отклонить</string>
<string name="options">Опции</string>
<string name="online">В сети</string>
<string name="offline">Не в сети</string>
<string name="send">Отправить</string>
<string name="allow">Разрешить</string>
<string name="open">Открыть</string>
<string name="no_data">Нет данных</string>
<string name="ellipsis"></string>
<string name="text_too_long">Введенный текст слишком длинный</string>
<string name="show_onboarding">Показать справку</string>
<!--Contacts and Private Conversations-->
<string name="no_contacts">Видимо, вы здесь новенький и пока что не имеете контактов.\n\nКоснитесь значка + вверху и следуйте инструкциям, чтобы добавить друзей в список.\n\nПомните: вы можете добавлять новые контакты только лично, чтобы никто не выдал себя за вас и не мог читать ваши сообщения.</string>
<string name="date_no_private_messages">Нет сообщений.</string>
<string name="no_private_messages">Это представление беседы.\n\nКажется, у нас нет беседы.\n\nПросто коснитесь поля ввода внизу, чтобы начать беседу.</string>
<string name="message_hint">Печатает сообщение</string>
<string name="delete_contact">Удалить контакт</string>
<string name="dialog_title_delete_contact">Подтвердите удаление контакта</string>
<string name="dialog_message_delete_contact">Вы действительно хотите удалить этот контакт и все связанные с ним сообщения?</string>
<string name="contact_deleted_toast">Контакт удален</string>
<!--Adding Contacts-->
<string name="add_contact_title">Добавить контакт</string>
<string name="your_nickname">Выберите идентификатор, который хотите использовать:</string>
<string name="face_to_face">Необходимо встретиться с человеком, который требуется добавить в качестве контакта.\n\nЭто не позволит кому-либо выдать себя за вас или читать ваши сообщения.</string>
<string name="continue_button">Продолжить</string>
<string name="your_invitation_code">Ваш код приглашения</string>
<string name="enter_invitation_code">Введите код приглашения вашего контакта:</string>
<string name="searching_format">Поиск контакта с кодом приглашения %06d\u2026</string>
<string name="connection_failed">Ошибка подключения</string>
<string name="could_not_find_contact">Briar не смог найти ваш контакт поблизости</string>
<string name="try_again_button">Попробовать снова</string>
<string name="connected_to_contact">Подключено к контакту</string>
<string name="calculating_confirmation_code">Вычисление кода подтверждения\u2026</string>
<string name="your_confirmation_code">Ваш код подтвержения</string>
<string name="enter_confirmation_code">Введите код подтверждения контакта:</string>
<string name="waiting_for_contact">Ожидание контакта\u2026</string>
<string name="waiting_for_contact_to_scan">Ожидание контакта для сканирования и подключения\u2026</string>
<string name="exchanging_contact_details">Обмен контактными данными\u2026</string>
<string name="codes_do_not_match">Коды не совпадают</string>
<string name="interfering">Это может означать, что кто-то пытается вмешаться в ваше подключение</string>
<string name="contact_added_toast">Контакт добавлен: %s</string>
<string name="contact_already_exists">Контакт %s уже существует</string>
<string name="contact_exchange_failed">Обмен контактами не удался</string>
<string name="qr_code_invalid">Неверный QR-код</string>
<string name="connecting_to_device">Подключение к устройству\u2026</string>
<string name="authenticating_with_device">Аутентификация с устройством\u2026</string>
<string name="connection_aborted_local">Соединение прервано нами! Это может означать, что кто-то пытается вмешаться в ваше подключение</string>
<string name="connection_aborted_remote">Соединение прервано вашим контактом! Это может означать, что кто-то пытается вмешаться в ваше подключение</string>
<!--Introductions-->
<string name="introduction_onboarding_title">Знакомство с контактами</string>
<string name="introduction_onboarding_text">Вы можете представить свои контакты друг другу, поэтому им не нужно встречаться лично, чтобы подключиться к Briar. </string>
<string name="introduction_activity_title">Выберите контакт</string>
<string name="introduction_message_title">Познакомить контакты</string>
<string name="introduction_message_hint">Добавить сообщение (необязательно)</string>
<string name="introduction_button">Выполнить знакомство</string>
<string name="introduction_sent">Ваше представление было отправлено.</string>
<string name="introduction_error">Произошла ошибка при выполнении знакомства.</string>
<string name="introduction_response_error">Ошибка при ответе на знакомство</string>
<string name="introduction_request_sent">Вы хотели познакомить %1$s с %2$s.</string>
<string name="introduction_request_received">%1$s попросил вас представить %2$s. Вы хотите добавить %2$s в ваш список контактов?</string>
<string name="introduction_request_exists_received">%1$s попросил вас представить %2$s, но %2$s уже находится в вашем списке контактов. Поскольку %1$s может не знать об этом, вы все равно можете ответить:</string>
<string name="introduction_request_answered_received">%1$s попросил вас представить %2$s.</string>
<string name="introduction_response_accepted_sent">Вы приняли знакомство с %1$s.</string>
<string name="introduction_response_declined_sent">Вы отказались от знакомства с %1$s.</string>
<string name="introduction_response_accepted_received">%1$s принял знакомство с %2$s.</string>
<string name="introduction_response_declined_received">%1$s отказался от знакомства с %2$s.</string>
<string name="introduction_response_declined_received_by_introducee">%1$s говорит, что %2$s отказался от знакомства.</string>
<plurals name="introduction_notification_text">
<item quantity="one">Новый контакт добавлен.</item>
<item quantity="few">%d новых контактов добавлено.</item>
<item quantity="many">%d новых контактов добавлено.</item>
<item quantity="other">%d новых контактов добавлено.</item>
</plurals>
<!--Private Groups-->
<string name="groups_list_empty">Вы не состоите в каких-либо группах.\n\nКоснитесь значка + вверху, чтобы создать группу самостоятельно или попросить ваши контакты пригласить их в одну из своих групп.</string>
<string name="groups_created_by">Создано %s</string>
<plurals name="messages">
<item quantity="one">%d сообщение</item>
<item quantity="few">%d сообщений</item>
<item quantity="many">%d сообщений</item>
<item quantity="other">%d сообщений</item>
</plurals>
<string name="groups_group_is_empty">Эта группа пуста</string>
<string name="groups_group_is_dissolved">Эта группа была распущена</string>
<string name="groups_remove">Удалить</string>
<string name="groups_create_group_title">Создать приватную группу</string>
<string name="groups_create_group_button">Создать группу</string>
<string name="groups_create_group_invitation_button">Отправить приглашение</string>
<string name="groups_create_group_hint">Выберите имя для своей приватной группы</string>
<string name="groups_invitation_sent">Приглашение на вступление в группу было отправлено</string>
<string name="groups_message_sent">Сообщение отправлено</string>
<string name="groups_member_list">Список участников</string>
<string name="groups_invite_members">Пригласить участников</string>
<string name="groups_member_created_you">Вы создали группу</string>
<string name="groups_member_created">%s создал группу</string>
<string name="groups_member_joined_you">Вы присоединились к группе</string>
<string name="groups_member_joined">%s присоединился к группе</string>
<string name="groups_leave">Покинуть группу</string>
<string name="groups_leave_dialog_title">Подтверждение</string>
<string name="groups_leave_dialog_message">Вы уверены, что хотите покинуть эту группу?</string>
<string name="groups_dissolve">Распустить группу</string>
<string name="groups_dissolve_dialog_title">Подтверждение</string>
<string name="groups_dissolve_dialog_message">Вы уверены, что хотите распустить эту группу?\n\nДругие участники не смогут продолжить разговор и могут не получить последние сообщения.</string>
<string name="groups_dissolve_button">Распустить</string>
<string name="groups_dissolved_dialog_title">Группа была распущена</string>
<string name="groups_dissolved_dialog_message">Создатель этой группы распустил ее.\n\nВы больше не можете писать сообщения в группу и можете получить не все сообщения, которые были написаны.</string>
<!--Private Group Invitations-->
<string name="groups_invitations_title">Приглашения в группу</string>
<string name="groups_invitations_invitation_sent">Вы предложили %1$s присоединиться к группе \"%2$s\".</string>
<string name="groups_invitations_invitation_received">%1$s приглашает вас присоединиться к группе \"%2$s\".</string>
<string name="groups_invitations_joined">Присоединился к группе</string>
<string name="groups_invitations_declined">Приглашение в группу отклонено</string>
<plurals name="groups_invitations_open">
<item quantity="one">%d открытое приглашение в группу</item>
<item quantity="few">%d открытых приглашений в группу</item>
<item quantity="many">%d открытых приглашений в группу</item>
<item quantity="other">%d открытых приглашений в группу</item>
</plurals>
<string name="groups_invitations_response_accepted_sent">Вы приняли приглашение в группу от %s.</string>
<string name="groups_invitations_response_declined_sent">Вы отклонили приглашение в группу от %s.</string>
<string name="groups_invitations_response_accepted_received">%s принял приглашение в группу.</string>
<string name="groups_invitations_response_declined_received">%s отклонил приглашение в группу.</string>
<string name="sharing_status_groups">Только создатель может пригласить новых членов в группу. Ниже перечислены все текущие члены группы.</string>
<!--Private Groups Revealing Contacts-->
<string name="groups_reveal_contacts">Показать контакты</string>
<string name="groups_reveal_dialog_message">Вы можете выбрать, раскрывать ли контакты всем текущим и будущим членам этой группы.\n\nПри раскрытии контактов связь с группой становится более быстрой и надежной, поскольку вы можете общаться с раскрытыми контактами, даже если создатель группы находится в автономном режиме.</string>
<string name="groups_reveal_visible">Связь между контактами видна группе</string>
<string name="groups_reveal_visible_revealed_by_us">Связь между контактами видна группе (раскрывается вами)</string>
<string name="groups_reveal_visible_revealed_by_contact">Связь между контактами видна группе (раскрывается %s)</string>
<string name="groups_reveal_invisible">Связь между контактами не видна группе</string>
<!--Forums-->
<string name="no_forums">У вас еще нет форумов.\n\nПочему бы вам не создать новый, коснувшись значка + вверху?\n\nВы также можете попросить свои контакты поделиться с вами форумами.</string>
<string name="create_forum_title">Создать форум</string>
<string name="choose_forum_hint">Выберите имя для вашего форума</string>
<string name="create_forum_button">Создать форум</string>
<string name="forum_created_toast">Форум создан</string>
<string name="no_forum_posts">Этот форум пуст.\n\nИспользуйте значок пера вверху, чтобы создать первый пост.\n\nЧувствуете одиночество? Поделитесь этим форумом с вашими контактами!</string>
<string name="no_posts">Нет постов</string>
<plurals name="posts">
<item quantity="one">%d пост</item>
<item quantity="few">%d постов</item>
<item quantity="many">%d постов</item>
<item quantity="other">%d постов</item>
</plurals>
<string name="forum_new_entry_posted">Опубликована запись форума</string>
<string name="forum_new_message_hint">Новая запись</string>
<string name="forum_message_reply_hint">Новый ответ</string>
<string name="btn_reply">Ответ</string>
<string name="forum_leave">Покинуть форум</string>
<string name="dialog_title_leave_forum">Подтвердить</string>
<string name="dialog_message_leave_forum">Вы уверены, что хотите покинуть этот форум? Контакты, с которыми вы поделились этим форумом, могут быть перестать получать обновления этого форума.</string>
<string name="dialog_button_leave">Покинуть</string>
<string name="forum_left_toast">Покинул форум</string>
<!--Forum Sharing-->
<string name="forum_share_button">Поделиться форумом</string>
<string name="contacts_selected">Выбранные контакты</string>
<string name="activity_share_toolbar_header">Выбор контактов</string>
<string name="no_contacts_selector">Кажется, вы здесь новенький и не имеете контактов.\n\nПожалуйста, вернитесь сюда после того, как добавите свой первый контакт.</string>
<string name="forum_shared_snackbar">Поделиться форумом совместно с выбранными контактами</string>
<string name="forum_share_message">Добавить сообщение (необязательно)</string>
<string name="forum_share_error">Произошла ошибка при при попытке поделиться этим форумом.</string>
<string name="forum_invitation_received">%1$s поделился форумом \"%2$s\" с вами.</string>
<string name="forum_invitation_sent">Вы поделились форумом \"%1$s\" с %2$s.</string>
<string name="forum_invitations_title">Приглашения на форум</string>
<string name="forum_invitation_exists">Вы уже приняли приглашение на этот форум. Принятие большего числа приглашений приведет к росту и укреплению общения на форуме.</string>
<string name="forum_joined_toast">Присоединился к форуму</string>
<string name="forum_declined_toast">Приглашение на форум отклонено</string>
<string name="shared_by_format">Поделился %s</string>
<string name="forum_invitation_already_sharing">Уже поделился</string>
<string name="forum_invitation_response_accepted_sent">Вы приняли приглашение на форум от %s.</string>
<string name="forum_invitation_response_declined_sent">Вы отклонили приглашение на форум от %s.</string>
<string name="forum_invitation_response_accepted_received">%sпринял приглашение на форум.</string>
<string name="forum_invitation_response_declined_received">%s отклонил приглашение на форум.</string>
<string name="sharing_status">Статус общего доступа</string>
<string name="sharing_status_forum">Любой член форума может поделиться своими контактами. Вы делитесь этим форумом со следующими контактами. Могут быть и другие члены, которых вы не видите.</string>
<string name="shared_with">Совместно с %1$d (%2$d в сети)</string>
<plurals name="forums_shared">
<item quantity="one">%d форум, общий с контактами</item>
<item quantity="few">%d форумов, общих с контактами</item>
<item quantity="many">%d форумов, общих с контактами</item>
<item quantity="other">%d форумов, общих с контактами</item>
</plurals>
<string name="nobody">Никто</string>
<!--Blogs-->
<string name="blogs_other_blog_empty_state">Этот блог пуст.\n\nЛибо автор еще ничего не написал, либо человек, который поделился с вами этим блогом, должен выйти в Интернет, чтобы сообщения могли быть синхронизированы.</string>
<string name="read_more">подробнее</string>
<string name="blogs_write_blog_post">Написать в блоге</string>
<string name="blogs_write_blog_post_body_hint">Введите ваше сообщение здесь</string>
<string name="blogs_publish_blog_post">Опубликовать</string>
<string name="blogs_blog_post_created">Запись блога создана</string>
<string name="blogs_blog_post_received">Появилась новая запись блога</string>
<string name="blogs_blog_post_scroll_to">Перейти к</string>
<string name="blogs_feed_empty_state">Это глобальный блог.\n\nПохоже, что никто ничего не писал.\n\nБудьте первым - коснитесь значка пера, чтобы написать новую запись блога.</string>
<string name="blogs_remove_blog">Удалить блог</string>
<string name="blogs_remove_blog_dialog_message">Вы действительно хотите удалить этот блог со всеми записями?\nОбратите внимание, что это не приведет к удалению блога на устройствах других пользователей.</string>
<string name="blogs_remove_blog_ok">Удалить блог</string>
<string name="blogs_blog_removed">Блог удален</string>
<string name="blogs_reblog_comment_hint">Добавить комментарий (необязательно)</string>
<string name="blogs_reblog_button">Перепостить</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Поделиться блогом</string>
<string name="blogs_sharing_error">Произошла ошибка при при попытке поделиться этим блогом.</string>
<string name="blogs_sharing_button">Поделиться блогом</string>
<string name="blogs_sharing_snackbar">Поделиться блогом совместно с выбранными контактами</string>
<string name="blogs_sharing_response_accepted_sent">Вы приняли приглашение в блог от %s.</string>
<string name="blogs_sharing_response_declined_sent">Вы отклонили приглашение в блог от %s.</string>
<string name="blogs_sharing_response_accepted_received">%s принял приглашение в блог.</string>
<string name="blogs_sharing_response_declined_received">%s отклонил приглашение в блог.</string>
<string name="blogs_sharing_invitation_received">%1$s поделился блогом \"%2$s\" с вами.</string>
<string name="blogs_sharing_invitation_sent">Вы поделились блогом \"%1$s\" с %2$s.</string>
<string name="blogs_sharing_invitations_title">Приглашения в блог</string>
<string name="blogs_sharing_joined_toast">Подписался на блог</string>
<string name="blogs_sharing_declined_toast">Приглашение в блог отклонено</string>
<string name="sharing_status_blog">Любой, кто подписывается на блог, может поделиться им со своими контактами. Вы делитесь этим блогом со следующими контактами. Могут также быть и другие подписчики, которых вы не видите.</string>
<!--RSS Feeds-->
<string name="blogs_rss_feeds_import">Импорт RSS-канала</string>
<string name="blogs_rss_feeds_import_button">Импорт</string>
<string name="blogs_rss_feeds_import_hint">Введите URL-адрес RSS-канала</string>
<string name="blogs_rss_feeds_import_error">Мы сожалеем! Не удалось импортировать этот RSS-канал.</string>
<string name="blogs_rss_feeds_manage">Управление RSS-каналами</string>
<string name="blogs_rss_feeds_manage_imported">Импортирован:</string>
<string name="blogs_rss_feeds_manage_author">Автор:</string>
<string name="blogs_rss_feeds_manage_updated">Последнее обновление:</string>
<string name="blogs_rss_remove_feed">Удалить RSS-канал</string>
<string name="blogs_rss_remove_feed_dialog_message">Вы действительно хотите удалить этот RSS-канал и все его сообщения?\nЛюбые сообщения, которыми вы поделились, не будут удалены с устройств других пользователей.</string>
<string name="blogs_rss_remove_feed_ok">Удалить RSS-канал</string>
<string name="blogs_rss_feeds_manage_delete_error">Не удалось удалить RSS-канал!</string>
<string name="blogs_rss_feeds_manage_empty_state">Вы еще не импортировали RSS-каналы.\n\nПочему бы вам не нажать кнопку + в правом верхнем углу экрана, чтобы добавить первый?</string>
<string name="blogs_rss_feeds_manage_error">Ошибка при загрузке каналов. Повторите попытку позже.</string>
<!--Settings Network-->
<string name="network_settings_title">Сети</string>
<string name="bluetooth_setting">Подключение через Bluetooth</string>
<string name="bluetooth_setting_enabled">Когда контакты находятся поблизости</string>
<string name="bluetooth_setting_disabled">Только при добавлении контактов</string>
<string name="tor_network_setting">Подключение через Tor</string>
<string name="tor_network_setting_never">Никогда</string>
<string name="tor_network_setting_wifi">Только при использовании Wi-Fi</string>
<string name="tor_network_setting_always">При использовании Wi-Fi или мобильных данных</string>
<!--Settings Security and Panic-->
<string name="security_settings_title">Безопасность</string>
<string name="change_password">Изменить пароль</string>
<string name="current_password">Введите текущий пароль:</string>
<string name="choose_new_password">Выберите новый пароль:</string>
<string name="confirm_new_password">Подтвердите новый пароль:</string>
<string name="password_changed">Пароль был изменен.</string>
<string name="panic_setting">Настройка кнопки паники</string>
<string name="panic_setting_title">Кнопка паники</string>
<string name="panic_setting_hint">Настройте поведение Briar, когда вы используете приложение Кнопка паники</string>
<string name="panic_app_setting_title">Приложение Кнопка паники</string>
<string name="unknown_app">неизвестное приложение</string>
<string name="panic_app_setting_summary">Приложение не установлено</string>
<string name="panic_app_setting_none">Никто</string>
<string name="dialog_title_connect_panic_app">Подтвердить приложение Паника</string>
<string name="dialog_message_connect_panic_app">Вы уверены, что хотите разрешить %1$s запускать действия разрушительной Кнопкой паники?</string>
<string name="lock_setting_title">Выйти</string>
<string name="lock_setting_summary">Выйти из Briar, если нажата Кнопка паники</string>
<string name="purge_setting_title">Удалить аккаунт</string>
<string name="purge_setting_summary">Удалите свою учетную запись Briar, если нажата Кнопка паники. Предупреждение: это необратимо удалит ваши идентификаторы, контакты и сообщения</string>
<string name="uninstall_setting_title">Удалить Briar</string>
<string name="uninstall_setting_summary">Это требует подтверждения вручную в случае возникновения паники</string>
<!--Settings Notifications-->
<string name="notification_settings_title">Уведомления</string>
<string name="notify_private_messages_setting_title">Приватные сообщения</string>
<string name="notify_private_messages_setting_summary">Показывать оповещения для приватных сообщений</string>
<string name="notify_group_messages_setting_title">Групповые сообщения</string>
<string name="notify_group_messages_setting_summary">Показывать оповещения для групповых сообщений</string>
<string name="notify_forum_posts_setting_title">Записи форума</string>
<string name="notify_forum_posts_setting_summary">Показывать оповещения для записей форума</string>
<string name="notify_blog_posts_setting_title">Записи блога</string>
<string name="notify_blog_posts_setting_summary">Показывать оповещения для записей блога</string>
<string name="notify_vibration_setting">Вибрация</string>
<string name="notify_lock_screen_setting_title">Экран блокировки</string>
<string name="notify_lock_screen_setting_summary">Показывать уведомления на экране блокировки</string>
<string name="notify_sound_setting">Звук</string>
<string name="notify_sound_setting_default">Мелодия по умолчанию</string>
<string name="notify_sound_setting_disabled">Нет</string>
<string name="choose_ringtone_title">Выберите мелодию звонка</string>
<!--Settings Feedback-->
<string name="feedback_settings_title">Обратная связь</string>
<string name="send_feedback">Отправить отзыв</string>
<!--Link Warning-->
<string name="link_warning_title">Ссылка-предупреждение</string>
<string name="link_warning_intro">Вы собираетесь открыть следующую ссылку с внешним приложением.</string>
<string name="link_warning_text">Это может быть использовано для идентификации вас. Подумайте о том, доверяете ли вы человеку, который прислал вам эту ссылку, и рассмотрите возможность его открытия в Orfox.</string>
<string name="link_warning_open_link">Открыть ссылку</string>
<!--Crash Reporter-->
<string name="crash_report_title">Отчет о сбое Briar</string>
<string name="briar_crashed">К сожалению, Briar неожиданно завершил работу.</string>
<string name="not_your_fault">Это не ваша вина.</string>
<string name="please_send_report">Пожалуйста, помогите сделать Briar еще лучше, отправив нам отчет о сбоях.</string>
<string name="report_is_encrypted">Мы обещаем, что отчет зашифрован и будет отправлен безопасно.</string>
<string name="feedback_title">Обратная связь</string>
<string name="describe_crash">Опишите, что произошло (необязательно)</string>
<string name="enter_feedback">Введите свой отзыв</string>
<string name="optional_contact_email">Ваш адрес электронной почты (необязательно)</string>
<string name="include_debug_report_crash">Включить анонимные данные о сбое</string>
<string name="include_debug_report_feedback">Включить анонимные данные об этом устройстве</string>
<string name="could_not_load_report_data">Не удалось загрузить данные отчета.</string>
<string name="send_report">Отправить отчет</string>
<string name="close">Закрыть</string>
<string name="dev_report_saved">Отчет сохранен. Он будет отправлен при следующем входе в Briar.</string>
<!--Sign Out-->
<string name="progress_title_logout">Выход из Briar…</string>
<!--Screen Filters & Tapjacking-->
<string name="screen_filter_title">Обнаружено наложение экрана</string>
<string name="screen_filter_body">Другое приложение рисуется поверх Briar. Чтобы защитить вашу безопасность, Briar не будет реагировать на прикосновения, когда другое приложение рисует сверху.\n\nПопробуйте отключить следующие приложения при использовании Briar:\n\n%1$s</string>
</resources>

View File

@@ -22,7 +22,6 @@
<string name="startup_failed_activity_title">Dështim Nisjeje i Briar-it</string>
<string name="startup_failed_db_error">Për ndonjë arsye, baza e të dhënave e Briar-it tuaj është aq e dëmtuar, sa smund të ndreqet. Llogaria juaj, të dhënat tuaja dhe krejt lidhjet tuaja me kontaktet kanë humbur. Mjerisht lypset të ri-instaloni Briar-in dhe të rregulloni një llogari të re.</string>
<string name="startup_failed_service_error">Briar-i sarriti të nisë një shtojcë të domosdoshme. Ri-instalimi i Briar-it zakonisht e zgjidh këtë problem. Por, ju lutemi, kini parasysh se me të do të humbni llogarinë tuaj dhe krejt të dhënat e lidhura me të, ngaqë Briar nuk përdor shërbyes qendrorë për të depozituar në ta të dhënat tuaja.</string>
<string name="expiry_warning">Ky software ka skaduar.\nJu lutemi, instaloni një version më të ri.</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Hap sirtarin e lëvizjeve</string>
<string name="nav_drawer_close_description">Mbylle sirtarin e lëvizjeve</string>
@@ -242,7 +241,6 @@
<string name="blogs_blog_post_received">U morën Postime të Reja Blogu</string>
<string name="blogs_blog_post_scroll_to">Kalo Te</string>
<string name="blogs_feed_empty_state">Kjo është prurja globale e blogjeve.\n\nDuket se askush ska shkruar gjë në ndonjë blog.\n\nBëhuni ju i pari dhe prekni ikonën penë që të shkruani postimin e parë të një blogu të ri.</string>
<string name="blogs_personal_blog">Blogu Personal i %s</string>
<string name="blogs_remove_blog">hiqe Blogun</string>
<string name="blogs_remove_blog_dialog_message">Jeni i sigurt se doni të hiqet ky blog dhe krejt postimet?\nMbani parasysh që kjo nuk do ta heqë blogun nga pajisjet e personave të tjerë.</string>
<string name="blogs_remove_blog_ok">Hiqe Blogun</string>
@@ -257,8 +255,6 @@
<string name="blogs_sharing_response_declined_sent">Hodhët poshtë ftesën e blogut nga %s.</string>
<string name="blogs_sharing_response_accepted_received">%s pranoi ftesën e blogut.</string>
<string name="blogs_sharing_response_declined_received">%s hodhi poshtë ftesën e blogut.</string>
<string name="blogs_sharing_invitation_received">%1$s ka ndarë me ju blogun personal të %2$s.</string>
<string name="blogs_sharing_invitation_sent">Keni ndarë me %2$s blogun personal të %1$s.</string>
<string name="blogs_sharing_invitations_title">Ftesa Blogu</string>
<string name="blogs_sharing_joined_toast">U pajtuat te Blogu</string>
<string name="blogs_sharing_declined_toast">Ftesa e Blogut u Hodh Poshtë</string>

View File

@@ -0,0 +1,327 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<!--Setup-->
<string name="setup_title">Briar Podešavanje</string>
<string name="setup_explanation">Vaš Briar račun je kriptovan i sačuvan na vašem uređaju, ne u server-oblaku. Ako deinstalirate Briar ili zaboravite šifru, nema načina da povratite vaš račun ili vaše podatke.</string>
<string name="choose_nickname">Izaberite vaš nadimak</string>
<string name="choose_password">Izaberite vašu šifru</string>
<string name="confirm_password">Potvrdite vašu šifru</string>
<string name="name_too_long">Ime je predugo</string>
<string name="password_too_weak">Šifra je slaba</string>
<string name="passwords_do_not_match">Šifre se ne poklapaju</string>
<string name="create_account_button">Kreirajte račun</string>
<!--Login-->
<string name="enter_password">Unesite vašu šifru:</string>
<string name="try_again">Pogrešna šifra, probajte opet</string>
<string name="sign_in_button">Prijavite se</string>
<string name="forgotten_password">Zaboravio sam šifru</string>
<string name="dialog_title_lost_password">Izgubljena šifra</string>
<string name="dialog_message_lost_password">Vaš Briar račun je kriptovan i sačuvan na vašem uređaju, ne u server-oblaku, pa nemožemo resetovati vašu šifru. Želite li da izbrišete račun i počnete iz početka?\n\nPozor: Vaši identiteti, kontakti i poruke će biti premanentno izgubljeni.</string>
<string name="startup_failed_notification_title">Brirar nije mogao da startuje</string>
<string name="startup_failed_notification_text">Možda ćete morati da reinstalirate Briar</string>
<string name="startup_failed_activity_title">Briar neuspješno startovanje</string>
<string name="startup_failed_db_error">Iz nekog razloga, Briar baza podataka je nepovratno korumpirana. Vaš račun, vaši podaci i svi kontakti su izgubljeni. Nažalost, morate reinstalirati Briar i podesiti novi račun.</string>
<string name="startup_failed_service_error">Briar nije mogao da startuje potreban plugin. Reinstaliranje Briara obično riješi problem. Međutim, imajte na umu da ćete izgubiti vaš račun i sve podatke vezane za njega pošto Briar ne koristi centralne servere da sačuva vaše podatke.</string>
<string name="expiry_date_reached">Ovaj softver je istekao.\nHvala na testiranju!</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Otvorite navigacionu fioku</string>
<string name="nav_drawer_close_description">Zatvorite navigacionu fioku</string>
<string name="contact_list_button">Kontakti</string>
<string name="groups_button">Privatne Grupe</string>
<string name="forums_button">Forumi</string>
<string name="blogs_button">Blogovi</string>
<string name="settings_button">Podešavanja</string>
<string name="sign_out_button">Odjava</string>
<!--Transports-->
<string name="transport_tor">Internet</string>
<string name="transport_bt">Bluetooth</string>
<string name="transport_lan">Wi-Fi</string>
<!--Notifications-->
<string name="ongoing_notification_title">Prijavljeni ste u Briar</string>
<string name="ongoing_notification_text">Dodirnite da otvorite Briar.</string>
<!--Misc-->
<string name="now">sada</string>
<string name="show">Pokazi</string>
<string name="hide">Sakrij</string>
<string name="ok">OK</string>
<string name="cancel">Odustani</string>
<string name="got_it">Jasno</string>
<string name="delete">Briši</string>
<string name="accept">Prihvati</string>
<string name="decline">Odbij</string>
<string name="options">Opcije</string>
<string name="online">Na vezi</string>
<string name="offline">Iskljucen</string>
<string name="send">Šalji</string>
<string name="allow">Dozvoli</string>
<string name="open">Otvori</string>
<string name="no_data">Nema podataka</string>
<string name="ellipsis">...</string>
<string name="text_too_long">Uneti tekst je predugačak</string>
<string name="show_onboarding">Pokazi Pomoc dijalog </string>
<!--Contacts and Private Conversations-->
<string name="no_contacts">Čini se da ste ovdje novi i da nemate još kontakata.\n\nTaknite + ikonu na vrhu i pratite instrukcije da bi dodali neke prijatelje na listu.\n\nMolimo da upamtite: Mozete samo dodati nove kontakte postupkom lice-u-lice kako bi se spriječili pokušaji lažnog predstavljanja ili da bi pročitali poruke upućene vama.</string>
<string name="date_no_private_messages">Nema poruka.</string>
<string name="no_private_messages">Ovo je pregled konverzacija.\n\nČini se da imamo nedostatak konverzacija.\n\nSamo taknite polje ya unos na dnu kako bi počeli konverzaciju.</string>
<string name="message_hint">Ukucajte poruku</string>
<string name="delete_contact">Izbrišite kontakt</string>
<string name="dialog_title_delete_contact">Potvrdite brisanje kontakta</string>
<string name="dialog_message_delete_contact">Jeste li sigurni da želite da uklonite ovaj kontakt i sve poruke koje ste razmijenili?</string>
<string name="contact_deleted_toast">Kontakt izbrisan</string>
<!--Adding Contacts-->
<string name="add_contact_title">Dodaj kontakt</string>
<string name="your_nickname">Izaberite identitet koji želite da koristite</string>
<string name="face_to_face">Morate se sresti sa osobom koju želite da dodate kao kontakt.\n\nOvo će spriječiti bilo koga da se predstavi kao vi ili da ubuduće čita poruke.</string>
<string name="continue_button">Dalje</string>
<string name="your_invitation_code">Vaš kod za poziv je</string>
<string name="enter_invitation_code">Unesite kod pozivnice vašeg kontakta:</string>
<string name="searching_format">Tražim kontakt sa kodom pozivnice %06d\u2026</string>
<string name="connection_failed">Veza je pukla</string>
<string name="could_not_find_contact">Briar nije mogao naći vaš kontakt u blizini</string>
<string name="try_again_button">Probajte opet</string>
<string name="connected_to_contact">Povezite se sa kontaktom</string>
<string name="calculating_confirmation_code">Kalkulisanje koda konfirmacije\u2026</string>
<string name="your_confirmation_code">Vaš konfirmacioni kod je</string>
<string name="enter_confirmation_code">Unesite konfirmacioni kod vašeg kontakta:</string>
<string name="waiting_for_contact">Čekam kontakt\u2026</string>
<string name="waiting_for_contact_to_scan">Čekam da kontakt skenira i poveže se\u2026</string>
<string name="exchanging_contact_details">Razmjenjujem detalje kontakta\u2026</string>
<string name="codes_do_not_match">Kodovi se nijesu poklopili</string>
<string name="interfering">Ovo može značiti da neko pokušava da se umiješa u vašu vezu</string>
<string name="contact_added_toast">Kontakt dodat: %s</string>
<string name="contact_already_exists">Kontakt %s već postoji</string>
<string name="contact_exchange_failed">Razmjena kontakata nije uspjela</string>
<string name="qr_code_invalid">QR kod nije validan</string>
<string name="connecting_to_device">Povezujem se sa uređajem\u2026</string>
<string name="authenticating_with_device">Autentikacija sa uređajem\u2026</string>
<string name="connection_aborted_local">Konekcija prekinuta sa naše strane! Ovo može značiti da neko pokušava da se umiješa u vašu vezu</string>
<string name="connection_aborted_remote">Konekcija prekinuta sa od strane kontakta! Ovo može značiti da neko pokušava da se umiješa u vašu vezu</string>
<!--Introductions-->
<string name="introduction_onboarding_title">Upoznajte vaše kontakte</string>
<string name="introduction_onboarding_text">Vi možete da upoznate vaše kontakte međusobno, da nebi morali da srijeću lice u liceda bi se povezali na Briar.</string>
<string name="introduction_activity_title">Izaberite kontakt</string>
<string name="introduction_message_title">Upoznajte kontakte</string>
<string name="introduction_message_hint">Dodajte poruku (opciono)</string>
<string name="introduction_button">Izvršite upoznavanje</string>
<string name="introduction_sent">Vaše poziv na upoznavanje je poslat</string>
<string name="introduction_error">Došlo je do greške pri izvršenju upoznavanja.</string>
<string name="introduction_response_error">Greška pri odgovoru na upoznavanje</string>
<string name="introduction_request_sent">Tražili ste da se %1$s i %2$s upoznaju.</string>
<string name="introduction_request_received">%1$s je tražio da vas upozna sa %2$s. Da li želite da dodate %2$s u vašu listu kontakata?</string>
<string name="introduction_request_exists_received">%1$s je tražio da vas upozna sa %2$s, ali %2$s je već u vašoj listi kontakata. Pošto %1$s to možda nezna, vi i dalje možete da odgovorite: </string>
<string name="introduction_request_answered_received">%1$s je tražio da vas upozna sa %2$s.</string>
<string name="introduction_response_accepted_sent">Prihvatili ste upoznavanje sa kontaktom %1$s.</string>
<string name="introduction_response_declined_sent">Odbili ste upoznavanje sa kontaktom %1$s.</string>
<string name="introduction_response_accepted_received">%1$s prihvata upoznavanje sa %2$s.</string>
<string name="introduction_response_declined_received">%1$s je prihvatio-la upoznavanje sa kontaktom %2$s.</string>
<string name="introduction_response_declined_received_by_introducee">%1$s kaže da je %2$s odbio-la upoznavanje.</string>
<!--Private Groups-->
<string name="groups_list_empty">Ne učestvujete ni u jednoj grupi.\n\nTaknite + ikonu na vrhu da bi sami kreirali grupu ili tražite vašim kontaktima da vas pozovu u neku od njihovih grupa.</string>
<string name="groups_created_by">Kreator je %s</string>
<string name="groups_group_is_empty">Grupa je prazna</string>
<string name="groups_group_is_dissolved">Ova grupa je rasformirana</string>
<string name="groups_remove">Ukloni</string>
<string name="groups_create_group_title">Kreiraj privatnu grupu</string>
<string name="groups_create_group_button">Kreiraj grupu</string>
<string name="groups_create_group_invitation_button">Pošalji poziv</string>
<string name="groups_create_group_hint">Izaberite ime za vašu privatnu grupu</string>
<string name="groups_invitation_sent">Grupna pozivnica je poslata</string>
<string name="groups_message_sent">Poruka poslata</string>
<string name="groups_member_list">Lista članova</string>
<string name="groups_invite_members">Pošaljite poziv članovima</string>
<string name="groups_member_created_you">Vi ste kreirali grupu</string>
<string name="groups_member_created">%s je kreator grupe</string>
<string name="groups_member_joined_you">Pristupili ste grupi</string>
<string name="groups_member_joined">1%s je pristupio-la grupi</string>
<string name="groups_leave">Napustite grupu</string>
<string name="groups_leave_dialog_title">Potvrdite napuštanje grupe</string>
<string name="groups_leave_dialog_message">Jeste li sigurni da želite da napustite grupu?</string>
<string name="groups_dissolve">Raspustite grupu</string>
<string name="groups_dissolve_dialog_title">Potvrdite raspuštanje grupe</string>
<string name="groups_dissolve_dialog_message">Jeste li sigurni da želite da raspustite ovu grupu?\n\nOstali članovi neće više moći da nastave njihovu konverzaciju i možda neće dobiti poslednje poruke.</string>
<string name="groups_dissolve_button">Raspusti</string>
<string name="groups_dissolved_dialog_title">Grupa je raspuštena</string>
<string name="groups_dissolved_dialog_message">Kreator je raspustio ovu grupu.\n\nNemožete više pisati poruke u grupi i možda nećete primiti poljednje poruke koje su napisane.</string>
<!--Private Group Invitations-->
<string name="groups_invitations_title">Grupni pozivi</string>
<string name="groups_invitations_invitation_sent">Pozvali ste kontakt %1$s da se pridruzi grupi \"%2$s\".</string>
<string name="groups_invitations_invitation_received">%1$s je poslao-la poziv da pristupite grupi \"%2$s\".</string>
<string name="groups_invitations_joined">Pristupili ste grupi</string>
<string name="groups_invitations_declined">Grupni poziv je odbijen</string>
<string name="groups_invitations_response_accepted_sent">Prihvatili ste grupni poziv od kontakta %s.</string>
<string name="groups_invitations_response_declined_sent">Odbili ste grupni poziv od kontakta %s.</string>
<string name="groups_invitations_response_accepted_received">%s je prihvatio-la grupni poziv.</string>
<string name="groups_invitations_response_declined_received">1%s je odbio-la grupni poziv.</string>
<string name="sharing_status_groups">Samo kreator moze da pozove nove članove u grupu. Ispod su trenutni članovi grupe.</string>
<!--Private Groups Revealing Contacts-->
<string name="groups_reveal_contacts">Otkrijte kontakte</string>
<string name="groups_reveal_dialog_message">Možete odlučiti da otkrijete kontakte svim trenutnim i budućim članovima ove grupe.\n\nOtkrivanje kontakata čini konekciju ka članovima grupe bržom i pouzdanijom, jer možete da komunicirate sa otkrivenim kontaktima čak i kad kreator grupe nije na vezi.</string>
<string name="groups_reveal_visible">Veze kontakata su vidljive grupi</string>
<string name="groups_reveal_visible_revealed_by_us">Veze kontakata su vidljive grupi (vi ste je otkrili)</string>
<string name="groups_reveal_visible_revealed_by_contact">Veze kontakata su vidljive grupi (otkriveno od strane %s)</string>
<string name="groups_reveal_invisible">Veze kontakata nisu vidljive grupi</string>
<!--Forums-->
<string name="no_forums">Još nemate nijedan forum.\n\nZašto ne kreirate jedan sami dodirom na + ikonu pri vrhu?\n\nMožete takođe da pitate kontakte da podijele neki forum sa vama.</string>
<string name="create_forum_title">Kreirajte forum</string>
<string name="choose_forum_hint">Izaberite ime za vaš forum</string>
<string name="create_forum_button">Kreirajte forum</string>
<string name="forum_created_toast">Forum je kreiran</string>
<string name="no_forum_posts">Ovaj forum je prazan.\n\nKoristite ikonu olovke pri vrhu da napišete prvi post.\n\nOsjećate se usamljeno ovdje? Podijelite ovaj forum sa više vaših kontakata!</string>
<string name="no_posts">Nema postova</string>
<string name="forum_new_entry_posted">Forumski zapis postovan</string>
<string name="forum_new_message_hint">Novi zapis</string>
<string name="forum_message_reply_hint">Novi odgovor</string>
<string name="btn_reply">Odgovor</string>
<string name="forum_leave">Napustite forum</string>
<string name="dialog_title_leave_forum">Potvrdite napuštanje foruma</string>
<string name="dialog_message_leave_forum">jeste li sigurni da želite da napustite forum? Kontakti kojima ste podijelili ovaj forum mogu prestati da dobijaju nove postove foruma. </string>
<string name="dialog_button_leave">Napusti</string>
<string name="forum_left_toast">Napustili ste forum</string>
<!--Forum Sharing-->
<string name="forum_share_button">Dijelite forum</string>
<string name="contacts_selected">Kontakti selektovani</string>
<string name="activity_share_toolbar_header">Izaberite kontakte</string>
<string name="no_contacts_selector">Izgleda da ste ovdje novi i da nemate još kontakata.\n\nVratite se nazad ovdje kad dodate vaš prvi kontakt.</string>
<string name="forum_shared_snackbar">Forum je podijeljen sa izabranim kontaktima</string>
<string name="forum_share_message">Dodajte poruku (opciono)</string>
<string name="forum_share_error">Došlo je do greške prilikom slanja ovog foruma.</string>
<string name="forum_invitation_received">%1$s je podijelio-la forum \"%2$s\" sa vama.</string>
<string name="forum_invitation_sent">Podijelili ste forum \"%1$s\" sa %2$s.</string>
<string name="forum_invitations_title">Pozivnice za forum</string>
<string name="forum_invitation_exists">Vi ste već prihvatili poziv u ovaj forum. Prihvatanje dodatnih pozivnica će povećati i ojačati komunikaciju u forumu.</string>
<string name="forum_joined_toast">Pristupili ste forumu</string>
<string name="forum_declined_toast">Pozivnica za forum je odbijena</string>
<string name="shared_by_format">Podijelio-la %s</string>
<string name="forum_invitation_already_sharing">Već se dijeli</string>
<string name="forum_invitation_response_accepted_sent">Prihvatili ste poziv u forum od %s.</string>
<string name="forum_invitation_response_declined_sent">Odbili ste poziv u forum od %s.</string>
<string name="forum_invitation_response_accepted_received">1%s prihvata vaš poziv u forum.</string>
<string name="forum_invitation_response_declined_received">%s odbija vaš poziv u forum.</string>
<string name="sharing_status">Status dijeljenja</string>
<string name="sharing_status_forum">Bilo koji član foruma može ga podijeliti sa svojim kontaktima. Vi dijelite forum sa slijedećim kontaktima. Moguće je da ima drugih članova koje ne vidite.</string>
<string name="shared_with">Pdijeljeno sa %1$d (%2$d na vezi)</string>
<string name="nobody">Niko</string>
<!--Blogs-->
<string name="blogs_other_blog_empty_state">Ovaj blog je trenutno prazan.\n\nIli autor nije još ništa napisao, ili osoba koja je podijelila ovaj blog sa vama treba da bude na vezi, kako bi postovi bili sinhronizovani.</string>
<string name="read_more">pročitaj ostalo</string>
<string name="blogs_write_blog_post">Napiši Blog Post</string>
<string name="blogs_write_blog_post_body_hint">Ukucajte vaš blog post ovdje</string>
<string name="blogs_publish_blog_post">Objavi</string>
<string name="blogs_blog_post_created">Blog post je kreiran</string>
<string name="blogs_blog_post_received">Primljen je novi blog post</string>
<string name="blogs_blog_post_scroll_to">Skroluj do</string>
<string name="blogs_feed_empty_state">Ovo je globalni blog kanal.\n\nIzgleda da niko još uvjek nije ništa blogovao.\n\nBudi prvi i dotakni ikonu olovke da napišeš novi blog post.</string>
<string name="blogs_remove_blog">Ukloni blog</string>
<string name="blogs_remove_blog_dialog_message">Jeste li sigurni da želite da uklonite ovaj blog i sve postove?\nUzmite u obzir da ovo neće ukloniti blog sa uređaja drugih osoba.</string>
<string name="blogs_remove_blog_ok">Uklonite blog</string>
<string name="blogs_blog_removed">Blog uklonjen</string>
<string name="blogs_reblog_comment_hint">Dodajte komentar (opciono)</string>
<string name="blogs_reblog_button">Reblogujte</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Podijelite blog</string>
<string name="blogs_sharing_error">Došlo je do greške pri podjeli bloga.</string>
<string name="blogs_sharing_button">Podijeli blog</string>
<string name="blogs_sharing_snackbar">Blog je podijeljen sa izabranim kontaktima</string>
<string name="blogs_sharing_response_accepted_sent">Prihvatili ste blog pozivnicu od %s.</string>
<string name="blogs_sharing_response_declined_sent">Odbili ste blog pozivnicu od %s.</string>
<string name="blogs_sharing_response_accepted_received">%s je prihvatio-la blog pozivnicu.</string>
<string name="blogs_sharing_response_declined_received">%s je odbio-la blog pozivnicu.</string>
<string name="blogs_sharing_invitation_received">%1$s je podijelio-la blog \"%2$s\" sa vama.</string>
<string name="blogs_sharing_invitation_sent">Podijelili ste blog \"%1$s\" sa %2$s.</string>
<string name="blogs_sharing_invitations_title">Blog pozivnice</string>
<string name="blogs_sharing_joined_toast">Potpisani ste na blog</string>
<string name="blogs_sharing_declined_toast">Blog poziv je Odbijen</string>
<string name="sharing_status_blog">Ko god se potpiše za ovaj blog može da ga podijeli sa svojim kontaktima. Vi dijelite blog sa slijedećim kontaktima. Moguće je da ima još potpisnika koje nemožete da vidite.</string>
<!--RSS Feeds-->
<string name="blogs_rss_feeds_import">Uvezi RSS kanal</string>
<string name="blogs_rss_feeds_import_button">Uvezi</string>
<string name="blogs_rss_feeds_import_hint">Unesi URL od RSS kanala</string>
<string name="blogs_rss_feeds_import_error">Žao nam je! Došlo je do greške pri unosu vašeg kanala.</string>
<string name="blogs_rss_feeds_manage">Rukujte RSS kanalima</string>
<string name="blogs_rss_feeds_manage_imported">Uvezeno:</string>
<string name="blogs_rss_feeds_manage_author">Autor:</string>
<string name="blogs_rss_feeds_manage_updated">Zadnje ažuriranje:</string>
<string name="blogs_rss_remove_feed">Uklonite kanal</string>
<string name="blogs_rss_remove_feed_dialog_message">Jeste li sigurni da želite da uklonite ovaj kanal i sve njegove postove?\nBilo koji post koji ste podijelili se neće ukloniti sa uređaja drugih osoba.</string>
<string name="blogs_rss_remove_feed_ok">Ukloni kanal</string>
<string name="blogs_rss_feeds_manage_delete_error">Kanal nije bilo moguće ukloniti!</string>
<string name="blogs_rss_feeds_manage_empty_state">Nijeste uvezli nijedan RSS kanal.\n\nYZašto nebi dotakli plus znak na vrhu desnog dijela ekrana da dodate prvi?</string>
<string name="blogs_rss_feeds_manage_error">Došlo je do problema pri učitavanju vaših kanala. Probajte opet kasnije.</string>
<!--Settings Network-->
<string name="network_settings_title">Mreže</string>
<string name="bluetooth_setting">Povežite se preko Bluetooth-a</string>
<string name="bluetooth_setting_enabled">Kad god su kontakti blizu</string>
<string name="bluetooth_setting_disabled">Samo pri dodavanju kontakata</string>
<string name="tor_network_setting">Povežite se preko Tor-a</string>
<string name="tor_network_setting_never">Nikad</string>
<string name="tor_network_setting_wifi">Samo kad koristim Wi-Fi</string>
<string name="tor_network_setting_always">Kad koristim Wi-Fi ili mobile data</string>
<!--Settings Security and Panic-->
<string name="security_settings_title">Sigurnost</string>
<string name="change_password">Promijeni sifru</string>
<string name="current_password">Unesite vašu trenutnu šifru:</string>
<string name="choose_new_password">Izaberite vašu novu šifru:</string>
<string name="confirm_new_password">Potvrdite vašu novu šifru:</string>
<string name="password_changed">Šifra je promijenjena</string>
<string name="panic_setting">Podešavanje panik dugmeta</string>
<string name="panic_setting_title">Panik dugme</string>
<string name="panic_setting_hint">Podesite kako Briar reaguje kad koristite panik dugme app</string>
<string name="panic_app_setting_title">Panik Dugne App</string>
<string name="unknown_app">nepoznata aplikacija</string>
<string name="panic_app_setting_summary">Nema postavljene aplikacije</string>
<string name="panic_app_setting_none">Ništa</string>
<string name="dialog_title_connect_panic_app">Potvrdi Panic App</string>
<string name="dialog_message_connect_panic_app">Jeste li sigurni da želite da dozvolite %1$s da pokrene destruktivne akcije panik dugmeta?</string>
<string name="lock_setting_title">Izlogujte se</string>
<string name="lock_setting_summary">Izloguj se iz Briar-a ako je pritisnuto panik dugme</string>
<string name="purge_setting_title">Izbriši račun</string>
<string name="purge_setting_summary">Izbriši Briar račun ako je panik dugme pritisnuto. Pažnja: Ovo će permanentno izbrisati vaše identitete, kontakte i poruke</string>
<string name="uninstall_setting_title">Deinstaliraj Briar</string>
<string name="uninstall_setting_summary">Ovo zahtijeva ručnu potvrdu u slučaju panike</string>
<!--Settings Notifications-->
<string name="notification_settings_title">Obavještenja</string>
<string name="notify_private_messages_setting_title">Privatne poruke</string>
<string name="notify_private_messages_setting_summary">Prikaži upozorenja na privatne poruke</string>
<string name="notify_group_messages_setting_title">Grupne poruke</string>
<string name="notify_group_messages_setting_summary">Prikaži upozorenja za grupne poruke</string>
<string name="notify_forum_posts_setting_title">Forum postovi</string>
<string name="notify_forum_posts_setting_summary">Prikaži upozorenja za forum postove</string>
<string name="notify_blog_posts_setting_title">Blog postovi</string>
<string name="notify_blog_posts_setting_summary">Prikaži upozorenja na blog postove</string>
<string name="notify_vibration_setting">Vibriranje</string>
<string name="notify_lock_screen_setting_title">Zaključaj ekran</string>
<string name="notify_lock_screen_setting_summary">Prikaži obavještenja na zaključanom ekranu</string>
<string name="notify_sound_setting">Zvuk</string>
<string name="notify_sound_setting_default">Podrazumijevanio zvono</string>
<string name="notify_sound_setting_disabled">Ništa</string>
<string name="choose_ringtone_title">Izaberi zvuk zvona</string>
<!--Settings Feedback-->
<string name="feedback_settings_title">Povratne informacije</string>
<string name="send_feedback">Šalji povratne informacije</string>
<!--Link Warning-->
<string name="link_warning_title">Link upozorenje</string>
<string name="link_warning_intro">Upravo ćete otvoriti slijedeći link sa eksternom aplikacijom</string>
<string name="link_warning_text">Ovo može biti upotrijebljeno da vas identifikuju. Razmislite da li vjerujete osobi koja vam je poslala ovaj link i razmotrite da ga možda otvorite u Orfox-u.</string>
<string name="link_warning_open_link">Otvori link</string>
<!--Crash Reporter-->
<string name="crash_report_title">Briar izvještaj po krahu</string>
<string name="briar_crashed">Izvinite, Briar je krahirao.</string>
<string name="not_your_fault">Nijeste vi krivi.</string>
<string name="please_send_report">Molimo pomozite nam da napravimo bolji Briar tako što ćete nam poslati izvještaj o krahu.</string>
<string name="report_is_encrypted">Obećavamo da je izvještaj kriptovan i sigurno poslat.</string>
<string name="feedback_title">Povratna informacija</string>
<string name="describe_crash">Opišite šta se desilo (opciono)</string>
<string name="enter_feedback">Unesite vašu povratnu informaciju</string>
<string name="optional_contact_email">Vaša email adres (opciono)</string>
<string name="include_debug_report_crash">Uključite anonimne podatke o krahu</string>
<string name="include_debug_report_feedback">Uključite anonimne podatke o ovom uređaju</string>
<string name="could_not_load_report_data">Nije bilo moguće učitati podatke izvještaja</string>
<string name="send_report">Pošalji izvještaj</string>
<string name="close">Zatvori</string>
<string name="dev_report_saved">Izvještaj sačuvan. Biće poslat slijedeći put kada se ulogujete u Briar.</string>
<!--Sign Out-->
<string name="progress_title_logout">Isključujete se iz Briara...</string>
<!--Screen Filters & Tapjacking-->
<string name="screen_filter_title">Detektovano je prekrivanje ekrana</string>
<string name="screen_filter_body">Neka druga aplikacija crta interfejs preko Briara. Kako bi vas zaštitili, Briar neće reagovati na dodir kada druga aplikacija ispisuje preko njega.\n\nProbajte da ugasite slijedeće aplikacije kad koristite Briar:\n\n%1$s</string>
</resources>

View File

@@ -0,0 +1,342 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<!--Setup-->
<string name="setup_title">Briar Kurulum</string>
<string name="setup_explanation">Briar hesabınız bulutta değil, cihazınızda şifreli olarak saklanır. Briar\'ı kaldırırsanız veya şifrenizi unutursanız, hesabınızı ve verilerinizi kurtarmanın bir yolu yoktur.</string>
<string name="choose_nickname">Kullanıcı adınızı belirleyin</string>
<string name="choose_password">Parolanızı belirleyin</string>
<string name="confirm_password">Parolanızı doğrulayın</string>
<string name="name_too_long">İsim çok uzun</string>
<string name="password_too_weak">Parola çok zayıf</string>
<string name="passwords_do_not_match">Girdiğiniz iki parola uyuşmuyor</string>
<string name="create_account_button">Hesabı Oluştur</string>
<!--Login-->
<string name="enter_password">Parolanızı girin</string>
<string name="try_again">Parola yanlış, tekrar deneyin</string>
<string name="sign_in_button">Giriş Yap</string>
<string name="forgotten_password">Parolamı unuttum</string>
<string name="dialog_title_lost_password">Kayıp Parola</string>
<string name="dialog_message_lost_password">Briar hesabınız, bulutta değil şifreli olarak cihazınızda saklanır, bu nedenle şifrenizi sıfırlayamıyoruz. Hesabınızı silmek ve tekrar başlamak ister misiniz? \n\nUyarı: Kimlikleriniz, kişileriniz ve iletileriniz kaybolur.</string>
<string name="startup_failed_notification_title">Briar başlayamadı</string>
<string name="startup_failed_notification_text">Briar\'ı tekrar kurmanız gerekli.</string>
<string name="startup_failed_activity_title">Briar Başlangıç Hatası</string>
<string name="startup_failed_db_error">Bazı nedenlerden dolayı, Briar veritabanınız onarılamayacak kadar bozuk. Hesabınız, verileriniz ve tüm iletişim bağlantılarınız kayboldu. Ne yazık ki, Briar\'ı yeniden yükleyip yeni bir hesap oluşturmanız gerekiyor.</string>
<string name="startup_failed_service_error">Briar gerekli bir eklentiyi başlatamadı. Briar\'ı yeniden yüklemek genellikle bu sorunu çözer. Bununla birlikte, lütfen Briar\'ın verilerinizi depolamak için merkezi sunucuları kullanmadığından hesabınızı ve onunla ilişkili tüm verileri kaybedeceğinizi unutmayın.</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Gezinme çekmecesini aç</string>
<string name="nav_drawer_close_description">Gezinme çekmecesini kapat</string>
<string name="contact_list_button">Kişiler</string>
<string name="groups_button">Özel Gruplar</string>
<string name="forums_button">Forumlar</string>
<string name="blogs_button">Bloglar</string>
<string name="settings_button">Ayarlar</string>
<string name="sign_out_button">Oturumu Kapat</string>
<!--Transports-->
<string name="transport_tor">İnternet</string>
<string name="transport_bt">Bluetooth</string>
<string name="transport_lan">Wi-Fi</string>
<!--Notifications-->
<string name="ongoing_notification_title">Briar\'a giriş yapıldı</string>
<string name="ongoing_notification_text">Briar\'ı açmak için dokunun</string>
<plurals name="private_message_notification_text">
<item quantity="one">Yeni özel mesaj.</item>
<item quantity="other">%d yeni özel mesaj.</item>
</plurals>
<plurals name="group_message_notification_text">
<item quantity="one">Yeni grup mesajı.</item>
<item quantity="other">%d yeni grup mesajı.</item>
</plurals>
<plurals name="forum_post_notification_text">
<item quantity="one">Yeni forum iletisi.</item>
<item quantity="other">%d yeni forum iletisi.</item>
</plurals>
<plurals name="blog_post_notification_text">
<item quantity="one">Yeni blog iletisi.</item>
<item quantity="other">%d yeni blog iletisi.</item>
</plurals>
<!--Misc-->
<string name="now">şimdi</string>
<string name="show">Göster</string>
<string name="hide">Gizle</string>
<string name="ok">Tamam</string>
<string name="cancel">İptal</string>
<string name="got_it">Anladım</string>
<string name="delete">Sil</string>
<string name="accept">Onayla</string>
<string name="decline">Reddet</string>
<string name="options">Seçenekler</string>
<string name="online">Çevrimiçi</string>
<string name="offline">Çevrimdışı</string>
<string name="send">Gönder</string>
<string name="allow">İzin ver</string>
<string name="open"></string>
<string name="no_data">Bilgi yok</string>
<string name="ellipsis"></string>
<string name="text_too_long">Girilen metin çok uzun</string>
<string name="show_onboarding">Yardım Penceresini Göster</string>
<!--Contacts and Private Conversations-->
<string name="no_contacts">Görünüşe göre burada yeni başladınız ve henüz bir iletişim kurmadınız.\n\n en üstte bulunan + simgesine dokunun ve talimatları izleyerek listenize bazı arkadaşlar ekleyin.\n\nLütfen unutmayın: Gelecekte başkalarının kimliğinize bürünmesini veya mesajlarınızı okumasını önlemek için yeni kişileri sadece yüz yüze ekleyebilirsiniz.</string>
<string name="date_no_private_messages">Hiç mesaj yok.</string>
<string name="no_private_messages">Bu görüşme penceresidir.\n\n Henüz hiç bir şey yazılmamış gibi görünüyor.\n\nBir konuşmaya başlamak için alt taraftaki giriş alanına hafifçe dokunun.</string>
<string name="message_hint">Mesaj yazın</string>
<string name="delete_contact">Kişiyi sil</string>
<string name="dialog_title_delete_contact">Kişi Silmeyi Onayla</string>
<string name="dialog_message_delete_contact">Bu kişiyi ve bu kişiyle ilgili tüm iletileri kaldırmak istediğinize emin misiniz?</string>
<string name="contact_deleted_toast">Kişi silindi</string>
<!--Adding Contacts-->
<string name="add_contact_title">Kişi ekle</string>
<string name="your_nickname">Kullanmak istediğiniz kimliğinizi seçin:</string>
<string name="face_to_face">Kişi olarak eklemek istediğiniz kişiyle buluşmanız gerekir.\n\nBu, gelecekte başkalarının sizin kimliğinize bürünmesini veya mesajlarınızı okumasını engelleyecektir.</string>
<string name="continue_button">Devam et</string>
<string name="your_invitation_code">Davetiye kodunuz</string>
<string name="enter_invitation_code">Lütfen kişinizin davetiye kodunu girin:</string>
<string name="searching_format">Davet kodu ile kişi arama %06d\u2026</string>
<string name="connection_failed">Bağlantı hatası</string>
<string name="could_not_find_contact">Briar yakınınızda bulunduğunuz kişiyi bulamadı.</string>
<string name="try_again_button">Tekrar deneyin</string>
<string name="connected_to_contact">Kişiye bağlanıldı</string>
<string name="calculating_confirmation_code">Doğrulama kodu hesaplanıyor\u2026</string>
<string name="your_confirmation_code">Doğrulama kodunuz</string>
<string name="enter_confirmation_code">Lütfen kişinizin doğrulama kodunu girin:</string>
<string name="waiting_for_contact">Şu kişi için bekleniyor:\u2026</string>
<string name="waiting_for_contact_to_scan">Kişinin QR kodu taraması ve bağlantı kurması bekleniyor\u2026</string>
<string name="exchanging_contact_details">Kişi ayrıntıları karşılıklı paylaşılıyor\u2026</string>
<string name="codes_do_not_match">Kodlar uyuşmadı</string>
<string name="interfering">Bu, birisinin bağlantınızı kesmeye çalıştığı anlamına gelebilir</string>
<string name="contact_added_toast">Kişi eklendi: %s</string>
<string name="contact_already_exists"> %s kişisi zaten var</string>
<string name="contact_exchange_failed">Kişi değişimi hatası</string>
<string name="qr_code_invalid">QR kod hatalı</string>
<string name="connecting_to_device">Cihaza bağlanıyor\u2026</string>
<string name="authenticating_with_device">Cihazla kimlik doğrulama\u2026</string>
<string name="connection_aborted_local">Bağlantı bizim tarafımızca iptal edildi! Bu, birisinin bağlantınızı kesmeye çalıştığı anlamına gelebilir</string>
<string name="connection_aborted_remote">Bağlantı kişi tarafından kesildi! Bu, birisinin bağlantınızı kesmeye çalıştığı anlamına gelebilir</string>
<!--Introductions-->
<string name="introduction_onboarding_title">Kişilerinizi tanıştırın</string>
<string name="introduction_onboarding_text">Kişilerinizi birbirinize tanıtabilirsiniz, bu nedenle Briar\'a bağlanmak için şahsen bir araya gelmeniz gerekmez.</string>
<string name="introduction_activity_title">Kişi seç</string>
<string name="introduction_message_title">Kişileri Tanıştırın</string>
<string name="introduction_button">Tanıştır</string>
<string name="introduction_sent">Tanıştırma isteğiniz gönderildi.</string>
<string name="introduction_error">Tanıştırma isteği yaparken bir hata oluştu.</string>
<string name="introduction_response_error">Tanışma isteğine yanıt verirken hata oluştu</string>
<string name="introduction_request_sent">%1$s ile %2$s kişilerini tanıştırmak istediniz.</string>
<string name="introduction_request_received"> %1$s sizi %2$s ile tanıştırmak istedi. Kişi listesine %2$s kişisini eklemek ister misiniz?</string>
<string name="introduction_request_exists_received">%1$s sizi %2$s ile tanıştırmak istiyor, ancak %2$s zaten kişi listenizde ekli. %1$s, bunu bilmediğinden, siz yine de yanıt verebilirsiniz:</string>
<string name="introduction_request_answered_received">%1$s sizi %2$s ile tanıştırmak istedi.</string>
<string name="introduction_response_accepted_sent">%1$s kişisinin tanıştırma isteğini kabul ettiniz.</string>
<string name="introduction_response_declined_sent">%1$s kişisinin tanıştırma isteğini reddettiniz.</string>
<string name="introduction_response_accepted_received">%1$s, %2$s ile tanışmayı kabul etti.</string>
<string name="introduction_response_declined_received">%1$s, %2$s ile tanışmayı reddetti.</string>
<string name="introduction_response_declined_received_by_introducee">%1$s, %2$s kişisinin tanışmayı reddettiğini söyledi.</string>
<plurals name="introduction_notification_text">
<item quantity="one">Yeni kişi eklendi.</item>
<item quantity="other">%d yeni kişi eklendi.</item>
</plurals>
<!--Private Groups-->
<string name="groups_list_empty">Şu anda herhangi bir gruba katılamazsınız.\n\n Kendiniz bir grup oluşturmak için + simgesini tıklayın veya kişilerinizden sizi gruplarından birine davet etmesini isteyin.</string>
<string name="groups_created_by">%s tarafından oluşturuldu</string>
<plurals name="messages">
<item quantity="one">%d mesaj</item>
<item quantity="other">%d mesaj</item>
</plurals>
<string name="groups_group_is_empty">Bu grup boş</string>
<string name="groups_group_is_dissolved">Bu grup dağıldı</string>
<string name="groups_remove">Sil</string>
<string name="groups_create_group_title">Özel Grup Oluştur</string>
<string name="groups_create_group_button">Grup Oluştur</string>
<string name="groups_create_group_invitation_button">Davetiye Gönder</string>
<string name="groups_invitation_sent">Grup davetiyesi gönderildi</string>
<string name="groups_message_sent">Mesaj gönderildi</string>
<string name="groups_member_list">Üye Listesi</string>
<string name="groups_invite_members">Üyeleri Davet Edin</string>
<string name="groups_member_created_you">Grubu siz oluşturdunuz</string>
<string name="groups_member_created">Grubu %s oluşturdu</string>
<string name="groups_member_joined_you">Gruba katıldınız</string>
<string name="groups_member_joined">%s gruba katıldı</string>
<string name="groups_leave">Gruptan Ayrıl</string>
<string name="groups_leave_dialog_title">Gruptan Ayrılmayı Onayla</string>
<string name="groups_leave_dialog_message">Bu gruptan ayrılmak istediğinizden emin misiniz?</string>
<string name="groups_dissolve">Grubu Dağıt</string>
<string name="groups_dissolve_dialog_title">Grubu Dağıtmayı Onayla</string>
<string name="groups_dissolve_dialog_message">Bu grubu dağıtmak istediğinizden emin misiniz?\n\nDiğer tüm üyeler sohbetlerine devam edemeyecek ve en yeni mesajları alamayacak.</string>
<string name="groups_dissolve_button">Dağıt</string>
<string name="groups_dissolved_dialog_title">Grup Dağıtıldı</string>
<string name="groups_dissolved_dialog_message">Bu grubun kurucusu, grubu dağıttı.\n\nArtık gruba mesaj yazamazsınız ve yazılmış olan mesajları alamayabilirsiniz.</string>
<!--Private Group Invitations-->
<string name="groups_invitations_title">Grup Davetleri</string>
<string name="groups_invitations_invitation_sent">%1$s kişisini \"%2$s\" grubuna davet ettiniz.</string>
<string name="groups_invitations_invitation_received">%1$s sizi \"%2$s\" grubuna davet etti.</string>
<string name="groups_invitations_joined">Gruba katıldı</string>
<string name="groups_invitations_declined">Grup davetiyesi reddedildi</string>
<plurals name="groups_invitations_open">
<item quantity="one">%d açık grup davetiyesi</item>
<item quantity="other">%d açık grup davetiyesi</item>
</plurals>
<string name="groups_invitations_response_accepted_sent">%s kişisinden gelen grup davetini kabul ettiniz.</string>
<string name="groups_invitations_response_declined_sent">%s kişisinden gelen grup davetini reddettiniz.</string>
<string name="groups_invitations_response_accepted_received">%s grup davetini kabul etti.</string>
<string name="groups_invitations_response_declined_received">%s grup davetini reddetti.</string>
<string name="sharing_status_groups">Sadece grubu oluşturan kişi gruba yeni üyeler davet edebilir. Aşağıda, grubun şimdiki üyeleri bulunmaktadır.</string>
<!--Private Groups Revealing Contacts-->
<string name="groups_reveal_contacts">Kişileri Göster</string>
<string name="groups_reveal_dialog_message">Kişileri bu grubun şimdiki ve gelecekteki üyelerinin tümüne gösterip göstermeyeceğinizi seçebilirsiniz.\n\nKişileri görünür yapmak , grupla olan bağlantınızı daha hızlı ve güvenilir hale getirir, çünkü grubun oluşturucusu çevrimdışı olduğunda bile görünen kişilerle iletişim kurabilirsiniz.</string>
<string name="groups_reveal_visible">Kişi ilişkisi grup tarafından görülebilir</string>
<string name="groups_reveal_visible_revealed_by_us">Kişi ilişkisi grup tarafından görülebilir (siz bildirdiniz)</string>
<string name="groups_reveal_visible_revealed_by_contact">Kişi ilişkileri grup tarafından görülebilir (%s görünür yaptı)</string>
<string name="groups_reveal_invisible">Kişi ilişkisi grup tarafından görülemez</string>
<!--Forums-->
<string name="no_forums">Şu anda hiç forumunuz yok.\n\nÜstteki + simgesine dokunarak neden kendiniz yeni bir tane oluşturmuyorsunuz?\n\nAyrıca kişilerinizden sizinle forum paylaşmalarını isteyebilirsiniz.</string>
<string name="create_forum_button">Forumu Ouştur</string>
<string name="forum_created_toast">Forum Oluşturuldu</string>
<string name="no_forum_posts">Bu forum boş\n\nİlk gönderiyi oluşturmak için üstteki kalem simgesini kullanın.\n\nBurada kendinizi yalnız hissediyor musunuz? Bu forumu daha fazla kişiyle paylaşın!</string>
<string name="no_posts">Gönderi yok</string>
<plurals name="posts">
<item quantity="one">%d gönderi</item>
<item quantity="other">%d gönderi</item>
</plurals>
<string name="forum_new_entry_posted">Forum girdisi gönderildi</string>
<string name="forum_new_message_hint">Yeni Girdi</string>
<string name="forum_message_reply_hint">Yeni Cevap</string>
<string name="btn_reply">Cevapla</string>
<string name="forum_leave">Forumdan Ayrıl</string>
<string name="dialog_title_leave_forum">Forumdan Ayrılmayı Onayla</string>
<string name="dialog_message_leave_forum">Bu forumdan ayrılmak istediğinizden emin misiniz? Bu forumu paylaştığınız kişilerin, bu foruma ilişkin güncellemeleri almaları kesilebilir.</string>
<string name="dialog_button_leave">Ayrıl</string>
<string name="forum_left_toast">Forumdan Ayrıl</string>
<!--Forum Sharing-->
<string name="forum_share_button">Forumu Paylaş</string>
<string name="contacts_selected">Kişiler Seçildi</string>
<string name="activity_share_toolbar_header">Kişi Seç</string>
<string name="no_contacts_selector">Görünüşe göre burada yenisiniz ve henüz iletişim kuramıyorsunuz.\n\nİlk kişinizi ekledikten sonra lütfen buraya geri gelin.</string>
<string name="forum_shared_snackbar">Forum, seçilen kişiler ile paylaşıldı</string>
<string name="forum_share_error">Forumu paylaşırken bir hata meydana geldi.</string>
<string name="forum_invitation_received">%1$s sizinle \"%2$s\" forumunu paylaştı.</string>
<string name="forum_invitation_sent">\"%1$s\" forumunu %2$s ile paylaştınız.</string>
<string name="forum_invitations_title">Forum Davetleri</string>
<string name="forum_invitation_exists">Zaten bu foruma ait bir davetiyeyi kabul ettiniz. Daha fazla daveti kabul etmek forumda iletişimi artıracak ve güçlenecektir.</string>
<string name="forum_joined_toast">Foruma Katınıldı</string>
<string name="forum_declined_toast">Forum Daveti Reddedildi</string>
<string name="shared_by_format">%s tarafından paylaşıldı</string>
<string name="forum_invitation_already_sharing">Zaten paylaşılıyor</string>
<string name="forum_invitation_response_accepted_sent">%s tarafından yapılan forum davetini kabul ettiniz.</string>
<string name="forum_invitation_response_declined_sent">%s tarafından yapılan forum davetini reddettiniz.</string>
<string name="forum_invitation_response_accepted_received">%s forum davetini kabul etti.</string>
<string name="forum_invitation_response_declined_received">%s forum davetini reddetti.</string>
<string name="sharing_status">Paylaşım Durumu</string>
<string name="sharing_status_forum">Bir forumun herhangi bir üyesi o forumu arkadaşlarıyla paylaşabilir. Bu forumu aşağıdaki kişilerle paylaşıyorsunuz. Göremediğiniz diğer üyeler de olabilir</string>
<string name="shared_with">%1$d ile paylaşıldı (%2$d çevrimiçi)</string>
<plurals name="forums_shared">
<item quantity="one">%d kişiler tarafından paylaşılan forum</item>
<item quantity="other">%d kişiler tarafından paylaşılan forum.</item>
</plurals>
<string name="nobody">Hiç kimse</string>
<!--Blogs-->
<string name="blogs_other_blog_empty_state">Bu blog şu anda boş.\n\n Yazar henüz bir şey yazmadı ya da sizinle bu blogu paylaşan kişinin çevrimiçi olması gerekiyor, böylece gönderiler senkronize edilecektir.</string>
<string name="read_more">daha fazlasını oku</string>
<string name="blogs_write_blog_post">Blog Gönderisi Yaz</string>
<string name="blogs_write_blog_post_body_hint">Metninizi buraya yazın</string>
<string name="blogs_publish_blog_post">Yayınla</string>
<string name="blogs_blog_post_created">Blog Gönderisi Oluşturuldu</string>
<string name="blogs_blog_post_received">Yeni Blog Gönderisi Alındı</string>
<string name="blogs_blog_post_scroll_to">Kaydırma</string>
<string name="blogs_feed_empty_state">Bu, global blog yayınıdır\n\nHenüz birileri birşeyler yazmış gibi gibi görünmüyor\n\nİlk gönderiyi yazmak için kalem simgesine dokunun.</string>
<string name="blogs_remove_blog">Blog\'u sil</string>
<string name="blogs_remove_blog_dialog_message">Bu blog\'u ve tüm yayınları kaldırmak istediğinizden emin misiniz? Bu, blog\'u başkalarının cihazlarından kaldırmaz.</string>
<string name="blogs_remove_blog_ok">Blog\'u Sil</string>
<string name="blogs_blog_removed">Blog Silindi</string>
<string name="blogs_reblog_button">Tekrar Blogla</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Blog\'u Paylaş</string>
<string name="blogs_sharing_error">Blog\'u paylaşırken bir hata meydana geldi.</string>
<string name="blogs_sharing_button">Blog\'u Paylaş</string>
<string name="blogs_sharing_snackbar">Blog seçilen kişilerle paylaşıldı</string>
<string name="blogs_sharing_response_accepted_sent">%s kişisinden gelen blog davetini kabul ettiniz.</string>
<string name="blogs_sharing_response_declined_sent">%s kişisinden gelen blog davetini reddettiniz.</string>
<string name="blogs_sharing_response_accepted_received">%s blog davetini kabul etti.</string>
<string name="blogs_sharing_response_declined_received">%s blog davetini reddetti.</string>
<string name="blogs_sharing_invitations_title">Blog Davetleri</string>
<string name="blogs_sharing_joined_toast">Blog Takip Edildi</string>
<string name="blogs_sharing_declined_toast">Blog Daveti Reddedildi</string>
<string name="sharing_status_blog">Bir blog\'a abone olan herkes, blog\'u kişileriyle paylaşabilir. Bu blog\'u şu kişilerle paylaşıyorsunuz. Göremediğiniz diğer aboneler de olabilir.</string>
<!--RSS Feeds-->
<string name="blogs_rss_feeds_import">RSS kaynaklarını içeri aktar</string>
<string name="blogs_rss_feeds_import_button">İçeri Aktar</string>
<string name="blogs_rss_feeds_import_hint">URL ya da RSS kaynağı girin</string>
<string name="blogs_rss_feeds_import_error">Üzgünüz! RSS beslemenizi içe aktarırken bir hata oluştu.</string>
<string name="blogs_rss_feeds_manage">RSS\'leri Yönet</string>
<string name="blogs_rss_feeds_manage_imported">İçeri Aktarıldı:</string>
<string name="blogs_rss_feeds_manage_author">Yazar:</string>
<string name="blogs_rss_feeds_manage_updated">Son Güncelleme:</string>
<string name="blogs_rss_remove_feed">Kaynağı Sil</string>
<string name="blogs_rss_remove_feed_dialog_message">Bu yayını ve tüm yayınlarını kaldırmak istediğinizden emin misiniz? \nPaylaştığınız yayınlar diğer kullanıcıların cihazlarından kaldırılamaz.</string>
<string name="blogs_rss_remove_feed_ok">Kaynağı Sil</string>
<string name="blogs_rss_feeds_manage_delete_error">Besleme silinemedi!</string>
<string name="blogs_rss_feeds_manage_empty_state">İçeri aktarılmış herhangi bir RSS beslemeniz yok.\n\nBir tane eklemek için neden ekranın üst tarafındaki + simgesini kullanmıyorsunuz?</string>
<string name="blogs_rss_feeds_manage_error">Beslemeleriniz yüklenirken bir hata meydana geldi. Lütfen daha sonra tekrar deneyin.</string>
<!--Settings Network-->
<string name="network_settings_title">Ağlar</string>
<string name="bluetooth_setting">Bluetooth ile Bağlan</string>
<string name="bluetooth_setting_enabled">Yakınlardaki her kişi</string>
<string name="bluetooth_setting_disabled">Yalnızca kişi eklerken</string>
<string name="tor_network_setting">Tor ile Bağlan</string>
<string name="tor_network_setting_never">Asla</string>
<string name="tor_network_setting_wifi">Yanlızca Wi-Fi kullanırken</string>
<string name="tor_network_setting_always">Mobil veri veya Wi-Fi kullanırken</string>
<!--Settings Security and Panic-->
<string name="security_settings_title">Güvenlik</string>
<string name="change_password">Parola değiştir</string>
<string name="current_password">Geçerli parolanızı girin:</string>
<string name="choose_new_password">Yeni parolanızı girin:</string>
<string name="confirm_new_password">Yeni parolanızı tekrar girin:</string>
<string name="password_changed">Parolanız başarıyla değişti.</string>
<string name="panic_setting">Panik buton ayarları</string>
<string name="panic_setting_title">Panik butonu</string>
<string name="panic_setting_hint">Panik düğmesi uygulaması kullandığınızda Briar\'ın nasıl tepki vereceğini yapılandırma</string>
<string name="panic_app_setting_title">Panik Butonu Uygulaması</string>
<string name="unknown_app">bilinmeyen bir uygulama</string>
<string name="panic_app_setting_summary">Kurulmuş bir uygulama yok</string>
<string name="panic_app_setting_none">Yok</string>
<string name="dialog_title_connect_panic_app">Panik Uygulaması Doğrulama</string>
<string name="dialog_message_connect_panic_app">%1$s kişisinin yıkıcı panik düğmesi eylemlerini tetiklemesine izin vermek istediğinizden emin misiniz?</string>
<string name="lock_setting_title">Çıkış Yap</string>
<string name="lock_setting_summary">Panik Butonuna basıldığında Briar\'dan çıkış yap</string>
<string name="purge_setting_title">Hesabı Sil</string>
<string name="purge_setting_summary">Bir panik düğmesine basıldığında Briar hesabınızı silin. Dikkat: Bu, kimliklerinizi, kayıtlarınızı ve iletilerinizi kalıcı olarak silecektir.</string>
<string name="uninstall_setting_title">Briar\'ı Kaldır</string>
<string name="uninstall_setting_summary">Bu, bir panik olayında manuel onay gerektirir</string>
<!--Settings Notifications-->
<string name="notification_settings_title">Bildirimler</string>
<string name="notify_vibration_setting">Titretişim</string>
<string name="notify_sound_setting">Ses</string>
<string name="notify_sound_setting_default">Varsayılan zil sesi</string>
<string name="notify_sound_setting_disabled">Yok</string>
<string name="choose_ringtone_title">Zil sesi seçin</string>
<!--Settings Feedback-->
<string name="feedback_settings_title">Geri bildirim</string>
<string name="send_feedback">Geri bildirim gönder</string>
<!--Link Warning-->
<string name="link_warning_title">Uyarı Bağlantısı</string>
<string name="link_warning_intro">Aşağıdaki bağlantıyı harici bir uygulamayla açmak üzeresiniz.</string>
<string name="link_warning_text">Bu kimliğinizi ele geçirmek için kullanılabilir. Bu bağlantıyı gönderen kişiye güvenip güvenmediğinize karar verin ve Orfox ile açmayı deneyin.</string>
<string name="link_warning_open_link">Bağlantı</string>
<!--Crash Reporter-->
<string name="crash_report_title">Briar Çökme Raporu</string>
<string name="briar_crashed">Üzgünüz, Briar çöktü.</string>
<string name="not_your_fault">Bu senin hatan değil.</string>
<string name="please_send_report">Bize bir çökme raporu göndererek Briar\'ı daha iyi bir hale getirmemize yardımcı olun.</string>
<string name="report_is_encrypted">Raporun şifrelendiğine ve güvenli bir şekilde gönderildiğine söz veriyoruz.</string>
<string name="feedback_title">Geri bildirim</string>
<string name="describe_crash">Ne olduğunu bize anlatın (isteğe bağlı)</string>
<string name="enter_feedback">Geri bildiriminizi girin</string>
<string name="optional_contact_email">E-posta adresiniz (isteğe bağlı)</string>
<string name="include_debug_report_crash">Çökme ile ilgili anonim verileri içerir</string>
<string name="include_debug_report_feedback">Bu cihazla ilgili anonim verileri içerir</string>
<string name="could_not_load_report_data">Rapor verileri yüklenemedi.</string>
<string name="send_report">Raporu gönder</string>
<string name="close">Kapat</string>
<string name="dev_report_saved">Rapor kaydedildi. Briar\'a bir sonraki girişinizde gönderilecektir.</string>
<!--Sign Out-->
<string name="progress_title_logout">Briar\'dan çıkılıyor...</string>
<!--Screen Filters & Tapjacking-->
</resources>

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string translatable="false" name="app_name">Briar Beta</string>
<!-- Setup -->
<string name="setup_title">Briar Setup</string>
@@ -99,28 +98,17 @@
<!-- Adding Contacts -->
<string name="add_contact_title">Add a Contact</string>
<string name="your_nickname">Choose the identity you want to use:</string>
<string name="face_to_face">You must meet up with the person you want to add as a contact.\n\nThis will prevent anyone from impersonating you or reading your messages in future.</string>
<string name="continue_button">Continue</string>
<string name="your_invitation_code">Your invitation code is</string>
<string name="enter_invitation_code">Please enter your contact\'s invitation code:</string>
<string name="searching_format">Searching for contact with invitation code %06d\u2026</string>
<string name="connection_failed">Connection failed</string>
<string name="could_not_find_contact">Briar could not find your contact nearby</string>
<string name="try_again_button">Try Again</string>
<string name="connected_to_contact">Connected to contact</string>
<string name="calculating_confirmation_code">Calculating confirmation code\u2026</string>
<string name="your_confirmation_code">Your confirmation code is</string>
<string name="enter_confirmation_code">Please enter your contact\'s confirmation code:</string>
<string name="waiting_for_contact">Waiting for contact\u2026</string>
<string name="waiting_for_contact_to_scan">Waiting for contact to scan and connect\u2026</string>
<string name="exchanging_contact_details">Exchanging contact details\u2026</string>
<string name="codes_do_not_match">Codes do not match</string>
<string name="interfering">This could mean that someone is trying to interfere with your connection</string>
<string name="contact_added_toast">Contact added: %s</string>
<string name="contact_already_exists">Contact %s already exists</string>
<string name="contact_exchange_failed">Contact exchange failed</string>
<string name="qr_code_invalid">The QR code is invalid</string>
<string name="camera_error">Camera error</string>
<string name="connecting_to_device">Connecting to device\u2026</string>
<string name="authenticating_with_device">Authenticating with device\u2026</string>
<string name="connection_aborted_local">Connection aborted by us! This could mean that someone is trying to interfere with your connection</string>
@@ -357,6 +345,7 @@
<string name="notify_sound_setting_default">Default ringtone</string>
<string name="notify_sound_setting_disabled">None</string>
<string name="choose_ringtone_title">Choose ringtone</string>
<string name="cannot_load_ringtone">Cannot load ringtone</string>
<!-- Settings Feedback -->
<string name="feedback_settings_title">Feedback</string>

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