Compare commits

..

1 Commits

Author SHA1 Message Date
akwizgran
d311557f09 Check whether first visible message ID is null before storing. 2017-05-29 10:05:49 +01:00
123 changed files with 1475 additions and 2727 deletions

View File

@@ -1,4 +1,4 @@
image: registry.gitlab.com/fdroid/ci-images-base:latest
image: registry.gitlab.com/fdroid/ci-images:client-latest
cache:
paths:

View File

@@ -25,38 +25,38 @@ android {
dependencies {
compile project(':bramble-core')
compile fileTree(dir: 'libs', include: '*.jar')
compile fileTree(dir: 'libs', include: ['*.jar'])
provided 'javax.annotation:jsr250-api:1.0'
}
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-2015-12-01.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.7.6-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.7.6-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.7.6-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.7.6-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 '9bcdaf0a7ba0933735328d8ec466c25c25dbb459efc2bce9e55c774eabea5162'
}
task verifyTorBinaryArm(type: Verify, dependsOn: 'downloadTorBinaryArm') {
src "$torBinaryDir/tor_arm.zip"
algorithm 'SHA-256'
checksum '1da6008663a8ad98b349e62acbbf42c379f65ec504fa467cb119c187cd5a4c6b'
checksum '83272962eda701cd5d74d2418651c4ff0f0b1dff51f558a292d1a1c42bf12146'
}
task verifyTorBinaryArmPie(type: Verify, dependsOn: 'downloadTorBinaryArmPie') {
src "$torBinaryDir/tor_arm_pie.zip"
algorithm 'SHA-256'
checksum 'eb061f880829e05f104690ac744848133f2dacef04759d425a2cff0df32c271e'
checksum 'd0300d1e45de11ebb24ed62b9c492be9c2e88590b7822195ab38c7a76ffcf646'
}
task verifyTorBinaryX86(type: Verify, dependsOn: 'downloadTorBinaryX86') {
src "$torBinaryDir/tor_x86.zip"
algorithm 'SHA-256'
checksum 'f5308aff8303daca082f82227d02b51ddedba4ab1d1420739ada0427ae5dbb41'
checksum 'b8813d97b01ee1b9c9a4233c1b9bbe9f9f6b494ae6f9cbd84de8a3911911615e'
}
task verifyTorBinaryX86Pie(type: Verify, dependsOn: 'downloadTorBinaryX86Pie') {
src "$torBinaryDir/tor_x86_pie.zip"
algorithm 'SHA-256'
checksum '889a6c81ac73d05d35ed610ca5a913cee44d333e4ae1749c2a107f2f7dd8197b'
checksum '9c66e765aa196dc089951a1b2140cc8290305c2fcbf365121f99e01a233baf4e'
}
project.afterEvaluate {

View File

@@ -7,12 +7,12 @@ apply plugin: 'witness'
dependencies {
compile "com.google.dagger:dagger:2.0.2"
compile 'com.google.dagger:dagger-compiler:2.0.2'
compile 'com.google.code.findbugs:jsr305:3.0.2'
compile 'com.google.code.findbugs:jsr305:3.0.1'
testCompile 'junit:junit:4.12'
testCompile "org.jmock:jmock:2.8.2"
testCompile "org.jmock:jmock-junit4:2.8.2"
testCompile "org.jmock:jmock-legacy:2.8.2"
testCompile "org.jmock:jmock:2.8.1"
testCompile "org.jmock:jmock-junit4:2.8.1"
testCompile "org.jmock:jmock-legacy:2.8.1"
testCompile "org.hamcrest:hamcrest-library:1.3"
testCompile "org.hamcrest:hamcrest-core:1.3"
}
@@ -21,7 +21,7 @@ dependencyVerification {
verify = [
'com.google.dagger:dagger:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
'com.google.dagger:dagger-compiler:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
'com.google.code.findbugs:jsr305:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.code.findbugs:jsr305:c885ce34249682bc0236b4a7d56efcc12048e6135a5baf7a9cde8ad8cda13fcd',
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'com.google.dagger:dagger-producers:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
'com.google.guava:guava:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',

View File

@@ -137,8 +137,7 @@ public interface CryptoComponent {
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
/** Encodes the pseudo-random tag that is used to recognise a stream. */
void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
long streamNumber);
void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber);
/**
* Signs the given byte[] with the given PrivateKey.

View File

@@ -4,11 +4,6 @@ import org.briarproject.bramble.api.crypto.SecretKey;
public interface TransportConstants {
/**
* The current version of the transport protocol.
*/
int PROTOCOL_VERSION = 3;
/**
* The length of the pseudo-random tag in bytes.
*/
@@ -19,22 +14,21 @@ public interface TransportConstants {
*/
int STREAM_HEADER_NONCE_LENGTH = 24;
/**
* The length of the stream header initialisation vector (IV) in bytes.
*/
int STREAM_HEADER_IV_LENGTH = STREAM_HEADER_NONCE_LENGTH - 8;
/**
* The length of the message authentication code (MAC) in bytes.
*/
int MAC_LENGTH = 16;
/**
* The length of the stream header plaintext in bytes. The stream header
* contains the protocol version, stream number and frame key.
*/
int STREAM_HEADER_PLAINTEXT_LENGTH = 2 + 8 + SecretKey.LENGTH;
/**
* The length of the stream header in bytes.
*/
int STREAM_HEADER_LENGTH = STREAM_HEADER_NONCE_LENGTH
+ STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH;
int STREAM_HEADER_LENGTH = STREAM_HEADER_IV_LENGTH + SecretKey.LENGTH
+ MAC_LENGTH;
/**
* The length of the frame nonce in bytes.

View File

@@ -1,9 +1,8 @@
plugins {
id 'java'
id 'net.ltgt.apt' version '0.9'
id 'idea'
id "java"
id "net.ltgt.apt" version "0.9"
id "idea"
}
sourceCompatibility = 1.6
targetCompatibility = 1.6
@@ -11,18 +10,17 @@ apply plugin: 'witness'
dependencies {
compile project(':bramble-api')
compile 'com.madgag.spongycastle:core:1.56.0.0'
compile 'com.h2database:h2:1.4.192' // This is the last version that supports Java 1.6
compile 'org.bitlet:weupnp:0.1.4'
compile fileTree(dir: 'libs', include: '*.jar')
compile 'com.madgag.spongycastle:core:1.54.0.0'
compile 'com.h2database:h2:1.4.190'
testCompile project(path: ':bramble-api', configuration: 'testOutput')
}
dependencyVerification {
verify = [
'com.madgag.spongycastle:core:5e791b0eaa9e0c4594231b44f616a52adddb7dccedeb0ad9ad74887e19499a23',
'com.h2database:h2:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
'org.bitlet:weupnp:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
'com.madgag.spongycastle:core:1e7fa4b19ccccd1011364ab838d0b4702470c178bbbdd94c5c90b2d4d749ea1e',
'com.h2database:h2:23ba495a07bbbb3bd6c3084d10a96dad7a23741b8b6d64b213459a784195a98c'
]
}

Binary file not shown.

View File

@@ -54,7 +54,6 @@ public class BrambleCoreModule {
c.inject(new IdentityModule.EagerSingletons());
c.inject(new LifecycleModule.EagerSingletons());
c.inject(new PluginModule.EagerSingletons());
c.inject(new PropertiesModule.EagerSingletons());
c.inject(new SyncModule.EagerSingletons());
c.inject(new SystemModule.EagerSingletons());
c.inject(new TransportModule.EagerSingletons());

View File

@@ -45,10 +45,8 @@ import static org.briarproject.bramble.api.invitation.InvitationConstants.CODE_B
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;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
class CryptoComponentImpl implements CryptoComponent {
@@ -414,11 +412,8 @@ class CryptoComponentImpl implements CryptoComponent {
}
@Override
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
long streamNumber) {
public void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber) {
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
if (protocolVersion < 0 || protocolVersion > MAX_16_BIT_UNSIGNED)
throw new IllegalArgumentException();
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
throw new IllegalArgumentException();
// Initialise the PRF
@@ -426,14 +421,10 @@ class CryptoComponentImpl implements CryptoComponent {
// The output of the PRF must be long enough to use as a tag
int macLength = prf.getDigestSize();
if (macLength < TAG_LENGTH) throw new IllegalStateException();
// The input is the protocol version as a 16-bit integer, followed by
// the stream number as a 64-bit integer
byte[] protocolVersionBytes = new byte[INT_16_BYTES];
ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0);
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
byte[] streamNumberBytes = new byte[INT_64_BYTES];
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
prf.update(streamNumberBytes, 0, streamNumberBytes.length);
// The input is the stream number as a 64-bit integer
byte[] input = new byte[INT_64_BYTES];
ByteUtils.writeUint64(streamNumber, input, 0);
prf.update(input, 0, input.length);
byte[] mac = new byte[macLength];
prf.doFinal(mac, 0);
// The output is the first TAG_LENGTH bytes of the MAC

View File

@@ -20,11 +20,9 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_NO
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_PLAINTEXT_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
@NotThreadSafe
@@ -119,7 +117,7 @@ class StreamDecrypterImpl implements StreamDecrypter {
private void readStreamHeader() throws IOException {
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
byte[] streamHeaderPlaintext = new byte[STREAM_HEADER_PLAINTEXT_LENGTH];
byte[] streamHeaderPlaintext = new byte[SecretKey.LENGTH];
// Read the stream header
int offset = 0;
while (offset < STREAM_HEADER_LENGTH) {
@@ -128,35 +126,21 @@ class StreamDecrypterImpl implements StreamDecrypter {
if (read == -1) throw new EOFException();
offset += read;
}
// Extract the nonce
// The nonce consists of the stream number followed by the IV
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
System.arraycopy(streamHeaderCiphertext, 0, streamHeaderNonce, 0,
STREAM_HEADER_NONCE_LENGTH);
ByteUtils.writeUint64(streamNumber, streamHeaderNonce, 0);
System.arraycopy(streamHeaderCiphertext, 0, streamHeaderNonce,
INT_64_BYTES, STREAM_HEADER_IV_LENGTH);
// Decrypt and authenticate the stream header
try {
cipher.init(false, streamHeaderKey, streamHeaderNonce);
int decrypted = cipher.process(streamHeaderCiphertext,
STREAM_HEADER_NONCE_LENGTH,
STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH,
STREAM_HEADER_IV_LENGTH, SecretKey.LENGTH + MAC_LENGTH,
streamHeaderPlaintext, 0);
if (decrypted != STREAM_HEADER_PLAINTEXT_LENGTH)
throw new RuntimeException();
if (decrypted != SecretKey.LENGTH) throw new RuntimeException();
} catch (GeneralSecurityException e) {
throw new FormatException();
}
// Check the protocol version
int receivedProtocolVersion =
ByteUtils.readUint16(streamHeaderPlaintext, 0);
if (receivedProtocolVersion != PROTOCOL_VERSION)
throw new FormatException();
// Check the stream number
long receivedStreamNumber = ByteUtils.readUint64(streamHeaderPlaintext,
INT_16_BYTES);
if (receivedStreamNumber != streamNumber) throw new FormatException();
// Extract the frame key
byte[] frameKeyBytes = new byte[SecretKey.LENGTH];
System.arraycopy(streamHeaderPlaintext, INT_16_BYTES + INT_64_BYTES,
frameKeyBytes, 0, SecretKey.LENGTH);
frameKey = new SecretKey(frameKeyBytes);
frameKey = new SecretKey(streamHeaderPlaintext);
}
}

View File

@@ -13,8 +13,7 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.inject.Provider;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
@Immutable
@@ -37,22 +36,22 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
AuthenticatedCipher cipher = cipherProvider.get();
long streamNumber = ctx.getStreamNumber();
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION, streamNumber);
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
crypto.encodeTag(tag, ctx.getTagKey(), streamNumber);
byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderIv);
SecretKey frameKey = crypto.generateSecretKey();
return new StreamEncrypterImpl(out, cipher, streamNumber, tag,
streamHeaderNonce, ctx.getHeaderKey(), frameKey);
streamHeaderIv, ctx.getHeaderKey(), frameKey);
}
@Override
public StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
SecretKey headerKey) {
AuthenticatedCipher cipher = cipherProvider.get();
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderIv);
SecretKey frameKey = crypto.generateSecretKey();
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderNonce,
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderIv,
headerKey, frameKey);
}
}

View File

@@ -18,11 +18,9 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_NO
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_PLAINTEXT_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
@NotThreadSafe
@@ -35,7 +33,7 @@ class StreamEncrypterImpl implements StreamEncrypter {
private final long streamNumber;
@Nullable
private final byte[] tag;
private final byte[] streamHeaderNonce;
private final byte[] streamHeaderIv;
private final byte[] frameNonce, frameHeader;
private final byte[] framePlaintext, frameCiphertext;
@@ -43,13 +41,13 @@ class StreamEncrypterImpl implements StreamEncrypter {
private boolean writeTag, writeStreamHeader;
StreamEncrypterImpl(OutputStream out, AuthenticatedCipher cipher,
long streamNumber, @Nullable byte[] tag, byte[] streamHeaderNonce,
long streamNumber, @Nullable byte[] tag, byte[] streamHeaderIv,
SecretKey streamHeaderKey, SecretKey frameKey) {
this.out = out;
this.cipher = cipher;
this.streamNumber = streamNumber;
this.tag = tag;
this.streamHeaderNonce = streamHeaderNonce;
this.streamHeaderIv = streamHeaderIv;
this.streamHeaderKey = streamHeaderKey;
this.frameKey = frameKey;
frameNonce = new byte[FRAME_NONCE_LENGTH];
@@ -116,23 +114,22 @@ class StreamEncrypterImpl implements StreamEncrypter {
}
private void writeStreamHeader() throws IOException {
// The header contains the protocol version, stream number and frame key
byte[] streamHeaderPlaintext = new byte[STREAM_HEADER_PLAINTEXT_LENGTH];
ByteUtils.writeUint16(PROTOCOL_VERSION, streamHeaderPlaintext, 0);
ByteUtils.writeUint64(streamNumber, streamHeaderPlaintext,
INT_16_BYTES);
System.arraycopy(frameKey.getBytes(), 0, streamHeaderPlaintext,
INT_16_BYTES + INT_64_BYTES, SecretKey.LENGTH);
// The nonce consists of the stream number followed by the IV
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
ByteUtils.writeUint64(streamNumber, streamHeaderNonce, 0);
System.arraycopy(streamHeaderIv, 0, streamHeaderNonce, INT_64_BYTES,
STREAM_HEADER_IV_LENGTH);
byte[] streamHeaderPlaintext = frameKey.getBytes();
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
System.arraycopy(streamHeaderNonce, 0, streamHeaderCiphertext, 0,
STREAM_HEADER_NONCE_LENGTH);
// Encrypt and authenticate the stream header key
System.arraycopy(streamHeaderIv, 0, streamHeaderCiphertext, 0,
STREAM_HEADER_IV_LENGTH);
// Encrypt and authenticate the frame key
try {
cipher.init(true, streamHeaderKey, streamHeaderNonce);
int encrypted = cipher.process(streamHeaderPlaintext, 0,
STREAM_HEADER_PLAINTEXT_LENGTH, streamHeaderCiphertext,
STREAM_HEADER_NONCE_LENGTH);
if (encrypted != STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH)
SecretKey.LENGTH, streamHeaderCiphertext,
STREAM_HEADER_IV_LENGTH);
if (encrypted != SecretKey.LENGTH + MAC_LENGTH)
throw new RuntimeException();
} catch (GeneralSecurityException badCipher) {
throw new RuntimeException(badCipher);

View File

@@ -70,7 +70,25 @@ class XSalsa20Poly1305AuthenticatedCipher implements AuthenticatedCipher {
byte[] subKey = new byte[SUBKEY_LENGTH];
xSalsa20Engine.processBytes(zero, 0, SUBKEY_LENGTH, subKey, 0);
// Clamp the subkey
// Reverse the order of the Poly130 subkey
//
// NaCl and libsodium use the first 32 bytes of XSalsa20 as the
// subkey for crypto_onetimeauth_poly1305, which interprets it
// as r[0] ... r[15], k[0] ... k[15]. See section 9 of the NaCl
// paper (http://cr.yp.to/highspeed/naclcrypto-20090310.pdf),
// where the XSalsa20 output is defined as (r, s, t, ...).
//
// BC's Poly1305 implementation interprets the subkey as
// k[0] ... k[15], r[0] ... r[15] (per poly1305_aes_clamp in
// the reference implementation).
//
// To be NaCl-compatible, we reverse the subkey.
System.arraycopy(subKey, 0, zero, 0, SUBKEY_LENGTH / 2);
System.arraycopy(subKey, SUBKEY_LENGTH / 2, subKey, 0,
SUBKEY_LENGTH / 2);
System.arraycopy(zero, 0, subKey, SUBKEY_LENGTH / 2,
SUBKEY_LENGTH / 2);
// Now we can clamp the correct part of the subkey
Poly1305KeyGenerator.clamp(subKey);
// Initialize Poly1305 with the subkey

View File

@@ -29,7 +29,6 @@ import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
@@ -127,8 +126,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (long streamNumber : inKeys.getWindow().getUnseen()) {
TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
streamNumber);
crypto.encodeTag(tag, inKeys.getTagKey(), streamNumber);
inContexts.put(new Bytes(tag), tagCtx);
}
}
@@ -244,8 +242,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
// Add tags for any stream numbers added to the window
for (long streamNumber : change.getAdded()) {
byte[] addTag = new byte[TAG_LENGTH];
crypto.encodeTag(addTag, inKeys.getTagKey(), PROTOCOL_VERSION,
streamNumber);
crypto.encodeTag(addTag, inKeys.getTagKey(), streamNumber);
inContexts.put(new Bytes(addTag), new TagContext(
tagCtx.contactId, inKeys, streamNumber));
}
@@ -253,8 +250,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (long streamNumber : change.getRemoved()) {
if (streamNumber == tagCtx.streamNumber) continue;
byte[] removeTag = new byte[TAG_LENGTH];
crypto.encodeTag(removeTag, inKeys.getTagKey(),
PROTOCOL_VERSION, streamNumber);
crypto.encodeTag(removeTag, inKeys.getTagKey(), streamNumber);
inContexts.remove(new Bytes(removeTag));
}
// Write the window back to the DB

View File

@@ -14,8 +14,7 @@ import static junit.framework.Assert.assertEquals;
import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.junit.Assert.assertArrayEquals;
@@ -23,8 +22,7 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
private final AuthenticatedCipher cipher;
private final SecretKey streamHeaderKey, frameKey;
private final byte[] streamHeaderNonce, protocolVersionBytes;
private final byte[] streamNumberBytes, payload;
private final byte[] streamHeaderIv, payload;
private final int payloadLength = 123, paddingLength = 234;
private final long streamNumber = 1234;
@@ -32,12 +30,7 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
cipher = new TestAuthenticatedCipher(); // Null cipher
streamHeaderKey = TestUtils.getSecretKey();
frameKey = TestUtils.getSecretKey();
streamHeaderNonce =
TestUtils.getRandomBytes(STREAM_HEADER_NONCE_LENGTH);
protocolVersionBytes = new byte[2];
ByteUtils.writeUint16(PROTOCOL_VERSION, protocolVersionBytes, 0);
streamNumberBytes = new byte[8];
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
streamHeaderIv = TestUtils.getRandomBytes(STREAM_HEADER_IV_LENGTH);
payload = TestUtils.getRandomBytes(payloadLength);
}
@@ -54,9 +47,7 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(streamHeaderIv);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
@@ -85,85 +76,6 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
assertEquals(-1, s.readFrame(buffer));
}
@Test(expected = IOException.class)
public void testWrongProtocolVersionThrowsException() throws Exception {
byte[] wrongProtocolVersionBytes = new byte[2];
ByteUtils.writeUint16(PROTOCOL_VERSION + 1, wrongProtocolVersionBytes,
0);
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
FrameEncoder.encodeHeader(frameHeader, false, payloadLength,
paddingLength);
byte[] frameHeader1 = new byte[FRAME_HEADER_LENGTH];
int payloadLength1 = 345, paddingLength1 = 456;
FrameEncoder.encodeHeader(frameHeader1, true, payloadLength1,
paddingLength1);
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(wrongProtocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
out.write(payload);
out.write(new byte[paddingLength]);
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader1);
out.write(payload1);
out.write(new byte[paddingLength1]);
out.write(new byte[MAC_LENGTH]);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
StreamDecrypterImpl s = new StreamDecrypterImpl(in, cipher,
streamNumber, streamHeaderKey);
// Try to read the first frame
byte[] buffer = new byte[MAX_PAYLOAD_LENGTH];
s.readFrame(buffer);
}
@Test(expected = IOException.class)
public void testWrongStreamNumberThrowsException() throws Exception {
byte[] wrongStreamNumberBytes = new byte[8];
ByteUtils.writeUint64(streamNumber + 1, wrongStreamNumberBytes, 0);
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
FrameEncoder.encodeHeader(frameHeader, false, payloadLength,
paddingLength);
byte[] frameHeader1 = new byte[FRAME_HEADER_LENGTH];
int payloadLength1 = 345, paddingLength1 = 456;
FrameEncoder.encodeHeader(frameHeader1, true, payloadLength1,
paddingLength1);
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(wrongStreamNumberBytes);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
out.write(payload);
out.write(new byte[paddingLength]);
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader1);
out.write(payload1);
out.write(new byte[paddingLength1]);
out.write(new byte[MAC_LENGTH]);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
StreamDecrypterImpl s = new StreamDecrypterImpl(in, cipher,
streamNumber, streamHeaderKey);
// Try to read the first frame
byte[] buffer = new byte[MAX_PAYLOAD_LENGTH];
s.readFrame(buffer);
}
@Test(expected = IOException.class)
public void testTruncatedFrameThrowsException() throws Exception {
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -171,9 +83,7 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
paddingLength);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(streamHeaderIv);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
@@ -201,9 +111,7 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
byte[] payload = TestUtils.getRandomBytes(payloadLength);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(streamHeaderIv);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
@@ -230,9 +138,7 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
padding[paddingLength - 1] = 1;
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(streamHeaderIv);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
@@ -256,9 +162,7 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
paddingLength);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(streamHeaderIv);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.ByteUtils;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
@@ -12,9 +11,8 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_HE
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -23,8 +21,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
private final AuthenticatedCipher cipher;
private final SecretKey streamHeaderKey, frameKey;
private final byte[] tag, streamHeaderNonce, protocolVersionBytes;
private final byte[] streamNumberBytes, payload;
private final byte[] tag, streamHeaderIv, payload;
private final long streamNumber = 1234;
private final int payloadLength = 123, paddingLength = 234;
@@ -33,12 +30,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
streamHeaderKey = TestUtils.getSecretKey();
frameKey = TestUtils.getSecretKey();
tag = TestUtils.getRandomBytes(TAG_LENGTH);
streamHeaderNonce =
TestUtils.getRandomBytes(STREAM_HEADER_NONCE_LENGTH);
protocolVersionBytes = new byte[2];
ByteUtils.writeUint16(PROTOCOL_VERSION, protocolVersionBytes, 0);
streamNumberBytes = new byte[8];
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
streamHeaderIv = TestUtils.getRandomBytes(STREAM_HEADER_IV_LENGTH);
payload = TestUtils.getRandomBytes(payloadLength);
}
@@ -46,8 +38,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testRejectsNegativePayloadLength() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, -1, 0, false);
}
@@ -56,8 +47,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testRejectsNegativePaddingLength() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, 0, -1, false);
}
@@ -66,8 +56,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testRejectsMaxPayloadPlusPadding() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH + 1];
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 1, false);
@@ -77,8 +66,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testAcceptsMaxPayloadIncludingPadding() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH - 1, 1, false);
@@ -90,8 +78,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testAcceptsMaxPayloadWithoutPadding() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 0, false);
@@ -103,17 +90,14 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedNonFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, 0, false);
// Expect the tag, stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -129,17 +113,14 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, 0, true);
// Expect the tag, stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -155,16 +136,13 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedNonFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, 0, false);
// Expect the stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -180,16 +158,13 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, 0, true);
// Expect the stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -205,17 +180,14 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedNonFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, paddingLength, false);
// Expect the tag, stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -233,17 +205,14 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, paddingLength, true);
// Expect the tag, stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -261,16 +230,13 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedNonFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, paddingLength, false);
// Expect the stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -288,16 +254,13 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
s.writeFrame(payload, payloadLength, paddingLength, true);
// Expect the stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -315,8 +278,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteTwoFramesWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
int payloadLength1 = 345, paddingLength1 = 456;
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
@@ -327,9 +289,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
// MAC, second frame header, payload, padding, MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -355,8 +315,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
// Flush the stream once
s.flush();
@@ -364,9 +323,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
// Expect the tag and stream header
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
@@ -378,8 +335,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
// Flush the stream twice
s.flush();
@@ -388,9 +344,7 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
// Expect the tag and stream header
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag);
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);
@@ -401,17 +355,14 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testFlushDoesNotWriteTagIfNull() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
// Flush the stream once
s.flush();
// Expect the stream header
ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(streamHeaderIv);
expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]);

View File

@@ -1,59 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
import static junit.framework.TestCase.assertTrue;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
public class TagEncodingTest extends BrambleTestCase {
private final CryptoComponent crypto;
private final SecretKey tagKey;
private final long streamNumber = 1234567890;
public TagEncodingTest() {
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
tagKey = TestUtils.getSecretKey();
}
@Test
public void testKeyAffectsTag() throws Exception {
Set<Bytes> set = new HashSet<Bytes>();
for (int i = 0; i < 100; i++) {
byte[] tag = new byte[TAG_LENGTH];
SecretKey tagKey = TestUtils.getSecretKey();
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber);
assertTrue(set.add(new Bytes(tag)));
}
}
@Test
public void testProtocolVersionAffectsTag() throws Exception {
Set<Bytes> set = new HashSet<Bytes>();
for (int i = 0; i < 100; i++) {
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i, streamNumber);
assertTrue(set.add(new Bytes(tag)));
}
}
@Test
public void testStreamNumberAffectsTag() throws Exception {
Set<Bytes> set = new HashSet<Bytes>();
for (int i = 0; i < 100; i++) {
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber + i);
assertTrue(set.add(new Bytes(tag)));
}
}
}

View File

@@ -34,7 +34,6 @@ import java.util.Collection;
import javax.inject.Inject;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -116,7 +115,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
private void read(byte[] connectionData) throws Exception {
// Calculate the expected tag
byte[] expectedTag = new byte[TAG_LENGTH];
crypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION, streamNumber);
crypto.encodeTag(expectedTag, tagKey, streamNumber);
// Read the tag
InputStream in = new ByteArrayInputStream(connectionData);

View File

@@ -33,7 +33,6 @@ import java.util.concurrent.ScheduledExecutorService;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
@@ -87,7 +86,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets per contact)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(6).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction());
}
// Save the keys that were rotated
@@ -134,7 +133,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction());
}
// Save the keys
@@ -200,7 +199,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction());
}
// Rotate the transport keys (the keys are unaffected)
@@ -248,7 +247,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction());
}
// Rotate the transport keys (the keys are unaffected)
@@ -307,7 +306,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction());
}
// Rotate the transport keys (the keys are unaffected)
@@ -356,7 +355,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction(tags));
}
// Rotate the transport keys (the keys are unaffected)
@@ -366,8 +365,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
// Encode a new tag after sliding the window
oneOf(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION),
with((long) REORDERING_WINDOW_SIZE));
with(tagKey), with((long) REORDERING_WINDOW_SIZE));
will(new EncodeTagAction(tags));
// Save the reordering window (previous rotation period, base 1)
oneOf(db).setReorderingWindow(txn, contactId, transportId, 999,
@@ -430,7 +428,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction());
}
// Schedule key rotation at the start of the next rotation period
@@ -452,7 +450,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(PROTOCOL_VERSION), with(i));
with(tagKey), with(i));
will(new EncodeTagAction());
}
// Save the keys that were rotated

View File

@@ -7,19 +7,10 @@ apply plugin: 'witness'
dependencies {
compile project(':bramble-core')
compile fileTree(dir: 'libs', include: '*.jar')
compile 'net.java.dev.jna:jna:4.4.0'
compile 'net.java.dev.jna:jna-platform:4.4.0'
testCompile project(path: ':bramble-core', configuration: 'testOutput')
}
dependencyVerification {
verify = [
'net.java.dev.jna:jna:c4dadeeecaa90c8847902082aee5eb107fcf59c5d0e63a17fcaf273c0e2d2bd1',
'net.java.dev.jna:jna-platform:e9dda9e884fc107eb6367710540789a12dfa8ad28be9326b22ca6e352e325499',
]
}
tasks.withType(Test) {
systemProperty 'java.library.path', 'libs'
}

Binary file not shown.

Binary file not shown.

View File

@@ -5,10 +5,13 @@ dependencies {
def supportVersion = '23.2.1'
compile project(':briar-core')
compile project(':bramble-android')
compile fileTree(dir: 'libs', include: '*.jar')
compile "com.android.support:support-v4:$supportVersion"
compile("com.android.support:appcompat-v7:$supportVersion") {
exclude module: 'support-v4'
}
compile("com.android.support:preference-v14:$supportVersion") {
exclude module: 'support-v4'
}
@@ -17,7 +20,7 @@ dependencies {
exclude module: 'recyclerview-v7'
}
compile "com.android.support:cardview-v7:$supportVersion"
compile "com.android.support:support-annotations:$supportVersion"
compile 'com.android.support:support-annotations:23.4.0'
compile('ch.acra:acra:4.8.5') {
exclude module: 'support-v4'
exclude module: 'support-annotations'
@@ -25,16 +28,15 @@ dependencies {
compile 'info.guardianproject.panic:panic:0.5'
compile 'info.guardianproject.trustedintents:trustedintents:0.2'
compile 'de.hdodenhof:circleimageview:2.1.0'
compile 'com.google.zxing:core:3.3.0'
compile 'com.jpardogo.materialtabstrip:library:1.1.0'
compile 'com.github.bumptech.glide:glide:3.8.0'
compile 'uk.co.samuelwall:material-tap-target-prompt:1.9.2'
compile 'com.google.zxing:core:3.2.1'
provided 'javax.annotation:jsr250-api:1.0'
compile 'com.jpardogo.materialtabstrip:library:1.1.0'
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'uk.co.samuelwall:material-tap-target-prompt:1.3.0'
testCompile project(path: ':bramble-core', configuration: 'testOutput')
testCompile 'org.robolectric:robolectric:3.0'
testCompile 'org.mockito:mockito-core:2.8.9'
testCompile 'org.mockito:mockito-core:1.10.19'
}
dependencyVerification {
@@ -43,18 +45,20 @@ dependencyVerification {
'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2',
'info.guardianproject.trustedintents:trustedintents:6221456d8821a8d974c2acf86306900237cf6afaaa94a4c9c44e161350f80f3e',
'de.hdodenhof:circleimageview:bcbc588e19e6dcf8c120b1957776bfe229efba5d2fbe5da7156372eeacf65503',
'com.google.zxing:core:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d',
'com.jpardogo.materialtabstrip:library:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
'com.github.bumptech.glide:glide:750d9e7b940dc0ee48f8680623b55d46e14e8727acc922d7b156e57e7c549655',
'uk.co.samuelwall:material-tap-target-prompt:5d4951124366bc5c52e57beaa294db7611f0aa2a8d80e0163e1383e1966ba5b2',
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259',
'com.android.support:support-v4:81ce890f26d35c75ad17d0f998a7e3230330c3b41e0b629566bc744bee89e448',
'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d',
'com.android.support:preference-v14:44881bb46094e86d0bc2426f205419674a5b4eb514b44b5a4659b5de29f71eb7',
'com.android.support:design:003e0c0bea0a6891f8b2bc43f20ae7af2a49a17363e5bb10df5ee0bae12fa686',
'com.android.support:cardview-v7:4595f1c4a28cfa083b6c0920ad4d49e1c2ca4b8302a955e548f68eb63b74931b',
'com.android.support:support-annotations:e91a88dd0c5e99069b7f09d4a46b5e06f1e9c4c72fc0a8e987e25d86af480f01',
'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1',
'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1',
'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe',
'com.android.support:preference-v7:775101bd07bd052e455761c5c5d9523d7ad59f2f320e3e8cbde241fd6b1d6025',
'com.android.support:cardview-v7:4595f1c4a28cfa083b6c0920ad4d49e1c2ca4b8302a955e548f68eb63b74931b',
'com.jpardogo.materialtabstrip:library:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
'com.github.bumptech.glide:glide:76ef123957b5fbaebb05fcbe6606dd58c3bc3fcdadb257f99811d0ac9ea9b88b',
'uk.co.samuelwall:material-tap-target-prompt:f67e1caead12a914525b32cbf6da52a96b93ff89573f93cb41102ef3130fb64a',
]
}
@@ -78,10 +82,9 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 22
versionCode 1608
versionName "0.16.8"
applicationId "org.briarproject.briar.beta"
resValue "string", "app_package", "org.briarproject.briar.beta"
versionCode 14
versionName "0.14"
resValue "string", "app_package", "org.briarproject.briar"
buildConfigField "String", "GitHash", "\"${getGitHash()}\""
}
@@ -106,6 +109,5 @@ android {
lintOptions {
warning 'MissingTranslation'
warning 'ImpliedQuantity'
warning 'ExtraTranslation'
}
}

View File

@@ -19,7 +19,7 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:name="org.briarproject.briar.android.BriarApplicationImpl"
android:name=".android.BriarApplicationImpl"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
@@ -27,7 +27,7 @@
android:theme="@style/BriarTheme">
<service
android:name="org.briarproject.briar.android.BriarService"
android:name=".android.BriarService"
android:exported="false">
<intent-filter>
<action android:name="org.briarproject.briar.android.BriarService"/>
@@ -35,7 +35,7 @@
</service>
<activity
android:name="org.briarproject.briar.android.reporting.DevReportActivity"
android:name=".android.reporting.DevReportActivity"
android:excludeFromRecents="true"
android:exported="false"
android:finishOnTaskLaunch="true"
@@ -47,24 +47,24 @@
</activity>
<activity
android:name="org.briarproject.briar.android.splash.ExpiredActivity"
android:name=".android.splash.ExpiredActivity"
android:label="@string/app_name">
</activity>
<activity
android:name="org.briarproject.briar.android.login.PasswordActivity"
android:name=".android.login.PasswordActivity"
android:label="@string/app_name"
android:windowSoftInputMode="stateVisible">
</activity>
<activity
android:name="org.briarproject.briar.android.login.SetupActivity"
android:name=".android.login.SetupActivity"
android:label="@string/setup_title"
android:windowSoftInputMode="adjustResize">
</activity>
<activity
android:name="org.briarproject.briar.android.splash.SplashScreenActivity"
android:name=".android.splash.SplashScreenActivity"
android:theme="@style/BriarTheme.NoActionBar"
android:label="@string/app_name">
<intent-filter>
@@ -74,268 +74,268 @@
</activity>
<activity
android:name="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:name=".android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar"
android:launchMode="singleTop">
</activity>
<activity
android:name="org.briarproject.briar.android.contact.ConversationActivity"
android:name=".android.contact.ConversationActivity"
android:label="@string/app_name"
android:theme="@style/BriarTheme.NoActionBar"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="stateHidden|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.privategroup.creation.CreateGroupActivity"
android:name=".android.privategroup.creation.CreateGroupActivity"
android:label="@string/groups_create_group_title"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:name=".android.privategroup.conversation.GroupActivity"
android:label="@string/app_name"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity"
android:name=".android.privategroup.invitation.GroupInvitationActivity"
android:label="@string/groups_invitations_title"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
android:value=".android.navdrawer.NavDrawerActivity"/>
</activity>
<activity
android:name="org.briarproject.briar.android.privategroup.memberlist.GroupMemberListActivity"
android:name=".android.privategroup.memberlist.GroupMemberListActivity"
android:label="@string/groups_member_list"
android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:parentActivityName=".android.privategroup.conversation.GroupActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:value=".android.privategroup.conversation.GroupActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity"
android:name=".android.privategroup.reveal.RevealContactsActivity"
android:label="@string/groups_reveal_contacts"
android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:parentActivityName=".android.privategroup.conversation.GroupActivity"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:value=".android.privategroup.conversation.GroupActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.privategroup.creation.GroupInviteActivity"
android:name=".android.privategroup.creation.GroupInviteActivity"
android:label="@string/groups_invite_members"
android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:parentActivityName=".android.privategroup.conversation.GroupActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"/>
android:value=".android.privategroup.conversation.GroupActivity"/>
</activity>
<activity
android:name="org.briarproject.briar.android.sharing.ForumInvitationActivity"
android:name=".android.sharing.ForumInvitationActivity"
android:label="@string/forum_invitations_title"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.sharing.BlogInvitationActivity"
android:name=".android.sharing.BlogInvitationActivity"
android:label="@string/blogs_sharing_invitations_title"
android:parentActivityName="org.briarproject.briar.android.contact.ConversationActivity">
android:parentActivityName=".android.contact.ConversationActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.contact.ConversationActivity"
android:value=".android.contact.ConversationActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.forum.CreateForumActivity"
android:name=".android.forum.CreateForumActivity"
android:label="@string/create_forum_title"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="adjustResize">
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="stateVisible">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.forum.ForumActivity"
android:name=".android.forum.ForumActivity"
android:label="@string/app_name"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.sharing.ShareForumActivity"
android:name=".android.sharing.ShareForumActivity"
android:label="@string/activity_share_toolbar_header"
android:parentActivityName="org.briarproject.briar.android.forum.ForumActivity"
android:parentActivityName=".android.forum.ForumActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.forum.ForumActivity"
android:value=".android.forum.ForumActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.sharing.ShareBlogActivity"
android:name=".android.sharing.ShareBlogActivity"
android:label="@string/activity_share_toolbar_header"
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
android:parentActivityName=".android.blog.BlogActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.blog.BlogActivity"
android:value=".android.blog.BlogActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.sharing.ForumSharingStatusActivity"
android:name=".android.sharing.ForumSharingStatusActivity"
android:label="@string/sharing_status"
android:parentActivityName="org.briarproject.briar.android.forum.ForumActivity">
android:parentActivityName=".android.forum.ForumActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.forum.ForumActivity"
android:value=".android.forum.ForumActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.sharing.BlogSharingStatusActivity"
android:name=".android.sharing.BlogSharingStatusActivity"
android:label="@string/sharing_status"
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity">
android:parentActivityName=".android.blog.BlogActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.blog.BlogActivity"
android:value=".android.blog.BlogActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.blog.BlogActivity"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:name=".android.blog.BlogActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
android:value=".android.navdrawer.NavDrawerActivity"/>
</activity>
<activity
android:name="org.briarproject.briar.android.blog.WriteBlogPostActivity"
android:name=".android.blog.WriteBlogPostActivity"
android:label="@string/blogs_write_blog_post"
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
android:parentActivityName=".android.blog.BlogActivity"
android:windowSoftInputMode="stateVisible|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.blog.BlogActivity"
android:value=".android.blog.BlogActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.blog.ReblogActivity"
android:name=".android.blog.ReblogActivity"
android:label="@string/blogs_reblog_button"
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
android:parentActivityName=".android.blog.BlogActivity"
android:windowSoftInputMode="stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.blog.BlogActivity"
android:value=".android.blog.BlogActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.blog.RssFeedImportActivity"
android:name=".android.blog.RssFeedImportActivity"
android:label="@string/blogs_rss_feeds_import"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="stateVisible|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.blog.RssFeedManageActivity"
android:name=".android.blog.RssFeedManageActivity"
android:label="@string/blogs_rss_feeds_manage"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.invitation.AddContactActivity"
android:name=".android.invitation.AddContactActivity"
android:label="@string/add_contact_title"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.keyagreement.KeyAgreementActivity"
android:name=".android.keyagreement.KeyAgreementActivity"
android:label="@string/add_contact_title"
android:theme="@style/BriarTheme.NoActionBar"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
android:value=".android.navdrawer.NavDrawerActivity"/>
</activity>
<activity
android:name="org.briarproject.briar.android.introduction.IntroductionActivity"
android:name=".android.introduction.IntroductionActivity"
android:label="@string/introduction_activity_title"
android:parentActivityName="org.briarproject.briar.android.contact.ConversationActivity"
android:parentActivityName=".android.contact.ConversationActivity"
android:windowSoftInputMode="stateHidden|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.contact.ConversationActivity"
android:value=".android.contact.ConversationActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.StartupFailureActivity"
android:name=".android.StartupFailureActivity"
android:label="@string/startup_failed_activity_title">
</activity>
<activity
android:name="org.briarproject.briar.android.settings.SettingsActivity"
android:name=".android.settings.SettingsActivity"
android:label="@string/settings_button"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
android:permission="android.permission.READ_NETWORK_USAGE_HISTORY">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:value=".android.navdrawer.NavDrawerActivity"
/>
<intent-filter>
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE"/>
@@ -344,27 +344,27 @@
</activity>
<activity
android:name="org.briarproject.briar.android.login.ChangePasswordActivity"
android:name=".android.login.ChangePasswordActivity"
android:label="@string/change_password"
android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
android:parentActivityName=".android.settings.SettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.settings.SettingsActivity"
android:value=".android.settings.SettingsActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.panic.PanicPreferencesActivity"
android:name=".android.panic.PanicPreferencesActivity"
android:label="@string/panic_setting"
android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
android:parentActivityName=".android.settings.SettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.settings.SettingsActivity"
android:value=".android.settings.SettingsActivity"
/>
</activity>
<activity
android:name="org.briarproject.briar.android.panic.PanicResponderActivity"
android:name=".android.panic.PanicResponderActivity"
android:noHistory="true"
android:theme="@android:style/Theme.NoDisplay">
<!-- this can never have launchMode singleTask or singleInstance! -->
@@ -375,7 +375,7 @@
</activity>
<activity
android:name="org.briarproject.briar.android.panic.ExitActivity"
android:name=".android.panic.ExitActivity"
android:theme="@android:style/Theme.NoDisplay">
</activity>

View File

@@ -61,7 +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;
@@ -328,12 +327,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
}
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);
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
@@ -353,6 +347,17 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
return defaults;
}
@Override
public void clearAllContactNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
clearContactNotification();
clearIntroductionSuccessNotification();
}
});
}
@UiThread
private void showGroupMessageNotification(final GroupId g) {
androidExecutor.runOnUiThread(new Runnable() {
@@ -427,12 +432,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
}
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);
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
@@ -440,6 +440,16 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
}
}
@Override
public void clearAllGroupMessageNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
clearGroupMessageNotification();
}
});
}
@UiThread
private void showForumPostNotification(final GroupId g) {
androidExecutor.runOnUiThread(new Runnable() {
@@ -514,12 +524,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
}
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);
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
@@ -527,6 +532,16 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
}
}
@Override
public void clearAllForumPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
clearForumPostNotification();
}
});
}
@UiThread
private void showBlogPostNotification(final GroupId g) {
androidExecutor.runOnUiThread(new Runnable() {
@@ -587,12 +602,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
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);
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
@@ -648,12 +658,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
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);
b.setVisibility(VISIBILITY_SECRET);
}
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
@@ -700,6 +705,68 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
});
}
@Override
public void blockAllContactNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockContacts = true;
blockIntroductions = true;
}
});
}
@Override
public void unblockAllContactNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockContacts = false;
blockIntroductions = false;
}
});
}
@Override
public void blockAllGroupMessageNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockGroups = true;
}
});
}
@Override
public void unblockAllGroupMessageNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockGroups = false;
}
});
}
@Override
public void blockAllForumPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockForums = true;
}
});
}
@Override
public void unblockAllForumPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockForums = false;
}
});
}
@Override
public void blockAllBlogPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@@ -719,4 +786,5 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
}
});
}
}

View File

@@ -38,6 +38,8 @@ public class AppModule {
static class EagerSingletons {
@Inject
AndroidNotificationManager androidNotificationManager;
@Inject
ScreenFilterMonitor screenFilterMonitor;
}
private final Application application;
@@ -169,8 +171,10 @@ public class AppModule {
}
@Provides
@Singleton
ScreenFilterMonitor provideScreenFilterMonitor(
ScreenFilterMonitorImpl screenFilterMonitor) {
return screenFilterMonitor;
LifecycleManager lifecycleManager, ScreenFilterMonitorImpl sfm) {
lifecycleManager.registerService(sfm);
return sfm;
}
}

View File

@@ -6,9 +6,5 @@ package org.briarproject.briar.android;
*/
public interface BriarApplication {
// This build expires on 21 October 2017
long EXPIRY_DATE = 1508544000 * 1000L;
AndroidComponent getApplicationComponent();
}

View File

@@ -2,9 +2,6 @@ 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;
@@ -36,8 +33,6 @@ 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,
@@ -77,9 +72,6 @@ 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()
@@ -93,17 +85,6 @@ 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

@@ -1,13 +1,23 @@
package org.briarproject.briar.android;
import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.support.annotation.UiThread;
import android.support.v7.preference.PreferenceManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.api.android.ScreenFilterMonitor;
@@ -16,26 +26,38 @@ import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.GET_SIGNATURES;
import static java.util.logging.Level.WARNING;
@NotNullByDefault
class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class ScreenFilterMonitorImpl extends BroadcastReceiver
implements Service, ScreenFilterMonitor {
private static final Logger LOG =
Logger.getLogger(ScreenFilterMonitorImpl.class.getName());
private static final String PREF_SCREEN_FILTER_APPS =
"shownScreenFilterApps";
/*
* Ignore Play Services if it uses this package name and public key - it's
@@ -56,17 +78,124 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
"82BA35E003C1B4B10DD244A8EE24FFFD333872AB5221985EDAB0FC0D" +
"0B145B6AA192858E79020103";
private final Context appContext;
private final AndroidExecutor androidExecutor;
private final PackageManager pm;
private final SharedPreferences prefs;
private final AtomicBoolean used = new AtomicBoolean(false);
// The following must only be accessed on the UI thread
private final Set<String> apps = new HashSet<>();
private final Set<String> shownApps;
private boolean serviceStarted = false;
@Inject
ScreenFilterMonitorImpl(Application app) {
pm = app.getPackageManager();
ScreenFilterMonitorImpl(AndroidExecutor executor, Application app) {
this.androidExecutor = executor;
this.appContext = app;
pm = appContext.getPackageManager();
prefs = PreferenceManager.getDefaultSharedPreferences(appContext);
shownApps = getShownScreenFilterApps();
}
@Override
public void startService() throws ServiceException {
if (used.getAndSet(true)) throw new IllegalStateException();
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
@Override
public Void call() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_PACKAGE_ADDED);
intentFilter.addDataScheme("package");
appContext.registerReceiver(ScreenFilterMonitorImpl.this,
intentFilter);
apps.addAll(getInstalledScreenFilterApps());
serviceStarted = true;
return null;
}
});
try {
f.get();
} catch (InterruptedException | ExecutionException e) {
throw new ServiceException(e);
}
}
@Override
public void stopService() throws ServiceException {
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
@Override
public Void call() {
serviceStarted = false;
appContext.unregisterReceiver(ScreenFilterMonitorImpl.this);
return null;
}
});
try {
f.get();
} catch (InterruptedException | ExecutionException e) {
throw new ServiceException(e);
}
}
private Set<String> getShownScreenFilterApps() {
// Result must not be modified
Set<String> s = prefs.getStringSet(PREF_SCREEN_FILTER_APPS, null);
HashSet<String> result = new HashSet<>();
if (s != null) {
result.addAll(s);
}
return result;
}
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
final String packageName =
intent.getData().getEncodedSchemeSpecificPart();
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
String pkg = isOverlayApp(packageName);
if (pkg == null) {
return;
}
apps.add(pkg);
}
});
}
}
@Override
@UiThread
public Set<String> getApps() {
Set<String> screenFilterApps = new TreeSet<>();
if (!serviceStarted) {
apps.addAll(getInstalledScreenFilterApps());
}
TreeSet<String> buf = new TreeSet<>();
if (apps.isEmpty()) {
return buf;
}
buf.addAll(apps);
buf.removeAll(shownApps);
return buf;
}
@Override
@UiThread
public void storeAppsAsShown(Collection<String> s, boolean persistent) {
HashSet<String> buf = new HashSet<>(s);
shownApps.addAll(buf);
if (persistent && !s.isEmpty()) {
buf.addAll(getShownScreenFilterApps());
prefs.edit()
.putStringSet(PREF_SCREEN_FILTER_APPS, buf)
.apply();
}
}
private Set<String> getInstalledScreenFilterApps() {
HashSet<String> screenFilterApps = new HashSet<>();
List<PackageInfo> packageInfos =
pm.getInstalledPackages(GET_PERMISSIONS);
for (PackageInfo packageInfo : packageInfos) {
@@ -80,6 +209,21 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
return screenFilterApps;
}
// Checks if a package uses the SYSTEM_ALERT_WINDOW permission and if so
// returns the app name.
@Nullable
private String isOverlayApp(String pkg) {
try {
PackageInfo pkgInfo = pm.getPackageInfo(pkg, GET_PERMISSIONS);
if (isOverlayApp(pkgInfo)) {
return pkgToString(pkgInfo);
}
} catch (NameNotFoundException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
return null;
}
// Fetches the application name for a given package.
@Nullable
private String pkgToString(PackageInfo pkgInfo) {

View File

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

View File

@@ -39,7 +39,7 @@ import org.briarproject.briar.android.privategroup.conversation.GroupConversatio
import org.briarproject.briar.android.privategroup.creation.CreateGroupActivity;
import org.briarproject.briar.android.privategroup.creation.CreateGroupFragment;
import org.briarproject.briar.android.privategroup.creation.CreateGroupMessageFragment;
import org.briarproject.briar.android.privategroup.creation.CreateGroupModule;
import org.briarproject.briar.android.privategroup.creation.GroupCreateModule;
import org.briarproject.briar.android.privategroup.creation.GroupInviteActivity;
import org.briarproject.briar.android.privategroup.creation.GroupInviteFragment;
import org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity;
@@ -71,7 +71,7 @@ import dagger.Component;
@Component(
modules = {ActivityModule.class, ForumModule.class, SharingModule.class,
BlogModule.class, ContactModule.class, GroupListModule.class,
CreateGroupModule.class, GroupInvitationModule.class,
GroupCreateModule.class, GroupInvitationModule.class,
GroupConversationModule.class, GroupMemberModule.class,
GroupRevealModule.class},
dependencies = AndroidComponent.class)

View File

@@ -2,13 +2,9 @@ package org.briarproject.briar.android.activity;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.LayoutRes;
import android.support.annotation.UiThread;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.inputmethod.InputMethodManager;
import org.briarproject.bramble.api.db.DbException;
@@ -17,9 +13,7 @@ import org.briarproject.briar.android.BriarApplication;
import org.briarproject.briar.android.DestroyableContext;
import org.briarproject.briar.android.controller.ActivityLifecycleController;
import org.briarproject.briar.android.forum.ForumModule;
import org.briarproject.briar.android.fragment.ScreenFilterDialogFragment;
import org.briarproject.briar.android.widget.TapSafeFrameLayout;
import org.briarproject.briar.android.widget.TapSafeFrameLayout.OnTapFilteredListener;
import org.briarproject.briar.android.fragment.SFDialogFragment;
import org.briarproject.briar.api.android.ScreenFilterMonitor;
import java.util.ArrayList;
@@ -29,23 +23,21 @@ import java.util.Set;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
public abstract class BaseActivity extends AppCompatActivity
implements DestroyableContext, OnTapFilteredListener {
@Inject
protected ScreenFilterMonitor screenFilterMonitor;
implements DestroyableContext {
protected ActivityComponent activityComponent;
private final List<ActivityLifecycleController> lifecycleControllers =
new ArrayList<>();
private boolean destroyed = false;
private ScreenFilterDialogFragment dialogFrag;
@Inject
protected ScreenFilterMonitor screenFilterMonitor;
private SFDialogFragment dialogFrag;
public abstract void injectActivity(ActivityComponent component);
@@ -73,6 +65,7 @@ public abstract class BaseActivity extends AppCompatActivity
for (ActivityLifecycleController alc : lifecycleControllers) {
alc.onActivityCreate(this);
}
}
public ActivityComponent getActivityComponent() {
@@ -104,6 +97,12 @@ public abstract class BaseActivity extends AppCompatActivity
}
}
@Override
protected void onPostResume() {
super.onPostResume();
showNewScreenFilterWarning();
}
@Override
protected void onPause() {
super.onPause();
@@ -113,14 +112,18 @@ public abstract class BaseActivity extends AppCompatActivity
}
}
private void showScreenFilterWarning() {
if (dialogFrag != null && dialogFrag.isVisible()) return;
Set<String> apps = screenFilterMonitor.getApps();
if (apps.isEmpty()) return;
dialogFrag =
ScreenFilterDialogFragment.newInstance(new ArrayList<>(apps));
protected void showNewScreenFilterWarning() {
final Set<String> apps = screenFilterMonitor.getApps();
if (apps.isEmpty()) {
return;
}
dialogFrag = SFDialogFragment.newInstance(new ArrayList<>(apps));
dialogFrag.setCancelable(false);
dialogFrag.show(getSupportFragmentManager(), dialogFrag.getTag());
dialogFrag.show(getSupportFragmentManager(), "SFDialog");
}
public void rememberShownApps(ArrayList<String> s, boolean permanent) {
screenFilterMonitor.storeAppsAsShown(s, permanent);
}
@Override
@@ -158,70 +161,4 @@ public abstract class BaseActivity extends AppCompatActivity
supportFinishAfterTransition();
}
/*
* Wraps the given view in a wrapper that notifies this activity when an
* obscured touch has been filtered, and returns the wrapper.
*/
private View makeTapSafeWrapper(View v) {
TapSafeFrameLayout wrapper = new TapSafeFrameLayout(this);
wrapper.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
wrapper.setOnTapFilteredListener(this);
wrapper.addView(v);
return wrapper;
}
/*
* Finds the AppCompat toolbar, if any, and configures it to filter
* obscured touches. If a custom toolbar is used, it will be part of the
* content view and thus protected by the wrapper. But the default toolbar
* is outside the wrapper.
*/
private void protectToolbar() {
View decorView = getWindow().getDecorView();
if (decorView instanceof ViewGroup) {
Toolbar toolbar = findToolbar((ViewGroup) decorView);
if (toolbar != null) toolbar.setFilterTouchesWhenObscured(true);
}
}
@Nullable
private Toolbar findToolbar(ViewGroup vg) {
for (int i = 0, len = vg.getChildCount(); i < len; i++) {
View child = vg.getChildAt(i);
if (child instanceof Toolbar) return (Toolbar) child;
if (child instanceof ViewGroup) {
Toolbar toolbar = findToolbar((ViewGroup) child);
if (toolbar != null) return toolbar;
}
}
return null;
}
@Override
public void setContentView(@LayoutRes int layoutRes) {
setContentView(getLayoutInflater().inflate(layoutRes, null));
}
@Override
public void setContentView(View v) {
super.setContentView(makeTapSafeWrapper(v));
protectToolbar();
}
@Override
public void setContentView(View v, LayoutParams layoutParams) {
super.setContentView(makeTapSafeWrapper(v), layoutParams);
protectToolbar();
}
@Override
public void addContentView(View v, LayoutParams layoutParams) {
super.addContentView(makeTapSafeWrapper(v), layoutParams);
protectToolbar();
}
@Override
public void onTapFiltered() {
showScreenFilterWarning();
}
}

View File

@@ -1,6 +1,5 @@
package org.briarproject.briar.android.blog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -21,10 +20,8 @@ import java.util.logging.Logger;
import javax.annotation.Nullable;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION;
@UiThread
@@ -60,20 +57,7 @@ abstract class BasePostFragment extends BaseFragment {
false);
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
progressBar.setVisibility(VISIBLE);
ui = new BlogPostViewHolder(view, true, new OnBlogPostClickListener() {
@Override
public void onBlogPostClick(BlogPostItem post) {
// We're already there
}
@Override
public void onAuthorClick(BlogPostItem post) {
Intent i = new Intent(getContext(), BlogActivity.class);
i.putExtra(GROUP_ID, post.getGroupId().getBytes());
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
getContext().startActivity(i);
}
});
ui = new BlogPostViewHolder(view);
return view;
}

View File

@@ -106,7 +106,7 @@ class BlogControllerImpl extends BaseControllerImpl
BlogInvitationResponseReceivedEvent b =
(BlogInvitationResponseReceivedEvent) e;
InvitationResponse r = b.getResponse();
if (r.getShareableId().equals(groupId) && r.wasAccepted()) {
if (r.getGroupId().equals(groupId) && r.wasAccepted()) {
LOG.info("Blog invitation accepted");
onBlogInvitationAccepted(b.getContactId());
}

View File

@@ -27,6 +27,7 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.blog.BlogController.BlogSharingListener;
import org.briarproject.briar.android.blog.BlogPostAdapter.OnBlogPostClickListener;
import org.briarproject.briar.android.controller.SharingController;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.android.fragment.BaseFragment;
@@ -215,19 +216,10 @@ public class BlogFragment extends BaseFragment
showNextFragment(f);
}
@Override
public void onAuthorClick(BlogPostItem post) {
if (post.getGroupId().equals(groupId)) return; // We're already there
Intent i = new Intent(getContext(), BlogActivity.class);
i.putExtra(GROUP_ID, post.getGroupId().getBytes());
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
getContext().startActivity(i);
}
private void loadBlogPosts(final boolean reload) {
blogController.loadBlogPosts(
new UiResultExceptionHandler<Collection<BlogPostItem>,
DbException>(this) {
new UiResultExceptionHandler<Collection<BlogPostItem>, DbException>(
this) {
@Override
public void onResultUi(Collection<BlogPostItem> posts) {
if (posts.isEmpty()) {
@@ -265,13 +257,13 @@ public class BlogFragment extends BaseFragment
}
private void setToolbarTitle(Author a) {
getActivity().setTitle(a.getName());
String title = getString(R.string.blogs_personal_blog, a.getName());
getActivity().setTitle(title);
}
private void loadSharedContacts() {
blogController.loadSharingContacts(
new UiResultExceptionHandler<Collection<ContactId>,
DbException>(this) {
new UiResultExceptionHandler<Collection<ContactId>, DbException>(this) {
@Override
public void onResultUi(Collection<ContactId> contacts) {
sharingController.addAll(contacts);

View File

@@ -23,7 +23,8 @@ class BlogPostAdapter
int viewType) {
View v = LayoutInflater.from(ctx).inflate(
R.layout.list_item_blog_post, parent, false);
BlogPostViewHolder ui = new BlogPostViewHolder(v, false, listener);
BlogPostViewHolder ui = new BlogPostViewHolder(v);
ui.setOnBlogPostClickListener(listener);
return ui;
}
@@ -47,4 +48,8 @@ class BlogPostAdapter
return a.getId().equals(b.getId());
}
interface OnBlogPostClickListener {
void onBlogPostClick(BlogPostItem post);
}
}

View File

@@ -8,16 +8,14 @@ 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 implements BlogListener {
public class BlogPostFragment extends BasePostFragment {
private static final String TAG = BlogPostFragment.class.getName();
@@ -42,7 +40,6 @@ public class BlogPostFragment extends BasePostFragment implements BlogListener {
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
blogController.setBlogListener(this);
}
@Override
@@ -62,15 +59,4 @@ public class BlogPostFragment extends BasePostFragment implements BlogListener {
}
});
}
@Override
public void onBlogPostAdded(BlogPostHeader header, boolean local) {
// doesn't matter here
}
@Override
public void onBlogRemoved() {
finish();
}
}

View File

@@ -4,7 +4,6 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat;
@@ -21,6 +20,7 @@ import android.widget.TextView;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R;
import org.briarproject.briar.android.blog.BlogPostAdapter.OnBlogPostClickListener;
import org.briarproject.briar.android.view.AuthorView;
import org.briarproject.briar.api.blog.BlogCommentHeader;
import org.briarproject.briar.api.blog.BlogPostHeader;
@@ -48,16 +48,11 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
private final ImageView reblogButton;
private final TextView body;
private final ViewGroup commentContainer;
private final boolean fullText;
@NonNull
private final OnBlogPostClickListener listener;
private OnBlogPostClickListener listener;
BlogPostViewHolder(View v, boolean fullText,
@NonNull OnBlogPostClickListener listener) {
BlogPostViewHolder(View v) {
super(v);
this.fullText = fullText;
this.listener = listener;
ctx = v.getContext();
layout = (ViewGroup) v.findViewById(R.id.postLayout);
@@ -69,6 +64,10 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
(ViewGroup) v.findViewById(R.id.commentContainer);
}
void setOnBlogPostClickListener(OnBlogPostClickListener listener) {
this.listener = listener;
}
void setVisibility(int visibility) {
layout.setVisibility(visibility);
}
@@ -93,7 +92,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
if (item == null) return;
setTransitionName(item.getId());
if (!fullText) {
if (listener != null) {
layout.setClickable(true);
layout.setOnClickListener(new OnClickListener() {
@Override
@@ -112,20 +111,15 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
author.setPersona(
item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL);
// TODO make author clickable more often #624
if (!fullText && item.getHeader().getType() == POST) {
author.setAuthorClickable(new OnClickListener() {
@Override
public void onClick(View v) {
listener.onAuthorClick(item);
}
});
if (item.getHeader().getType() == POST) {
author.setBlogLink(post.getGroupId());
} else {
author.setAuthorNotClickable();
author.unsetBlogLink();
}
// post body
Spanned bodyText = getSpanned(item.getBody());
if (fullText) {
if (listener == null) {
body.setText(bodyText);
body.setTextIsSelectable(true);
makeLinksClickable(body);
@@ -171,14 +165,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
reblogger.setAuthor(item.getAuthor());
reblogger.setAuthorStatus(item.getAuthorStatus());
reblogger.setDate(item.getTimestamp());
if (!fullText) {
reblogger.setAuthorClickable(new OnClickListener() {
@Override
public void onClick(View v) {
listener.onAuthorClick(item);
}
});
}
reblogger.setBlogLink(item.getGroupId());
reblogger.setVisibility(VISIBLE);
reblogger.setPersona(AuthorView.REBLOGGER);
@@ -201,7 +188,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
// TODO make author clickable #624
body.setText(c.getComment());
if (fullText) body.setTextIsSelectable(true);
if (listener == null) body.setTextIsSelectable(true);
commentContainer.addView(v);
}

View File

@@ -19,6 +19,7 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.blog.BlogPostAdapter.OnBlogPostClickListener;
import org.briarproject.briar.android.blog.FeedController.FeedListener;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.android.fragment.BaseFragment;
@@ -33,7 +34,6 @@ import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.app.Activity.RESULT_OK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.support.design.widget.Snackbar.LENGTH_LONG;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_WRITE_BLOG_POST;
@@ -223,14 +223,6 @@ public class FeedFragment extends BaseFragment implements
showNextFragment(f);
}
@Override
public void onAuthorClick(BlogPostItem post) {
Intent i = new Intent(getContext(), BlogActivity.class);
i.putExtra(GROUP_ID, post.getGroupId().getBytes());
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
getContext().startActivity(i);
}
@Override
public String getUniqueTag() {
return TAG;

View File

@@ -1,8 +0,0 @@
package org.briarproject.briar.android.blog;
interface OnBlogPostClickListener {
void onBlogPostClick(BlogPostItem post);
void onAuthorClick(BlogPostItem post);
}

View File

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

View File

@@ -161,18 +161,7 @@ public class ReblogFragment extends BaseFragment implements TextInputListener {
private ViewHolder(View v) {
scrollView = (ScrollView) v.findViewById(R.id.scrollView);
progressBar = (ProgressBar) v.findViewById(R.id.progressBar);
post = new BlogPostViewHolder(v.findViewById(R.id.postLayout),
true, new OnBlogPostClickListener() {
@Override
public void onBlogPostClick(BlogPostItem post) {
// do nothing
}
@Override
public void onAuthorClick(BlogPostItem post) {
// probably don't want to allow author clicks here
}
});
post = new BlogPostViewHolder(v.findViewById(R.id.postLayout));
input = (TextInputView) v.findViewById(R.id.inputText);
}
}

View File

@@ -5,7 +5,6 @@ import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Patterns;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -21,12 +20,9 @@ import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.api.feed.FeedManager;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.view.View.GONE;
@@ -102,17 +98,10 @@ public class RssFeedImportActivity extends BriarActivity {
private void enableOrDisableImportButton() {
String url = urlInput.getText().toString();
importButton.setEnabled(validateAndNormaliseUrl(url) != null);
}
@Nullable
private String validateAndNormaliseUrl(String url) {
if (!Patterns.WEB_URL.matcher(url).matches()) return null;
try {
return new URL(url).toString();
} catch (MalformedURLException e) {
return null;
}
if (url.startsWith("http://") || url.startsWith("https://"))
importButton.setEnabled(true);
else
importButton.setEnabled(false);
}
private void publish() {
@@ -120,9 +109,7 @@ public class RssFeedImportActivity extends BriarActivity {
importButton.setVisibility(GONE);
progressBar.setVisibility(VISIBLE);
String url = validateAndNormaliseUrl(urlInput.getText().toString());
if (url == null) throw new AssertionError();
importFeed(url);
importFeed(urlInput.getText().toString());
}
private void importFeed(final String url) {

View File

@@ -178,6 +178,8 @@ public class ContactListFragment extends BaseFragment implements EventListener {
@Override
public void onStart() {
super.onStart();
notificationManager.blockAllContactNotifications();
notificationManager.clearAllContactNotifications();
eventBus.addListener(this);
loadContacts();
list.startPeriodicUpdate();
@@ -187,6 +189,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
public void onStop() {
super.onStop();
eventBus.removeListener(this);
notificationManager.unblockAllContactNotifications();
adapter.clear();
list.showProgressBar();
list.stopPeriodicUpdate();

View File

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

View File

@@ -2,7 +2,6 @@ package org.briarproject.briar.android.forum;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.TextInputLayout;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
@@ -39,15 +38,16 @@ import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LEN
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class CreateForumActivity extends BriarActivity {
public class CreateForumActivity extends BriarActivity
implements OnEditorActionListener, OnClickListener {
private static final Logger LOG =
Logger.getLogger(CreateForumActivity.class.getName());
private TextInputLayout nameEntryLayout;
private EditText nameEntry;
private Button createForumButton;
private ProgressBar progress;
private TextView feedback;
// Fields that are accessed from background threads must be volatile
@Inject
@@ -59,10 +59,12 @@ public class CreateForumActivity extends BriarActivity {
setContentView(R.layout.activity_create_forum);
nameEntryLayout =
(TextInputLayout) findViewById(R.id.createForumNameLayout);
nameEntry = (EditText) findViewById(R.id.createForumNameEntry);
nameEntry.addTextChangedListener(new TextWatcher() {
TextWatcher nameEntryWatcher = new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
@@ -70,39 +72,21 @@ public class CreateForumActivity extends BriarActivity {
}
@Override
public void onTextChanged(CharSequence s, int start,
public void onTextChanged(CharSequence text, int start,
int lengthBefore, int lengthAfter) {
enableOrDisableCreateButton();
}
};
nameEntry.setOnEditorActionListener(this);
nameEntry.addTextChangedListener(nameEntryWatcher);
@Override
public void afterTextChanged(Editable s) {
}
});
nameEntry.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent e) {
createForum();
return true;
}
});
feedback = (TextView) findViewById(R.id.createForumFeedback);
createForumButton = (Button) findViewById(R.id.createForumButton);
createForumButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
createForum();
}
});
createForumButton.setOnClickListener(this);
progress = (ProgressBar) findViewById(R.id.createForumProgressBar);
}
@Override
public void onStart() {
super.onStart();
showSoftKeyboard(nameEntry);
}
@Override
@@ -111,27 +95,36 @@ public class CreateForumActivity extends BriarActivity {
}
private void enableOrDisableCreateButton() {
if (createForumButton == null) return; // Not created yet
if (progress == null) return; // Not created yet
createForumButton.setEnabled(validateName());
}
@Override
public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) {
hideSoftKeyboard(textView);
return true;
}
private boolean validateName() {
String name = nameEntry.getText().toString();
int length = StringUtils.toUtf8(name).length;
if (length > MAX_FORUM_NAME_LENGTH) {
nameEntryLayout.setError(getString(R.string.name_too_long));
feedback.setText(R.string.name_too_long);
return false;
}
nameEntryLayout.setError(null);
feedback.setText("");
return length > 0;
}
private void createForum() {
if (!validateName()) return;
hideSoftKeyboard(nameEntry);
createForumButton.setVisibility(GONE);
progress.setVisibility(VISIBLE);
storeForum(nameEntry.getText().toString());
@Override
public void onClick(View view) {
if (view == createForumButton) {
hideSoftKeyboard(view);
if (!validateName()) return;
createForumButton.setVisibility(GONE);
progress.setVisibility(VISIBLE);
storeForum(nameEntry.getText().toString());
}
}
private void storeForum(final String name) {

View File

@@ -85,7 +85,7 @@ class ForumControllerImpl extends
(ForumInvitationResponseReceivedEvent) e;
ForumInvitationResponse r =
(ForumInvitationResponse) f.getResponse();
if (r.getShareableId().equals(getGroupId()) && r.wasAccepted()) {
if (r.getGroupId().equals(getGroupId()) && r.wasAccepted()) {
LOG.info("Forum invitation was accepted");
onForumInvitationAccepted(r.getContactId());
}

View File

@@ -119,6 +119,8 @@ public class ForumListFragment extends BaseEventFragment implements
@Override
public void onStart() {
super.onStart();
notificationManager.blockAllForumPostNotifications();
notificationManager.clearAllForumPostNotifications();
loadForums();
loadAvailableForums();
list.startPeriodicUpdate();
@@ -127,6 +129,7 @@ public class ForumListFragment extends BaseEventFragment implements
@Override
public void onStop() {
super.onStop();
notificationManager.unblockAllForumPostNotifications();
adapter.clear();
list.showProgressBar();
list.stopPeriodicUpdate();

View File

@@ -0,0 +1,67 @@
package org.briarproject.briar.android.fragment;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.BaseActivity;
import java.util.ArrayList;
import javax.annotation.Nullable;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class SFDialogFragment extends DialogFragment {
public static SFDialogFragment newInstance(ArrayList<String> apps) {
SFDialogFragment frag = new SFDialogFragment();
Bundle args = new Bundle();
args.putStringArrayList("apps", apps);
frag.setArguments(args);
return frag;
}
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
AlertDialog.Builder builder =
new AlertDialog.Builder(
getActivity(),
R.style.BriarDialogThemeNoFilter);
builder.setTitle(R.string.screen_filter_title);
LayoutInflater li = getActivity().getLayoutInflater();
//Pass null here because it's an AlertDialog
View v =
li.inflate(R.layout.alert_dialog_checkbox, null,
false);
TextView t = (TextView) v.findViewById(R.id.alert_dialog_text);
final ArrayList<String> apps =
getArguments().getStringArrayList("apps");
t.setText(getString(R.string.screen_filter_body, TextUtils
.join("\n", apps)));
final CheckBox cb =
(CheckBox) v.findViewById(
R.id.checkBox_screen_filter_reminder);
builder.setNeutralButton(R.string.continue_button,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
((BaseActivity) getActivity())
.rememberShownApps(apps, cb.isChecked());
}
});
builder.setView(v);
return builder.create();
}
}

View File

@@ -1,46 +0,0 @@
package org.briarproject.briar.android.fragment;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import java.util.ArrayList;
import javax.annotation.Nullable;
@NotNullByDefault
public class ScreenFilterDialogFragment extends DialogFragment {
public static ScreenFilterDialogFragment newInstance(
ArrayList<String> apps) {
ScreenFilterDialogFragment frag = new ScreenFilterDialogFragment();
Bundle args = new Bundle();
args.putStringArrayList("apps", apps);
frag.setArguments(args);
return frag;
}
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(),
R.style.BriarDialogThemeNoFilter);
builder.setTitle(R.string.screen_filter_title);
ArrayList<String> apps = getArguments().getStringArrayList("apps");
builder.setMessage(getString(R.string.screen_filter_body,
TextUtils.join("\n", apps)));
builder.setNeutralButton(R.string.continue_button,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
return builder.create();
}
}

View File

@@ -33,6 +33,7 @@ import im.delight.android.identicons.IdenticonDrawable;
import static android.app.Activity.RESULT_OK;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static android.widget.Toast.LENGTH_SHORT;
import static java.util.logging.Level.WARNING;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_MESSAGE_LENGTH;
@@ -93,6 +94,7 @@ public class IntroductionMessageFragment extends BaseFragment
View v = inflater.inflate(R.layout.introduction_message, container,
false);
ui = new ViewHolder(v);
ui.text.setVisibility(GONE);
ui.message.setSendButtonEnabled(false);
return v;
@@ -154,11 +156,17 @@ public class IntroductionMessageFragment extends BaseFragment
ui.contactName1.setText(c1.getAuthor().getName());
ui.contactName2.setText(c2.getAuthor().getName());
// set introduction text
ui.text.setText(String.format(
getString(R.string.introduction_message_text),
c1.getAuthor().getName(), c2.getAuthor().getName()));
// set button action
ui.message.setListener(IntroductionMessageFragment.this);
// hide progress bar and show views
ui.progressBar.setVisibility(GONE);
ui.text.setVisibility(VISIBLE);
ui.message.setSendButtonEnabled(true);
ui.message.showSoftKeyboard();
}
@@ -226,6 +234,7 @@ public class IntroductionMessageFragment extends BaseFragment
private final ProgressBar progressBar;
private final CircleImageView avatar1, avatar2;
private final TextView contactName1, contactName2;
private final TextView text;
private final TextInputView message;
private ViewHolder(View v) {
@@ -234,6 +243,7 @@ public class IntroductionMessageFragment extends BaseFragment
avatar2 = (CircleImageView) v.findViewById(R.id.avatarContact2);
contactName1 = (TextView) v.findViewById(R.id.nameContact1);
contactName2 = (TextView) v.findViewById(R.id.nameContact2);
text = (TextView) v.findViewById(R.id.introductionText);
message = (TextInputView) v
.findViewById(R.id.introductionMessageView);
}

View File

@@ -7,7 +7,6 @@ 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;
@@ -44,7 +43,6 @@ 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;
@@ -88,8 +86,6 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
try {
LOG.info("Opening camera");
camera = Camera.open();
if (camera == null)
throw new RuntimeException("No back-facing camera.");
} catch (RuntimeException e) {
LOG.log(WARNING, "Error opening camera", e);
return;
@@ -133,7 +129,6 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
private void startPreview(SurfaceHolder holder) {
LOG.info("Starting preview");
try {
if (camera == null) throw new IOException("Camera is null.");
camera.setPreviewDisplay(holder);
camera.startPreview();
previewStarted = true;
@@ -147,7 +142,6 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
private void stopPreview() {
LOG.info("Stopping preview");
try {
if (camera == null) throw new RuntimeException("Camera is null.");
stopConsumer();
camera.stopPreview();
} catch (RuntimeException e) {
@@ -158,14 +152,12 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
@UiThread
private void startConsumer() {
if (camera == null) throw new RuntimeException("Camera is null");
if (autoFocus) camera.autoFocus(this);
previewConsumer.start(camera);
}
@UiThread
private void stopConsumer() {
if (camera == null) throw new RuntimeException("Camera is null");
if (autoFocus) camera.cancelAutoFocus();
previewConsumer.stop();
}
@@ -184,7 +176,6 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
if (LOG.isLoggable(INFO))
LOG.info("Display orientation " + orientation + " degrees");
try {
if (camera == null) throw new RuntimeException("Camera is null");
camera.setDisplayOrientation(orientation);
} catch (RuntimeException e) {
LOG.log(WARNING, "Error setting display orientation", e);
@@ -225,11 +216,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
setFocusMode(params);
params.setFlashMode(FLASH_MODE_OFF);
setPreviewSize(params);
try {
camera.setParameters(params);
} catch (RuntimeException e) {
LOG.log(WARNING, "Error setting best camera parameters", e);
}
camera.setParameters(params);
return camera.getParameters();
}
@@ -300,7 +287,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
@UiThread
private void logCameraParameters() {
if (camera != null && LOG.isLoggable(INFO)) {
if (LOG.isLoggable(INFO)) {
Parameters params = camera.getParameters();
if (Build.VERSION.SDK_INT >= 15) {
LOG.info("Video stabilisation enabled: "

View File

@@ -22,7 +22,6 @@ 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
@@ -61,12 +60,8 @@ class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (camera == this.camera) {
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);
}
Size size = camera.getParameters().getPreviewSize();
new DecoderTask(data, size.width, size.height).execute();
}
}
@@ -75,7 +70,7 @@ class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
private final byte[] data;
private final int width, height;
private DecoderTask(byte[] data, int width, int height) {
DecoderTask(byte[] data, int width, int height) {
this.data = data;
this.width = width;
this.height = height;

View File

@@ -218,7 +218,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
statusView.setVisibility(VISIBLE);
status.setText(R.string.connecting_to_device);
task.connectAndRunProtocol(remotePayload);
} catch (IOException | IllegalArgumentException e) {
} catch (IOException 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.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.blog.FeedFragment;
import org.briarproject.briar.android.contact.ContactListFragment;
import org.briarproject.briar.android.controller.handler.UiResultHandler;
import org.briarproject.briar.android.forum.ForumListFragment;
import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
@@ -46,9 +45,6 @@ import javax.inject.Inject;
import static android.support.v4.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
import static android.support.v4.view.GravityCompat.START;
import static android.support.v4.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static org.briarproject.briar.android.util.UiUtils.getDaysUntilExpiry;
public class NavDrawerActivity extends BriarActivity implements
BaseFragmentListener, TransportStateListener,
@@ -132,12 +128,6 @@ public class NavDrawerActivity extends BriarActivity implements
public void onStart() {
super.onStart();
updateTransports();
controller.showExpiryWarning(new UiResultHandler<Boolean>(this) {
@Override
public void onResultUi(Boolean showWarning) {
if (showWarning) showExpiryWarning();
}
});
}
private void exitIfStartupFailed(Intent intent) {
@@ -264,34 +254,6 @@ public class NavDrawerActivity extends BriarActivity implements
// Do nothing for now
}
@SuppressWarnings("ConstantConditions")
private void showExpiryWarning() {
int daysUntilExpiry = getDaysUntilExpiry();
if (daysUntilExpiry < 0) signOut();
// show expiry warning text
final ViewGroup
expiryWarning = (ViewGroup) findViewById(R.id.expiryWarning);
TextView expiryWarningText =
(TextView) expiryWarning.findViewById(R.id.expiryWarningText);
expiryWarningText.setText(getResources()
.getQuantityString(R.plurals.expiry_warning, daysUntilExpiry,
daysUntilExpiry));
// make close button functional
ImageView expiryWarningClose =
(ImageView) expiryWarning.findViewById(R.id.expiryWarningClose);
expiryWarningClose.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
controller.expiryWarningDismissed();
expiryWarning.setVisibility(GONE);
}
});
expiryWarning.setVisibility(VISIBLE);
}
private void initializeTransports(final LayoutInflater inflater) {
transports = new ArrayList<>(3);

View File

@@ -3,15 +3,10 @@ package org.briarproject.briar.android.navdrawer;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.briar.android.controller.ActivityLifecycleController;
import org.briarproject.briar.android.controller.handler.ResultHandler;
@NotNullByDefault
public interface NavDrawerController extends ActivityLifecycleController {
boolean isTransportRunning(TransportId transportId);
void showExpiryWarning(final ResultHandler<Boolean> handler);
void expiryWarningDismissed();
}

View File

@@ -3,7 +3,6 @@ package org.briarproject.briar.android.navdrawer;
import android.app.Activity;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
@@ -15,10 +14,7 @@ import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.briar.android.controller.DbControllerImpl;
import org.briarproject.briar.android.controller.handler.ResultHandler;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -26,9 +22,6 @@ import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -37,21 +30,18 @@ public class NavDrawerControllerImpl extends DbControllerImpl
private static final Logger LOG =
Logger.getLogger(NavDrawerControllerImpl.class.getName());
private static final String EXPIRY_DATE_WARNING = "expiryDateWarning";
private final PluginManager pluginManager;
private final SettingsManager settingsManager;
private final EventBus eventBus;
private volatile TransportStateListener listener;
@Inject
NavDrawerControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, PluginManager pluginManager,
SettingsManager settingsManager, EventBus eventBus) {
LifecycleManager lifecycleManager,
PluginManager pluginManager, EventBus eventBus) {
super(dbExecutor, lifecycleManager);
this.pluginManager = pluginManager;
this.settingsManager = settingsManager;
this.eventBus = eventBus;
}
@@ -102,63 +92,6 @@ public class NavDrawerControllerImpl extends DbControllerImpl
});
}
@Override
public void showExpiryWarning(final ResultHandler<Boolean> handler) {
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
Settings settings =
settingsManager.getSettings(SETTINGS_NAMESPACE);
int warningInt = settings.getInt(EXPIRY_DATE_WARNING, 0);
if (warningInt == 0) {
// we have not warned before
handler.onResult(true);
} else {
long warningLong = warningInt * 1000L;
long now = System.currentTimeMillis();
long daysSinceLastWarning =
(now - warningLong) / 1000 / 60 / 60 / 24;
long daysBeforeExpiry =
(EXPIRY_DATE - now) / 1000 / 60 / 60 / 24;
if (daysSinceLastWarning >= 30) {
handler.onResult(true);
return;
}
if (daysBeforeExpiry <= 3 && daysSinceLastWarning > 0) {
handler.onResult(true);
return;
}
handler.onResult(false);
}
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
@Override
public void expiryWarningDismissed() {
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
Settings settings = new Settings();
int date = (int) (System.currentTimeMillis() / 1000L);
settings.putInt(EXPIRY_DATE_WARNING, date);
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
@Override
public boolean isTransportRunning(TransportId transportId) {
Plugin plugin = pluginManager.getPlugin(transportId);

View File

@@ -108,7 +108,7 @@ class GroupControllerImpl extends
(GroupInvitationResponseReceivedEvent) e;
final GroupInvitationResponse r =
(GroupInvitationResponse) g.getResponse();
if (getGroupId().equals(r.getShareableId()) && r.wasAccepted()) {
if (getGroupId().equals(r.getGroupId()) && r.wasAccepted()) {
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {

View File

@@ -1,15 +1,25 @@
package org.briarproject.briar.android.privategroup.conversation;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.UiThread;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
import org.briarproject.briar.android.threaded.BaseThreadItemViewHolder;
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.Author.Status.UNKNOWN;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityIcon;
import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityString;
import static org.briarproject.briar.api.privategroup.Visibility.INVISIBLE;
@UiThread
@NotNullByDefault
@@ -17,10 +27,16 @@ class JoinMessageItemViewHolder
extends BaseThreadItemViewHolder<GroupMessageItem> {
private final boolean isCreator;
private final ImageView icon;
private final TextView info;
private final Button options;
JoinMessageItemViewHolder(View v, boolean isCreator) {
super(v);
this.isCreator = isCreator;
icon = (ImageView) v.findViewById(R.id.icon);
info = (TextView) v.findViewById(R.id.info);
options = (Button) v.findViewById(R.id.optionsButton);
}
@Override
@@ -40,6 +56,9 @@ class JoinMessageItemViewHolder
getContext().getString(R.string.groups_member_joined,
item.getAuthor().getName()));
}
icon.setVisibility(View.GONE);
info.setVisibility(View.GONE);
options.setVisibility(View.GONE);
}
private void bind(final JoinMessageItem item) {
@@ -56,6 +75,32 @@ class JoinMessageItemViewHolder
item.getAuthor().getName()));
}
}
if (item.getStatus() == OURSELVES || item.getStatus() == UNKNOWN) {
icon.setVisibility(View.GONE);
info.setVisibility(View.GONE);
options.setVisibility(View.GONE);
} else {
icon.setVisibility(View.VISIBLE);
icon.setImageResource(getVisibilityIcon(item.getVisibility()));
info.setVisibility(View.VISIBLE);
info.setText(getVisibilityString(getContext(), item.getVisibility(),
item.getAuthor().getName()));
if (item.getVisibility() == INVISIBLE) {
options.setVisibility(View.VISIBLE);
options.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i =
new Intent(ctx, RevealContactsActivity.class);
i.putExtra(GROUP_ID, item.getGroupId().getBytes());
ctx.startActivity(i);
}
});
} else {
options.setVisibility(View.GONE);
}
}
}
}

View File

@@ -0,0 +1,58 @@
package org.briarproject.briar.android.privategroup.creation;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.android.contactselection.ContactSelectorActivity;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.android.sharing.BaseMessageFragment.MessageFragmentListener;
import java.util.Collection;
import javax.inject.Inject;
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_MSG_LENGTH;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public abstract class BaseGroupInviteActivity
extends ContactSelectorActivity implements MessageFragmentListener {
@Inject
CreateGroupController controller;
@Override
public void contactsSelected(Collection<ContactId> contacts) {
super.contactsSelected(contacts);
showNextFragment(new CreateGroupMessageFragment());
}
@Override
public boolean onButtonClick(String message) {
if (groupId == null)
throw new IllegalStateException("GroupId was not initialized");
controller.sendInvitation(groupId, contacts, message,
new UiResultExceptionHandler<Void, DbException>(this) {
@Override
public void onResultUi(Void result) {
setResult(RESULT_OK);
supportFinishAfterTransition();
}
@Override
public void onExceptionUi(DbException exception) {
setResult(RESULT_CANCELED);
handleDbException(exception);
}
});
return true;
}
@Override
public int getMaximumMessageLength() {
return MAX_GROUP_INVITATION_MSG_LENGTH;
}
}

View File

@@ -9,20 +9,16 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
import org.briarproject.briar.android.sharing.BaseMessageFragment.MessageFragmentListener;
import javax.annotation.Nullable;
import javax.inject.Inject;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class CreateGroupActivity extends BriarActivity
implements CreateGroupListener {
@Inject
CreateGroupController controller;
public class CreateGroupActivity extends BaseGroupInviteActivity implements
CreateGroupListener, MessageFragmentListener {
@Override
public void injectActivity(ActivityComponent component) {
@@ -33,20 +29,32 @@ public class CreateGroupActivity extends BriarActivity
public void onCreate(@Nullable Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_fragment_container);
if (bundle == null) {
showInitialFragment(new CreateGroupFragment());
}
}
@Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() == 1) {
// At this point, the group had been created already,
// so don't allow to create it again.
openNewGroup();
overridePendingTransition(R.anim.screen_old_in,
R.anim.screen_new_out);
} else {
super.onBackPressed();
}
}
@Override
public void onGroupNameChosen(String name) {
controller.createGroup(name,
new UiResultExceptionHandler<GroupId, DbException>(this) {
@Override
public void onResultUi(GroupId g) {
openNewGroup(g);
groupId = g;
switchToContactSelectorFragment(g);
}
@Override
@@ -56,10 +64,16 @@ public class CreateGroupActivity extends BriarActivity
});
}
private void openNewGroup(GroupId g) {
private void switchToContactSelectorFragment(GroupId g) {
showNextFragment(GroupInviteFragment.newInstance(g));
}
private void openNewGroup() {
Intent i = new Intent(this, GroupActivity.class);
i.putExtra(GROUP_ID, g.getBytes());
i.putExtra(GROUP_ID, groupId.getBytes());
startActivity(i);
// finish this activity, so we can't come back to it
finish();
}
}

View File

@@ -2,27 +2,19 @@ package org.briarproject.briar.android.privategroup.creation;
import android.content.Context;
import android.os.Bundle;
import android.support.design.widget.TextInputLayout;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.fragment.BaseFragment;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH;
public class CreateGroupFragment extends BaseFragment {
@@ -30,10 +22,8 @@ public class CreateGroupFragment extends BaseFragment {
public final static String TAG = CreateGroupFragment.class.getName();
private CreateGroupListener listener;
private EditText nameEntry;
private Button createGroupButton;
private TextInputLayout nameLayout;
private ProgressBar progress;
private EditText name;
private Button button;
@Override
public void onAttach(Context context) {
@@ -45,54 +35,42 @@ public class CreateGroupFragment extends BaseFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// inflate view
View v = inflater.inflate(R.layout.fragment_create_group, container,
false);
nameEntry = (EditText) v.findViewById(R.id.name);
nameEntry.addTextChangedListener(new TextWatcher() {
name = (EditText) v.findViewById(R.id.name);
name.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void onTextChanged(CharSequence s, int start,
int lengthBefore, int lengthAfter) {
enableOrDisableCreateButton();
public void onTextChanged(CharSequence s, int start, int before,
int count) {
validateName();
}
@Override
public void afterTextChanged(Editable s) {
}
});
nameEntry.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent e) {
createGroup();
return true;
}
});
nameLayout = (TextInputLayout) v.findViewById(R.id.nameLayout);
createGroupButton = (Button) v.findViewById(R.id.button);
createGroupButton.setOnClickListener(new OnClickListener() {
button = (Button) v.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
createGroup();
listener.hideSoftKeyboard(name);
listener.onGroupNameChosen(name.getText().toString());
}
});
progress = (ProgressBar) v.findViewById(R.id.progressBar);
return v;
}
@Override
public void onStart() {
super.onStart();
listener.showSoftKeyboard(nameEntry);
listener.showSoftKeyboard(name);
}
@Override
@@ -105,27 +83,12 @@ public class CreateGroupFragment extends BaseFragment {
return TAG;
}
private void enableOrDisableCreateButton() {
if (createGroupButton == null) return; // Not created yet
createGroupButton.setEnabled(validateName());
private void validateName() {
String name = this.name.getText().toString();
if (name.length() < 1 || StringUtils.utf8IsTooLong(name, MAX_GROUP_NAME_LENGTH))
button.setEnabled(false);
else if (!button.isEnabled())
button.setEnabled(true);
}
private boolean validateName() {
String name = nameEntry.getText().toString();
int length = StringUtils.toUtf8(name).length;
if (length > MAX_GROUP_NAME_LENGTH) {
nameLayout.setError(getString(R.string.name_too_long));
return false;
}
nameLayout.setError(null);
return length > 0;
}
private void createGroup() {
if (!validateName()) return;
listener.hideSoftKeyboard(nameEntry);
createGroupButton.setVisibility(GONE);
progress.setVisibility(VISIBLE);
listener.onGroupNameChosen(nameEntry.getText().toString());
}
}

View File

@@ -6,7 +6,7 @@ import dagger.Module;
import dagger.Provides;
@Module
public class CreateGroupModule {
public class GroupCreateModule {
@ActivityScope
@Provides

View File

@@ -3,43 +3,25 @@ package org.briarproject.briar.android.privategroup.creation;
import android.content.Intent;
import android.os.Bundle;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.contactselection.ContactSelectorActivity;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.android.sharing.BaseMessageFragment.MessageFragmentListener;
import java.util.Collection;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_MSG_LENGTH;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class GroupInviteActivity extends ContactSelectorActivity
public class GroupInviteActivity extends BaseGroupInviteActivity
implements MessageFragmentListener {
@Inject
CreateGroupController controller;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public void onCreate(@Nullable Bundle bundle) {
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
Intent i = getIntent();
byte[] g = i.getByteArrayExtra(GROUP_ID);
if (g == null) throw new IllegalStateException("No GroupId in intent");
if (g == null) throw new IllegalStateException("No GroupId in intent.");
groupId = new GroupId(g);
if (bundle == null) {
@@ -47,36 +29,4 @@ public class GroupInviteActivity extends ContactSelectorActivity
}
}
@Override
public void contactsSelected(Collection<ContactId> contacts) {
super.contactsSelected(contacts);
showNextFragment(new CreateGroupMessageFragment());
}
@Override
public boolean onButtonClick(String message) {
if (groupId == null)
throw new IllegalStateException("GroupId was not initialized");
controller.sendInvitation(groupId, contacts, message,
new UiResultExceptionHandler<Void, DbException>(this) {
@Override
public void onResultUi(Void result) {
setResult(RESULT_OK);
supportFinishAfterTransition();
}
@Override
public void onExceptionUi(DbException exception) {
setResult(RESULT_CANCELED);
handleDbException(exception);
}
});
return true;
}
@Override
public int getMaximumMessageLength() {
return MAX_GROUP_INVITATION_MSG_LENGTH;
}
}

View File

@@ -79,12 +79,15 @@ class GroupListControllerImpl extends DbControllerImpl
throw new IllegalStateException(
"GroupListListener needs to be attached");
eventBus.addListener(this);
notificationManager.blockAllGroupMessageNotifications();
notificationManager.clearAllGroupMessageNotifications();
}
@Override
@CallSuper
public void onStop() {
eventBus.removeListener(this);
notificationManager.unblockAllGroupMessageNotifications();
}
@Override

View File

@@ -36,6 +36,7 @@ class MemberListAdapter extends
@Override
public boolean areContentsTheSame(MemberListItem m1, MemberListItem m2) {
if (m1.isOnline() != m2.isOnline()) return false;
if (m1.getVisibility() != m2.getVisibility()) return false;
if (m1.getContactId() != m2.getContactId()) return false;
if (m1.getStatus() != m2.getStatus()) return false;
return true;

View File

@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.privategroup.GroupMember;
import org.briarproject.briar.api.privategroup.Visibility;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
@@ -38,6 +39,10 @@ class MemberListItem {
return groupMember.getContactId();
}
Visibility getVisibility() {
return groupMember.getVisibility();
}
boolean isOnline() {
return online;
}

View File

@@ -11,6 +11,9 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.view.AuthorView;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.Author.Status.UNKNOWN;
import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityIcon;
import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityString;
@UiThread
@NotNullByDefault
@@ -19,12 +22,16 @@ class MemberListItemHolder extends RecyclerView.ViewHolder {
private final AuthorView author;
private final ImageView bulb;
private final TextView creator;
private final ImageView icon;
private final TextView info;
MemberListItemHolder(View v) {
super(v);
author = (AuthorView) v.findViewById(R.id.authorView);
bulb = (ImageView) v.findViewById(R.id.bulbView);
creator = (TextView) v.findViewById(R.id.creatorView);
icon = (ImageView) v.findViewById(R.id.icon);
info = (TextView) v.findViewById(R.id.info);
}
protected void bind(MemberListItem item) {
@@ -57,6 +64,19 @@ class MemberListItemHolder extends RecyclerView.ViewHolder {
} else {
creator.setVisibility(View.GONE);
}
// visibility information
if (item.getStatus() == OURSELVES || item.getStatus() == UNKNOWN) {
icon.setVisibility(View.GONE);
info.setVisibility(View.GONE);
} else {
icon.setVisibility(View.VISIBLE);
icon.setImageResource(getVisibilityIcon(item.getVisibility()));
info.setVisibility(View.VISIBLE);
info.setText(
getVisibilityString(info.getContext(), item.getVisibility(),
item.getMember().getName()));
}
}
}

View File

@@ -6,7 +6,6 @@ import android.content.Intent;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.preference.CheckBoxPreference;
import android.support.v7.preference.ListPreference;
@@ -55,7 +54,6 @@ import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_RINGT
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_BLOG;
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_FORUM;
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_GROUP;
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_LOCK_SCREEN;
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_PRIVATE;
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_NAME;
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_URI;
@@ -83,8 +81,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
private CheckBoxPreference notifyForumPosts;
private CheckBoxPreference notifyBlogPosts;
private CheckBoxPreference notifyVibration;
private CheckBoxPreference notifyLockscreen;
private Preference notifySound;
// Fields that are accessed from background threads must be volatile
@@ -118,8 +114,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
"pref_key_notify_blog_posts");
notifyVibration = (CheckBoxPreference) findPreference(
"pref_key_notify_vibration");
notifyLockscreen = (CheckBoxPreference) findPreference(
"pref_key_notify_lock_screen");
notifySound = findPreference("pref_key_notify_sound");
enableBluetooth.setOnPreferenceChangeListener(this);
@@ -129,10 +123,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
notifyForumPosts.setOnPreferenceChangeListener(this);
notifyBlogPosts.setOnPreferenceChangeListener(this);
notifyVibration.setOnPreferenceChangeListener(this);
if (Build.VERSION.SDK_INT >= 21) {
notifyLockscreen.setVisible(true);
notifyLockscreen.setOnPreferenceChangeListener(this);
}
notifySound.setOnPreferenceClickListener(
new Preference.OnPreferenceClickListener() {
@Override
@@ -243,9 +234,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
notifyVibration.setChecked(settings.getBoolean(
PREF_NOTIFY_VIBRATION, true));
notifyLockscreen.setChecked(settings.getBoolean(
PREF_NOTIFY_LOCK_SCREEN, false));
String text;
if (settings.getBoolean(PREF_NOTIFY_SOUND, true)) {
String ringtoneName =
@@ -302,10 +290,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
Settings s = new Settings();
s.putBoolean(PREF_NOTIFY_VIBRATION, (Boolean) o);
storeSettings(s);
} else if (preference == notifyLockscreen) {
Settings s = new Settings();
s.putBoolean(PREF_NOTIFY_LOCK_SCREEN, (Boolean) o);
storeSettings(s);
}
return true;
}

View File

@@ -4,6 +4,9 @@ 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;
@@ -19,18 +22,27 @@ 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 {
private static final Logger LOG =
Logger.getLogger(SplashScreenActivity.class.getName());
// This build expires on 1 July 2017
private static final long EXPIRY_DATE = 1498863600 * 1000L;
@Inject
protected ConfigController configController;
@Inject
protected AndroidExecutor androidExecutor;
public SplashScreenActivity() {
Logger.getLogger("").setLevel(DEFAULT_LOG_LEVEL);
enableStrictMode();
}
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
@@ -71,6 +83,23 @@ public class SplashScreenActivity extends BaseActivity {
}
}
@Override
protected void showNewScreenFilterWarning() {
}
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

@@ -142,6 +142,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
if (replyIdBytes != null) replyId = new MessageId(replyIdBytes);
}
loadItems();
sharingController.setSharingListener(this);
loadSharingContacts();
}
@@ -238,7 +239,6 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
public void onStart() {
super.onStart();
sharingController.onStart();
loadItems();
list.startPeriodicUpdate();
}

View File

@@ -99,14 +99,13 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
@Override
public void onActivityDestroy() {
final MessageId messageId = listener.getFirstVisibleMessageId();
if (messageId != null) {
final MessageId first = listener.getFirstVisibleMessageId();
if (first != null) {
dbExecutor.execute(new Runnable() {
@Override
public void run() {
try {
messageTracker
.storeMessageId(groupId, messageId);
messageTracker.storeMessageId(groupId, first);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);

View File

@@ -31,7 +31,6 @@ import static android.text.format.DateUtils.FORMAT_ABBREV_TIME;
import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
public class UiUtils {
@@ -65,12 +64,6 @@ public class UiUtils {
MIN_DATE_RESOLUTION, flags).toString();
}
public static int getDaysUntilExpiry() {
long now = System.currentTimeMillis();
long daysBeforeExpiry = (EXPIRY_DATE - now) / 1000 / 60 / 60 / 24;
return (int) daysBeforeExpiry;
}
public static SpannableStringBuilder getTeaser(Context ctx, Spanned body) {
if (body.length() < TEASER_LENGTH)
throw new IllegalArgumentException(

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android.view;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.support.annotation.DimenRes;
@@ -15,7 +16,9 @@ import android.widget.TextView;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.R;
import org.briarproject.briar.android.blog.BlogActivity;
import org.briarproject.briar.android.util.UiUtils;
import javax.annotation.Nullable;
@@ -24,10 +27,12 @@ import de.hdodenhof.circleimageview.CircleImageView;
import im.delight.android.identicons.IdenticonDrawable;
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.graphics.Typeface.BOLD;
import static android.util.TypedValue.COMPLEX_UNIT_PX;
import static org.briarproject.bramble.api.identity.Author.Status.NONE;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
@UiThread
public class AuthorView extends RelativeLayout {
@@ -105,16 +110,24 @@ public class AuthorView extends RelativeLayout {
requestLayout();
}
public void setAuthorClickable(OnClickListener listener) {
public void setBlogLink(final GroupId groupId) {
setClickable(true);
TypedValue outValue = new TypedValue();
getContext().getTheme().resolveAttribute(
android.R.attr.selectableItemBackground, outValue, true);
setBackgroundResource(outValue.resourceId);
setOnClickListener(listener);
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(getContext(), BlogActivity.class);
i.putExtra(GROUP_ID, groupId.getBytes());
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
getContext().startActivity(i);
}
});
}
public void setAuthorNotClickable() {
public void unsetBlogLink() {
setClickable(false);
setBackgroundResource(android.R.color.transparent);
setOnClickListener(null);

View File

@@ -1,51 +0,0 @@
package org.briarproject.briar.android.widget;
import android.content.Context;
import android.support.annotation.AttrRes;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import static android.view.MotionEvent.FLAG_WINDOW_IS_OBSCURED;
@NotNullByDefault
public class TapSafeFrameLayout extends FrameLayout {
@Nullable
private OnTapFilteredListener listener;
public TapSafeFrameLayout(Context context) {
super(context);
setFilterTouchesWhenObscured(false);
}
public TapSafeFrameLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setFilterTouchesWhenObscured(false);
}
public TapSafeFrameLayout(Context context, @Nullable AttributeSet attrs,
@AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
setFilterTouchesWhenObscured(false);
}
public void setOnTapFilteredListener(OnTapFilteredListener listener) {
this.listener = listener;
}
@Override
public boolean onFilterTouchEventForSecurity(MotionEvent e) {
boolean filter = (e.getFlags() & FLAG_WINDOW_IS_OBSCURED) != 0;
if (filter && listener != null) listener.onTapFiltered();
return !filter;
}
public interface OnTapFilteredListener {
void onTapFiltered();
}
}

View File

@@ -18,14 +18,19 @@ public interface AndroidNotificationManager {
String PREF_NOTIFY_RINGTONE_NAME = "notifyRingtoneName";
String PREF_NOTIFY_RINGTONE_URI = "notifyRingtoneUri";
String PREF_NOTIFY_VIBRATION = "notifyVibration";
String PREF_NOTIFY_LOCK_SCREEN = "notifyLockScreen";
void clearContactNotification(ContactId c);
void clearAllContactNotifications();
void clearGroupMessageNotification(GroupId g);
void clearAllGroupMessageNotifications();
void clearForumPostNotification(GroupId g);
void clearAllForumPostNotifications();
void clearBlogPostNotification(GroupId g);
void clearAllBlogPostNotifications();
@@ -38,6 +43,18 @@ public interface AndroidNotificationManager {
void unblockNotification(GroupId g);
void blockAllContactNotifications();
void unblockAllContactNotifications();
void blockAllGroupMessageNotifications();
void unblockAllGroupMessageNotifications();
void blockAllForumPostNotifications();
void unblockAllForumPostNotifications();
void blockAllBlogPostNotifications();
void unblockAllBlogPostNotifications();

View File

@@ -2,10 +2,14 @@ package org.briarproject.briar.api.android;
import android.support.annotation.UiThread;
import java.util.Collection;
import java.util.Set;
public interface ScreenFilterMonitor {
@UiThread
Set<String> getApps();
@UiThread
void storeAppsAsShown(Collection<String> s, boolean persistent);
}

View File

@@ -104,9 +104,8 @@ 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_recent2";
private static final String EMOJI_LRU_PREFERENCE = "pref_emoji_recent";
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, "\t");
return StringUtils.join(emojis, ";");
}
private LinkedHashSet<String> deserialize(@Nullable String serialized) {
if (serialized == null) return new LinkedHashSet<>();
String[] list = serialized.split("\t");
String[] list = serialized.split(";");
LinkedHashSet<String> result = new LinkedHashSet<>(list.length);
Collections.addAll(result, list);
return result;

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@@ -1,42 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/margin_large">
android:gravity="center_horizontal"
android:padding="20dp" >
<android.support.design.widget.TextInputLayout
android:id="@+id/createForumNameLayout"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="@dimen/text_size_medium"
android:text="@string/choose_forum_name" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true">
android:id="@+id/createForumNameEntry"
android:maxLines="1"
android:inputType="text|textCapSentences" />
<EditText
android:id="@+id/createForumNameEntry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/choose_forum_hint"
android:inputType="text|textCapSentences"
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/createForumFeedback"
android:gravity="center"
android:paddingLeft="50dp"
android:paddingRight="50dp" />
<Button
android:id="@+id/createForumButton"
style="@style/BriarButton"
android:enabled="false"
android:text="@string/create_forum_button"/>
android:id="@+id/createForumButton"
android:text="@string/create_forum_button" />
<ProgressBar
android:id="@+id/createForumProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:indeterminate="true"
android:visibility="gone"
tools:visibility="visible"/>
android:visibility="gone" />
</LinearLayout>

View File

@@ -5,5 +5,5 @@
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/expiry_date_reached"
android:text="@string/expiry_warning"
android:textSize="@dimen/text_size_large"/>

View File

@@ -15,38 +15,6 @@
<include layout="@layout/toolbar"/>
<RelativeLayout
android:id="@+id/expiryWarning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/briar_warning_background"
android:orientation="horizontal"
android:padding="@dimen/margin_medium"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/expiryWarningText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/expiryWarningClose"
android:text="@plurals/expiry_warning"
android:textColor="@color/briar_text_primary_inverse"
android:textSize="@dimen/text_size_small"/>
<ImageView
android:id="@+id/expiryWarningClose"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:contentDescription="@string/close"
android:scaleType="center"
android:src="@drawable/ic_close"
android:tint="@color/briar_text_tertiary_inverse"/>
</RelativeLayout>
<FrameLayout
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"

View File

@@ -15,7 +15,7 @@
android:layout_weight="1"
android:gravity="top"
android:hint="@string/blogs_rss_feeds_import_hint"
android:inputType="textUri"/>
android:inputType="textMultiLine|textUri"/>
<Button
android:id="@+id/importButton"

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:filterTouchesWhenObscured="false"
android:orientation="vertical">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fadeScrollbars="false"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingStart="20dp"
android:paddingTop="20dp"
android:theme="@style/BriarTheme">
<TextView
android:id="@+id/alert_dialog_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="3dp"
android:paddingRight="6dp"
android:text="TextView"
android:textAppearance="@style/BriarTextBody"
android:theme="@+theme/BriarDialogTheme"/>
</ScrollView>
<CheckBox
android:id="@+id/checkBox_screen_filter_reminder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginStart="15dp"
android:layout_weight="0"
android:filterTouchesWhenObscured="false"
android:text="@string/checkbox_dont_show_again"
android:textAppearance="@style/BriarTextBody"/>
</LinearLayout>

View File

@@ -1,28 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/margin_large">
android:padding="@dimen/margin_medium">
<android.support.design.widget.TextInputLayout
android:id="@+id/nameLayout"
<EditText
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true">
<EditText
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/groups_create_group_hint"
android:inputType="text|textCapSentences"
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout>
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="bottom"
android:maxLines="1"
android:inputType="text|textCapSentences"
android:hint="@string/groups_create_group_hint"/>
<Button
android:id="@+id/button"
@@ -30,13 +22,4 @@
android:enabled="false"
android:text="@string/groups_create_group_button"/>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"
android:visibility="gone"
tools:visibility="visible"/>
</LinearLayout>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@@ -7,100 +7,119 @@
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_height="0dp"
android:layout_weight="1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_activity_horizontal"
android:gravity="center"
android:orientation="horizontal">
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top|center_horizontal"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarContact1"
style="@style/BriarAvatar"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
tools:src="@mipmap/ic_launcher_round"/>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/nameContact1"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
android:gravity="center"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_small"
tools:text="Contact 1"/>
android:layout_toLeftOf="@+id/introductionIcon"
android:gravity="top|center_horizontal"
android:orientation="vertical">
</LinearLayout>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarContact1"
style="@style/BriarAvatar"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
tools:src="@mipmap/ic_launcher_round"/>
<ImageView
android:id="@+id/introductionIcon"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:src="@drawable/ic_contact_introduction"
tools:ignore="ContentDescription"/>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/nameContact1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
android:gravity="center"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_tiny"
tools:text="Contact 1"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top|center_horizontal"
android:orientation="vertical">
</LinearLayout>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarContact2"
style="@style/BriarAvatar"
<ImageView
android:id="@+id/introductionIcon"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
tools:src="@mipmap/ic_launcher_round"/>
android:layout_centerHorizontal="true"
android:src="@drawable/ic_contact_introduction"
tools:ignore="ContentDescription"/>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/nameContact2"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
android:gravity="center"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_small"
tools:text="Contact 2"/>
android:layout_toEndOf="@+id/introductionIcon"
android:layout_toRightOf="@+id/introductionIcon"
android:gravity="top|center_horizontal"
android:orientation="vertical">
</LinearLayout>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarContact2"
style="@style/BriarAvatar"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
tools:src="@mipmap/ic_launcher_round"/>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/nameContact2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
android:gravity="center"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_tiny"
tools:text="Contact 2"/>
</LinearLayout>
</RelativeLayout>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
tools:visibility="gone"/>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/introductionText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_activity_horizontal"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_medium"
tools:text="@string/introduction_message_text"/>
</LinearLayout>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
tools:visibility="gone"/>
</ScrollView>
<org.briarproject.briar.android.view.LargeTextInputView
android:id="@+id/introductionMessageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:buttonText="@string/introduction_button"
app:hint="@string/introduction_message_hint"
app:maxLines="5"/>
<org.briarproject.briar.android.view.LargeTextInputView
android:id="@+id/introductionMessageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:buttonText="@string/introduction_button"
app:hint="@string/introduction_message_hint"
app:maxLines="5"/>
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@@ -19,15 +19,57 @@
android:textStyle="italic"
tools:text="@string/groups_member_joined"/>
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/info"
android:layout_alignLeft="@+id/text"
android:layout_alignTop="@+id/info"
android:layout_below="@+id/text"
android:layout_marginRight="@dimen/margin_medium"
android:scaleType="center"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_visibility"/>
<TextView
android:id="@+id/info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignEnd="@+id/text"
android:layout_alignRight="@+id/text"
android:layout_below="@+id/text"
android:layout_marginBottom="@dimen/margin_medium"
android:layout_toRightOf="@+id/icon"
android:gravity="center_vertical"
android:minHeight="24dp"
android:textColor="@color/briar_text_secondary"
android:textIsSelectable="true"
android:textSize="@dimen/text_size_tiny"
android:textStyle="italic"
tools:text="@string/groups_reveal_visible_revealed_by_contact"/>
<org.briarproject.briar.android.view.AuthorView
android:id="@+id/author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/text"
android:layout_alignStart="@+id/text"
android:layout_below="@+id/text"
android:layout_below="@+id/info"
android:layout_toLeftOf="@+id/optionsButton"
app:persona="commenter"/>
<Button
android:id="@+id/optionsButton"
style="@style/BriarButtonFlat.Positive.Tiny"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/author"
android:layout_alignEnd="@+id/text"
android:layout_alignRight="@+id/text"
android:layout_below="@+id/info"
android:gravity="right|center_vertical"
android:text="@string/options"/>
<View
style="@style/Divider.ThreadItem"
android:layout_below="@+id/author"

View File

@@ -13,13 +13,11 @@
android:id="@+id/authorView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="@dimen/margin_small"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_toStartOf="@+id/bulbView"
android:layout_toLeftOf="@+id/bulbView"
app:persona="list"/>
@@ -31,7 +29,6 @@
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/authorView"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
tools:src="@drawable/contact_connected"/>
@@ -42,16 +39,40 @@
android:layout_below="@+id/authorView"
android:layout_marginBottom="@dimen/margin_small"
android:layout_marginLeft="@dimen/listitem_group_member_indentation"
android:layout_marginStart="@dimen/listitem_group_member_indentation"
android:text="@string/groups_member_created_you"
android:textColor="@color/briar_text_secondary"
tools:visibility="visible"/>
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/creatorView"
android:layout_marginLeft="@dimen/listitem_group_member_indentation"
android:layout_marginRight="@dimen/margin_small"
android:contentDescription="@string/forum_invitation_already_sharing"
android:src="@drawable/ic_visibility"/>
<TextView
android:id="@+id/info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/creatorView"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_toRightOf="@+id/icon"
android:gravity="center_vertical"
android:minHeight="24dp"
android:textColor="@color/briar_text_secondary"
android:textIsSelectable="true"
android:textSize="@dimen/text_size_tiny"
android:textStyle="italic"
tools:text="@string/groups_reveal_visible_revealed_by_us"/>
<View
android:id="@+id/divider"
style="@style/Divider.ContactList"
android:layout_below="@+id/creatorView"
android:layout_marginStart="@dimen/listitem_group_member_indentation"
android:layout_below="@+id/info"
android:layout_marginLeft="@dimen/listitem_group_member_indentation"
android:layout_marginTop="@dimen/margin_medium"/>

View File

@@ -9,17 +9,17 @@
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_reveal"
android:icon="@drawable/ic_visibility_white"
android:title="@string/groups_reveal_contacts"
app:showAsAction="never"/>
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_group_invite"
android:icon="@drawable/ic_add_white"
android:title="@string/groups_invite_members"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_group_leave"

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,11 +22,7 @@
<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>
<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>
<string name="expiry_warning">Diese Version von Briar ist nicht mehr aktuell.\nBitte installiere eine neuere Version.</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Navigationsleiste öffnen</string>
<string name="nav_drawer_close_description">Navigationsleiste schliessen</string>
@@ -80,7 +76,7 @@
<string name="text_too_long">Der eingegebene Text ist leider zu lang</string>
<string name="show_onboarding">Hilfe anzeigen</string>
<!--Contacts and Private Conversations-->
<string name="no_contacts">Du scheinst hier neu zu sein und noch keine Kontakte zu haben.\n\nTippe auf das \"+\"-Symbol am oberen Bildschirmrand an und folge den Anweisungen, um Freunde zu deiner Kontaktliste hinzuzufügen.\n\nDenke daran, dass du neue Kontakte nur dann hinzufügen kannst, wenn du ihnen persönlich begegnest. Das hindert andere daran, sich als deine Person auszugeben oder deine Nachrichten zu lesen.</string>
<string name="no_contacts">Du scheinst hier neu zu sein und noch keine Kontakte zu haben.\n\nVerwende das \"+\"-Icon am oberen Bildschirmrand an und folge den Anweisungen um Freunde zu deiner Kontaktliste hinzuzufügen.\n\nDenke daran dass Du neue Kontakte nur dann hinzufügen kannst, wenn Du ihnen physisch begegnest. Das hindert andere daran, sich als Deine Person auszugeben oder Deine Nachrichten zu lesen.</string>
<string name="date_no_private_messages">Keine Nachrichten.</string>
<string name="no_private_messages">Das ist die Gesprächsansicht.\n\nEs scheint hier einen Mangel an Gesprächsthemen zu geben.\n\nUm ein neues Gespräch zu beginnen, verwende einfach das Eingabefeld am unteren Bildschirmrand.</string>
<string name="message_hint">Nachricht eingeben</string>
@@ -121,7 +117,8 @@
<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_message_text">Du kannst eine Nachricht erstellen die mit Deiner Kontaktempfehlung an %1$s und %2$s mitgesendet wird:</string>
<string name="introduction_message_hint">Gib eine Nachricht ein (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>
@@ -140,7 +137,7 @@
<item quantity="other">%d neue Kontakte hinzugefügt.</item>
</plurals>
<!--Private Groups-->
<string name="groups_list_empty">Du bist in keiner Gruppe Mitglied.\n\nTippe auf das \"+\"-Symbol am oberen Bildschirmrand, um eine Gruppe zu erstellen, oder frage deine Kontakte um Aufnahme in einer ihrer Gruppen.</string>
<string name="groups_list_empty">Du bist in keiner Gruppe Mitglied.\n\nVerwende das + Symbol am oberen Rand um eine Gruppe zu erstellen oder frage bitte Deine Kontakte um Aufnahme in einer deren Gruppen.</string>
<string name="groups_created_by">Erstellt durch %s</string>
<plurals name="messages">
<item quantity="one">%d Nachrichten</item>
@@ -152,7 +149,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_create_group_hint">Gebe Deiner privaten Gruppe einen Namen</string>
<string name="groups_invitation_sent">Gruppeneinladung versendet</string>
<string name="groups_message_sent">Nachricht gesendet</string>
<string name="groups_member_list">Mitglieder</string>
@@ -193,9 +190,9 @@
<string name="groups_reveal_visible_revealed_by_contact">Beziehung zum Kontakt ist für diese Gruppe sichtbar (offengelegt durch %s)</string>
<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="no_forums">Du hast noch keine Foren.\n\nWarum erstellst du nicht einfach selbst ein neues Forum, indem du auf das \"+\"-Icon am oberen Bildschirmrand tippst?\n\nDu kannst auch deine Kontakte auffordern, Foren mit dir zu teilen.</string>
<string name="create_forum_title">Neues Forum</string>
<string name="choose_forum_name">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>
@@ -219,7 +216,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_message">Du kannst eine optionale Einladungsnachricht verfassen, die an die ausgewählten Kontakte geschickt wird.</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>
@@ -251,11 +248,12 @@
<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_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_comment_hint">Füge optionalen Kommentar hinzu</string>
<string name="blogs_reblog_button">Rebloggen</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Blog teilen</string>
@@ -266,8 +264,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$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_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_invitations_title">Blogeinladungen</string>
<string name="blogs_sharing_joined_toast">Blog abonniert</string>
<string name="blogs_sharing_declined_toast">Blogeinladung abgelehnt</string>
@@ -285,7 +283,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>
@@ -320,17 +318,11 @@
<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_private_messages_setting">Benachrichtigungen für private Nachrichten anzeigen</string>
<string name="notify_group_messages_setting">Benachrichtigungen für Gruppennachrichten anzeigen</string>
<string name="notify_forum_posts_setting">Benachrichtigungen für Forenbeiträge anzeigen</string>
<string name="notify_blog_posts_setting">Benachrichtigungen für Blog-Beiträ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>
@@ -346,7 +338,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>
@@ -362,6 +354,8 @@
<!--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>
<string name="screen_filter_title">Bildschirmfilter erkannt</string>
<string name="screen_filter_body">Diese Apps können andere Apps überlagern:\n\n%1$s \n\nBriar reagiert im Fall einer Überlagerung nicht auf Benutzereingaben.
Falls dadurch Probleme in der Verwendung von Briar entstehen, versuche diese Apps zu deaktivieren.\n</string>
<string name="checkbox_dont_show_again">Nicht noch einmal für diese Apps warnen</string>
</resources>

View File

@@ -2,7 +2,7 @@
<resources>
<!--Setup-->
<string name="setup_title">Configuración de Briar</string>
<string name="setup_explanation">Tu cuenta de Briar se almacena de manera cifrada en tu dispositivo y no en ninguna nube. Si desinstalas Briar u olvidas tu contraseña, no podrás recuperar ni tu cuenta ni tus datos.</string>
<string name="setup_explanation">Tu cuenta de Briar se almacena de manera cifrada en tu teléfono y no en ninguna nube. Si desinstalas Briar u olvidas tu contraseña, no podrás recuperar ni tu cuenta, ni tus datos.</string>
<string name="choose_nickname">Elige tu nombre de usuario</string>
<string name="choose_password">Elige tu contraseña</string>
<string name="confirm_password">Confirma tu contraseña</string>
@@ -13,20 +13,16 @@
<!--Login-->
<string name="enter_password">Introduce tu contraseña:</string>
<string name="try_again">Contraseña incorrecta, inténtalo de nuevo</string>
<string name="sign_in_button">Identificarse</string>
<string name="sign_in_button">Registrarse</string>
<string name="forgotten_password">Olvidé mi contraseña</string>
<string name="dialog_title_lost_password">Contraseña olvidada</string>
<string name="dialog_message_lost_password">Tu cuenta de Briar se almacena de manera cifrada en tu dispositivo y no en ninguna nube, así que no podemos reiniciar tu contraseña. ¿Deseas eliminar tu cuenta y empezar de nuevo?\n\nAdvertencia: tus identidades, contactos y mensajes se perderán para siempre.</string>
<string name="dialog_message_lost_password">Tu cuenta de Briar se almacena de manera cifrada en tu teléfono y no en ninguna nube, así que no podemos reiniciar tu contraseña. ¿Deseas eliminar tu cuenta y empezar de nuevo?\n\nAdvertencia: Tus identidades, contactos y mensajes se perderán para siempre.</string>
<string name="startup_failed_notification_title">Briar no pudo iniciarse</string>
<string name="startup_failed_notification_text">Quizá tengas que reinstalar Briar.</string>
<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>
<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>
<string name="expiry_warning">Esta versión ha caducado.\nInstala una más reciente, por favor.</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>
@@ -48,8 +44,8 @@
<item quantity="other">Tienes %d nuevos mensajes privados.</item>
</plurals>
<plurals name="group_message_notification_text">
<item quantity="one">Tienes un nuevo mensaje de grupo.</item>
<item quantity="other">Tienes %d nuevos mensajes de grupo.</item>
<item quantity="one"> h</item>
<item quantity="other">%d mensajes de grupo nuevos.</item>
</plurals>
<plurals name="forum_post_notification_text">
<item quantity="one">Hay una nueva publicación en el foro.</item>
@@ -80,9 +76,9 @@
<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 ícono + 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="no_private_messages">Ésta 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>
<string name="delete_contact">Eliminar contacto</string>
<string name="dialog_title_delete_contact">Confirmar eliminación de contacto</string>
@@ -112,7 +108,7 @@
<string name="contact_already_exists">El contacto %s ya existe</string>
<string name="contact_exchange_failed">El intercambio del contacto falló</string>
<string name="qr_code_invalid">El código QR no es válido</string>
<string name="connecting_to_device">Conectando al dispositivo\u2026</string>
<string name="connecting_to_device">Conectado al dispositivo\u2026</string>
<string name="authenticating_with_device">Autentificándose con el dispositivo\u2026</string>
<string name="connection_aborted_local">¡Hemos interumpido la conexión! Esto podría significar que alguien está intentando interceptar la conexión</string>
<string name="connection_aborted_remote">¡La conexión ha sido interrumpida por tu contacto! Esto podría significar que alguien está intentando interceptar la conexión</string>
@@ -121,8 +117,9 @@
<string name="introduction_onboarding_text">Presenta a tus contactos entre sí para ahorrarles encontrarse en persona para poder conectar mediante Briar.</string>
<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">Presentarle a</string>
<string name="introduction_message_text">Puedes redactar un mensaje que se enviará a %1$s y a %2$s junto con tu presentación:</string>
<string name="introduction_message_hint">Escribe un mensaje (opcional)</string>
<string name="introduction_button">Realizar la presentación</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>
@@ -140,7 +137,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 ícono + 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>
@@ -152,7 +149,7 @@
<string name="groups_create_group_title">Crear grupo privado</string>
<string name="groups_create_group_button">Crear grupo</string>
<string name="groups_create_group_invitation_button">Enviar invitación</string>
<string name="groups_create_group_hint">Elige un nombre para tu grupo privado</string>
<string name="groups_create_group_hint">Dar nombre al grupo privado</string>
<string name="groups_invitation_sent">Se ha mandado la invitación de grupo</string>
<string name="groups_message_sent">Mensaje enviado</string>
<string name="groups_member_list">Integrantes</string>
@@ -172,7 +169,7 @@
<string name="groups_dissolved_dialog_message">El creador de este grupo lo ha disuelto.\n\nYa no puedes escribir mensajes al grupo y puede que no recibas todas las publicaciones que se hayan escrito.</string>
<!--Private Group Invitations-->
<string name="groups_invitations_title">Invitaciones de grupo</string>
<string name="groups_invitations_invitation_sent">Has invitado a %1$s a ingresar en el grupo «%2$s».</string>
<string name="groups_invitations_invitation_sent">Has invitado a %1$s a ingresar en el grupo \"%2$s\".</string>
<string name="groups_invitations_invitation_received">%1$s te ha invitado a unirte al grupo «%2$s».</string>
<string name="groups_invitations_joined">Te uniste al grupo</string>
<string name="groups_invitations_declined">Invitación de grupo declinada</string>
@@ -193,12 +190,12 @@
<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="create_forum_title">Crear foro</string>
<string name="choose_forum_hint">Elige un nombre para el foro</string>
<string name="no_forums">No tienes ningún foro aún.\n\n¿Por qué no creas uno pulsando el ícono + de la parte superior?\n\nTambién puedes pedirle a tus contactos que compartan foros contigo.</string>
<string name="create_forum_title">Nuevo foro</string>
<string name="choose_forum_name">Elige un nombre para tu 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>
<string name="no_forum_posts">Este foro está vacío.\n\nUsa el ícono 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>
<string name="no_posts">Sin publicaciones</string>
<plurals name="posts">
<item quantity="one">%d publicación</item>
@@ -219,7 +216,7 @@
<string name="activity_share_toolbar_header">Elige contactos</string>
<string name="no_contacts_selector">Parece que eres nuevo aquí y no tienes contactos aún.\n\nPor favor, vuelve cuando hayas añadido tu primer contacto.</string>
<string name="forum_shared_snackbar">Foro compartido con los contactos seleccionados</string>
<string name="forum_share_message">Añade un mensaje (opcional)</string>
<string name="forum_share_message">Puedes redactar un mensaje de invitación opcional que se enviará a los contactos seleccionados.</string>
<string name="forum_share_error">Hubo un error compartiendo este foro.</string>
<string name="forum_invitation_received">%1$s ha compartido el foro \"%2$s\" contigo.</string>
<string name="forum_invitation_sent">Has compartido el foro \"%1$s\" con %2$s.</string>
@@ -250,12 +247,13 @@
<string name="blogs_blog_post_created">Creado artículo del blog</string>
<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_feed_empty_state">Esta es la lista global de entradas del blog.\n\nParece que nadie ha publicado nada todavía.\n\nSé el primero: pulsa el icono 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>
<string name="blogs_blog_removed">Blog eliminado</string>
<string name="blogs_reblog_comment_hint">Añade un comentario (opcional)</string>
<string name="blogs_reblog_comment_hint">Añade un comentario opcional</string>
<string name="blogs_reblog_button">Rebloguear</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Compartir blog</string>
@@ -266,8 +264,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 \"%2$s\" contigo.</string>
<string name="blogs_sharing_invitation_sent">Has compartido el blog \"%1$s\" con %2$s.</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_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>
@@ -289,7 +287,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">Conectar mediante Bluetooth</string>
<string name="bluetooth_setting">Connectar 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>
@@ -320,19 +318,13 @@
<string name="uninstall_setting_summary">Requerirá confirmación manual en el momento de pánico</string>
<!--Settings Notifications-->
<string name="notification_settings_title">Notificaciones</string>
<string name="notify_private_messages_setting_title">Mensajes privados</string>
<string name="notify_private_messages_setting_summary">Notificar mensajes privados</string>
<string name="notify_group_messages_setting_title">Mensajes de grupo</string>
<string name="notify_group_messages_setting_summary">Notificar mensajes de grupo</string>
<string name="notify_forum_posts_setting_title">Publicaciones en foros</string>
<string name="notify_forum_posts_setting_summary">Notificar publicaciones en foros</string>
<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_private_messages_setting">Mostrar alertas para mensajes privados</string>
<string name="notify_group_messages_setting">Mostrar alertas para mensajes de grupo</string>
<string name="notify_forum_posts_setting">Mostrar alertas para publicaciones de foros</string>
<string name="notify_blog_posts_setting">Mostrar alertas para artículos de blogs</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_default">Alerta sonora predeterminada</string>
<string name="notify_sound_setting_disabled">Ninguna</string>
<string name="choose_ringtone_title">Elegir alerta sonora</string>
<!--Settings Feedback-->
@@ -362,6 +354,8 @@
<!--Sign Out-->
<string name="progress_title_logout">Saliendo de Briar…</string>
<!--Screen Filters & Tapjacking-->
<string name="screen_filter_title">Superposición de pantalla detectada</string>
<string name="screen_filter_body">Otra aplicación se está mostrando encima de Briar. Por seguridad, Briar no responderá a los toques mientras se muestre otra aplicación encima.\n\nIntenta apagar las siguientes aplicaciones cuando uses Briar:\n\n%1$s</string>
<string name="screen_filter_title">Filtro de pantalla detectado</string>
<string name="screen_filter_body">Las siguientes aplicaciones pueden mostrarse por encima de otras:\n\n%1$s \n\nBriar no reaccionará a los toques mientras otra aplicación se muestre encima.
Si experiencia algún problema, pruebe a desactivar esas aplicaciones cuando use Briar.\n</string>
<string name="checkbox_dont_show_again">No mostrar más para estas aplicaciones</string>
</resources>

View File

@@ -22,11 +22,7 @@
<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>
<plurals name="expiry_warning">
<item quantity="one">Ceci est une version béta de Briar. Votre compte expirera dans %d jour et ne peut pas être renouvelé.</item>
<item quantity="other">Ceci est une version béta de Briar. Votre compte expirera dans %d jours et ne peut pas être renouvelé.</item>
</plurals>
<string name="expiry_date_reached">Ce logiciel a expiré.\nMerci de l\'avoir testé!</string>
<string name="expiry_warning">Ce logiciel est arrivé à expiration.\nVeuillez installer une version plus récente.</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>
@@ -53,11 +49,11 @@
</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="other">%d nouveaux posts 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="other">%d Nouveaux posts de blog.</item>
</plurals>
<!--Misc-->
<string name="now">Maintenant</string>
@@ -101,7 +97,7 @@
<string name="try_again_button">Essayer à nouveau</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 d\'invitation 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>
@@ -121,7 +117,8 @@
<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_message_text">Vous pouvez composer un message qui sera envoyé à %1$s et %2$s avec votre introduction :</string>
<string name="introduction_message_hint">Taper le 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>
@@ -152,7 +149,7 @@
<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_create_group_hint">Ajouter un nom au 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>
@@ -194,17 +191,17 @@
<string name="groups_reveal_invisible">Votre lien avec le contact est invisible 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="create_forum_title">Créer un forum</string>
<string name="choose_forum_hint">Choisir un nom pour le Forum </string>
<string name="create_forum_title">Nouveau forum</string>
<string name="choose_forum_name">Choisissez 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="no_forum_posts">Ce forum est vide.\n\nUtilisez l\'icône crayon, en haut pour écrire le premier post.\n\nVous sentez-vous seul, ici? Partager ce forum avec d\'autres contacts !</string>
<string name="no_posts">Aucun post</string>
<plurals name="posts">
<item quantity="one">%d message</item>
<item quantity="other">%d messages</item>
</plurals>
<string name="forum_new_entry_posted">Message de forum saisi</string>
<string name="forum_new_entry_posted">Entrée de forum posté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>
@@ -219,7 +216,7 @@
<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_message">Vous pouvez composer un message d\'invitation optionnel qui sera envoyé aux contacts choisis.</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>
@@ -242,20 +239,21 @@
</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="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 posts 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_write_blog_post">Écrire un post de blog</string>
<string name="blogs_write_blog_post_body_hint">Saisir ici votre post de blog</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_created">Post de blog créé</string>
<string name="blogs_blog_post_received">Nouveau post 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_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 post 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_dialog_message">Êtes-vous sûr de vouloir supprimer ce blog et tous ses posts ?\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_comment_hint">Ajouter un commentaire optionnel</string>
<string name="blogs_reblog_button">Reblog</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Partager le blog</string>
@@ -266,8 +264,8 @@
<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 \"%2$s\" avec vous.</string>
<string name="blogs_sharing_invitation_sent">Vous avez partagé le blog \"%1$s\" avec %2$s.</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>
@@ -282,7 +280,7 @@
<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">Êtes-vous sûr de vouloir supprimer ce flux et tous ses messages ?\nLes postes que vous avez partagés ne seront pas supprimés des appareils des autres personnes.</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>
@@ -320,17 +318,11 @@
<string name="uninstall_setting_summary">Ceci nécessite une confirmation manuelle en cas d\'événement de panique</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_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_private_messages_setting">Afficher les alertes pour les messages privés</string>
<string name="notify_group_messages_setting">Afficher les alertes pour les messages de groupe</string>
<string name="notify_forum_posts_setting">Afficher les alertes pour les posts de forum</string>
<string name="notify_blog_posts_setting">Afficher les alertes pour les posts de blog</string>
<string name="notify_vibration_setting">Vibration</string>
<string name="notify_lock_screen_setting_title">Verrouiller l\'écran</string>
<string name="notify_lock_screen_setting_summary">Afficher les notifications sur l\'écran verrouillé</string>
<string name="notify_sound_setting">Sonnerie</string>
<string name="notify_sound_setting_default">Sonnerie par défaut</string>
<string name="notify_sound_setting_disabled">Aucune</string>
@@ -362,6 +354,8 @@
<!--Sign Out-->
<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">Filtre d\'écran détecté</string>
<string name="screen_filter_body">Les applications suivantes ont l\'autorisation de s\'afficher par dessus d\'autres :\n\n%1$s \n\nBriar ne répondra pas aux touches lorsqu\'une autre application s\'affiche par dessus.
En cas de problèmes, essayer d\'arrêter ces applications durant l\'utilisation de Briar.\n</string>
<string name="checkbox_dont_show_again">Ne plus m\'avertir à propos de ces applications</string>
</resources>

View File

@@ -21,15 +21,8 @@
<string name="startup_failed_notification_text">Potresti dover reinstallare Briar</string>
<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>
<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>
<string name="expiry_warning">Questo software è scaduto.\nSi prega di installare una nuova versione.</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>
<string name="contact_list_button">Contatti</string>
<string name="groups_button">Gruppi privati</string>
<string name="forums_button">Forum</string>
@@ -47,10 +40,6 @@
<item quantity="one">Nuovo messaggio privato.</item>
<item quantity="other">%d nuovi messaggi privati.</item>
</plurals>
<plurals name="group_message_notification_text">
<item quantity="one">Nuovo messaggio di gruppo.</item>
<item quantity="other">%d nuovi messaggi di gruppo.</item>
</plurals>
<plurals name="forum_post_notification_text">
<item quantity="one">Nuovo messaggio sul forum.</item>
<item quantity="other">%d nuovi messaggi sul forum.</item>
@@ -65,7 +54,6 @@
<string name="hide">Nascondi</string>
<string name="ok">OK</string>
<string name="cancel">Annulla</string>
<string name="got_it">Ho capito</string>
<string name="delete">Cancella</string>
<string name="accept">Accetta</string>
<string name="decline">Declina</string>
@@ -80,18 +68,14 @@
<string name="text_too_long">Il testo inserito è troppo lungo</string>
<string name="show_onboarding">Mostra l\'aiuto</string>
<!--Contacts and Private Conversations-->
<string name="no_contacts">Sembra che tu sia nuovo qui e non hai ancora contatti.\n\nPremi l\' icona in alto e segui le istruzioni per aggiungere qualche amico alla tua lista.\n\nPer favore ricorda: Puoi solo aggiungere nuovi contatti faccia-a-faccia per evitare che altri ti impersonino o leggano i tuoi messaggi in futuro.</string>
<string name="date_no_private_messages">Nessun messaggio.</string>
<string name="no_private_messages">Questa è la vista conversazione\n\n\Sembra che ci sia una mancanza di conversazione.\n\nPremi il campo di immissione in basso per iniziare una conversazione.</string>
<string name="message_hint">Scrivi un messaggio</string>
<string name="delete_contact">Elimina contatto</string>
<string name="dialog_title_delete_contact">Conferma cancellazione contatto</string>
<string name="dialog_message_delete_contact">Sei sicuro di voler rimuovere questo contatto e tutti i messaggi scambiati con esso?</string>
<string name="contact_deleted_toast">Contatto cancellato</string>
<!--Adding Contacts-->
<string name="add_contact_title">Aggiungi un Contatto</string>
<string name="your_nickname">Scegli l\'identità che vuoi usare:</string>
<string name="face_to_face">Devi incontrarti con la persona che vuoi aggiungere come contatto.\n\nQuesto evita che qualcuno ti impersoni o legga i tuoi messaggi in futuro.</string>
<string name="continue_button">Continua</string>
<string name="your_invitation_code">Il tuo codice di invito è</string>
<string name="enter_invitation_code">Si prega di inserire il vostro codice di invito di contatto:</string>
@@ -104,31 +88,22 @@
<string name="your_confirmation_code">Il tuo codice di conferma è</string>
<string name="enter_confirmation_code">Si prega di inserire il vostro codice di conferma di contatto:</string>
<string name="waiting_for_contact">In attesa del contatto\u2026</string>
<string name="waiting_for_contact_to_scan">Aspettando il contatto per la scansione ed il collegamento\u2026</string>
<string name="exchanging_contact_details">Scambio dettagli contatto\u2026</string>
<string name="codes_do_not_match">I codici non corrispondono</string>
<string name="interfering">Questo può voler dire che qualcuno sta cercando di interferire con la tua conessione</string>
<string name="contact_added_toast">Contatto aggiunto: %s</string>
<string name="contact_already_exists">Il contatto %s esiste già</string>
<string name="contact_exchange_failed">Scambio di contatto fallito</string>
<string name="qr_code_invalid">Il codice QR non è valido</string>
<string name="connecting_to_device">Connessione al dispositivo\u2026</string>
<string name="authenticating_with_device">Autenticazione con il dispositivo\u2026</string>
<string name="connection_aborted_local">Connessione abortita da noi!Questo può voler dire che qualcuno sta cercando di interferire con la tua connessione</string>
<string name="connection_aborted_remote">Connessione abortita dal tuo contatto!Questo può voler dire che qualcuno sta cercando di interferire con la tua connessione</string>
<!--Introductions-->
<string name="introduction_onboarding_title">Introduzione tuoi contatti</string>
<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_message_hint">Scrivi 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>
<string name="introduction_response_error">Errore durante la risposta all\' introduzione</string>
<string name="introduction_request_sent">Hai richiesto di introdurre %1$s a %2$s.</string>
<string name="introduction_request_received">%1$s ha chiesto di introdurti in %2$s. Vuoi aggiungere %2$s alla tua lista contatti?</string>
<string name="introduction_request_exists_received">%1$s ha chiesto di introdurti in %2$s, ma %2$s è già nella tua lista contatti. Dato che %1$s può non saperlo, puoi comunque rispondere:</string>
<string name="introduction_request_answered_received">%1$s ha richiesto di introdurti a %2$s.</string>
<string name="introduction_response_accepted_sent">Hai accettato l\'introduzione a %1$s.</string>
<string name="introduction_response_declined_sent">Hai declinato l\'introduzione a %1$s.</string>
@@ -140,91 +115,55 @@
<item quantity="other">Aggiunti %d nuovi contatti.</item>
</plurals>
<!--Private Groups-->
<string name="groups_list_empty">Non stai partecipando a nessun gruppo.\n\nClicca l\' icona + in alto per creare un tuo gruppo o chiedi ai tuoi contatti di essere invitato in uno dei loro gruppi.</string>
<string name="groups_created_by">Creato da %s</string>
<plurals name="messages">
<item quantity="one">%d messaggio</item>
<item quantity="other">%d messaggi</item>
</plurals>
<string name="groups_group_is_empty">Questo gruppo e\' vuoto</string>
<string name="groups_group_is_dissolved">Questo gruppo e\' stato sciolto</string>
<string name="groups_remove">Rimuovere</string>
<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_create_group_hint">Inserisci un nome per il tuo gruppo 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>
<string name="groups_invite_members">Invita Membri</string>
<string name="groups_member_created_you">Hai creato un nuovo gruppo</string>
<string name="groups_member_created">%s e\' il creatore del gruppo.</string>
<string name="groups_member_joined_you">Ti sei unito al gruppo</string>
<string name="groups_member_joined">%s si è unito al gruppo</string>
<string name="groups_leave">Lascia gruppo</string>
<string name="groups_leave_dialog_title">Abbandonare il gruppo</string>
<string name="groups_leave_dialog_message">Sei sicuro di voler abbandonare questo gruppo?</string>
<string name="groups_dissolve">Sciogli il gruppo</string>
<string name="groups_dissolve_dialog_title">Conferma lo scioglimento del gruppo</string>
<string name="groups_dissolve_dialog_message">Sei sicuro di voler sciogliere questo gruppo?\n\nTutti gli altri membri non saranno più in grado di continuare le loro conversazioni e potrebbero non ricevere gli ultimi messaggi.</string>
<string name="groups_dissolve_button">Sciogli</string>
<string name="groups_dissolved_dialog_title">Il gruppo è stato dissolto</string>
<string name="groups_dissolved_dialog_message">Il creatore di questo gruppo l\'ha sciolto.\n\nNon puoi più scrivere messaggi in questo gruppo e potresti non ricevere tutti i messaggi che sono stati scritti.</string>
<!--Private Group Invitations-->
<string name="groups_invitations_title">Inviti spediti</string>
<string name="groups_invitations_invitation_sent">Hai invitato %1$s ad unirsi al gruppo \"%2$s\".</string>
<string name="groups_invitations_invitation_received">%1$s ti ha invitato ad unirti al gruppo \"%2$s\".</string>
<string name="groups_invitations_joined">Unito al gruppo</string>
<string name="groups_invitations_declined">Invito al gruppo rifiutato</string>
<plurals name="groups_invitations_open">
<item quantity="one">%d invito al gruppo aperto.</item>
<item quantity="other">%d inviti al gruppo aperti.</item>
</plurals>
<string name="groups_invitations_response_accepted_sent">Hai accettato l\'invito al gruppo da %s.</string>
<string name="groups_invitations_response_declined_sent">Hai rifiutato l\'invito al gruppo da %s.</string>
<string name="groups_invitations_response_accepted_received">%s ha accettato l\'invito al gruppo.</string>
<string name="groups_invitations_response_declined_received">%s ha rifiutato l\'invito al gruppo.</string>
<string name="sharing_status_groups">Solo il creatore può invitare nuovi membri nel gruppo. Sotto ci sono i membri correnti del gruppo.</string>
<!--Private Groups Revealing Contacts-->
<string name="groups_reveal_contacts">Rivelare contatti</string>
<string name="groups_reveal_dialog_message">Puoi scegliere se rivelare i contatti a tutti i membri correnti e futuri di questo gruppo.\n\nRivelare i contatti fa diventare la connessione al gruppo più veloce e più affidabile, perchè puoi comunicare con i contatti rivelati anche quando il creatore del gruppo è offline.</string>
<string name="groups_reveal_visible">La relazione fra i contatti è visibile al gruppo</string>
<string name="groups_reveal_visible_revealed_by_us">La relazione fra i contatti è visibile al gruppo (rivelata da te)</string>
<string name="groups_reveal_visible_revealed_by_contact">La relazione fra i contatti è visibile al gruppo (rivelata da %s)</string>
<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_title">Nuovo Forum</string>
<string name="choose_forum_name">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>
<string name="no_posts">Nessun post.</string>
<plurals name="posts">
<item quantity="one">%d post</item>
<item quantity="other">%d post</item>
</plurals>
<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">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>
<string name="dialog_button_leave">Lascia</string>
<string name="forum_left_toast">Forum lasciato</string>
<!--Forum Sharing-->
<string name="forum_share_button">Condividi Forum</string>
<string name="contacts_selected">Contatti selezionati</string>
<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>
<string name="forum_invitations_title">Inviti Forum</string>
<string name="forum_invitation_exists">Hai già accettato l\' invito a questo forum. Accettare più inviti cresce e rinforza la comunicazione nel forum.</string>
<string name="forum_joined_toast">Unito al forum</string>
<string name="forum_declined_toast">Invito al forum declinato</string>
<string name="shared_by_format">Condiviso da %s</string>
@@ -234,29 +173,22 @@
<string name="forum_invitation_response_accepted_received">%s ha accettato il tuo invito al forum.</string>
<string name="forum_invitation_response_declined_received">%s ha declinato il tuo invito al forum.</string>
<string name="sharing_status">Stato Condivisione</string>
<string name="sharing_status_forum">Ogni membro del forum può condividere con esso i suoi contatti. Stai condividendo questo forum con i seguenti contatti. Ci potrebbero inoltre essere altri membri che non puoi vedere.</string>
<string name="shared_with">Condiviso con %1$d (%2$d online)</string>
<plurals name="forums_shared">
<item quantity="one">%d forum condiviso dai contatti</item>
<item quantity="other">%d forum condivisi dai contatti</item>
</plurals>
<string name="nobody">Nessuno</string>
<!--Blogs-->
<string name="blogs_other_blog_empty_state">Questo blog è attualmente vuoto.\n\nO l\'autore non ha ancora scritto nulla, oppure la persona che ha condiviso questo blog con te deve tornare in line, in modo che i post possano sincronizzarsi.</string>
<string name="read_more">leggi ancora</string>
<string name="blogs_write_blog_post">Scrivere un post sul blog</string>
<string name="blogs_write_blog_post_body_hint">Scrivi qui il tuo post del blog</string>
<string name="blogs_publish_blog_post">Pubblica</string>
<string name="blogs_blog_post_created">Post blog creato</string>
<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>
<string name="blogs_reblog_comment_hint">Aggiungi un commento facoltativo</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Condividi Blog</string>
<string name="blogs_sharing_error">C\'è stato un errore nella condivisione di questo blog.</string>
@@ -266,14 +198,13 @@
<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 \"%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_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_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>
<string name="sharing_status_blog">Chiunque si iscrive ad un blog può condividerlo con i suoi contatti. Stai condividendo questo blog con i seguenti contatti. Ci potrebbero inoltre essere altri membri che non puoi vedere.</string>
<!--RSS Feeds-->
<string name="blogs_rss_feeds_import">Importa RSS Feed</string>
<string name="blogs_rss_feeds_import">Importa RSS Fee</string>
<string name="blogs_rss_feeds_import_button">Importa</string>
<string name="blogs_rss_feeds_import_hint">Inserire l\'URL dell\'RSS feed</string>
<string name="blogs_rss_feeds_import_error">Ci dispiace! C\'è stato un errore nell\'importazione del tuo feed.</string>
@@ -281,21 +212,11 @@
<string name="blogs_rss_feeds_manage_imported">Importato:</string>
<string name="blogs_rss_feeds_manage_author">Autore:</string>
<string name="blogs_rss_feeds_manage_updated">Ultimo Aggiornamento:</string>
<string name="blogs_rss_remove_feed">Rimuovi feed</string>
<string name="blogs_rss_remove_feed_dialog_message">Sei sicuro di voler rimuovere questo feed e tutti i suoi post?\nOgni post che hai condiviso non sarà rimosso dai dispositivi degli altri.</string>
<string name="blogs_rss_remove_feed_ok">Rimuovi feed</string>
<string name="blogs_rss_feeds_manage_delete_error">Non è stato possibile cancellare il feed!</string>
<string name="blogs_rss_feeds_manage_empty_state">Non hai importato alcun feed RSS.\n\nPerchè non premi il più in alto per aggingere il primo?</string>
<string name="blogs_rss_feeds_manage_error">C\'è stato un problema nel caricare i tuoi feeds. Per favore riprova fra poco.</string>
<!--Settings Network-->
<string name="network_settings_title">Reti</string>
<string name="network_settings_title">Network</string>
<string name="bluetooth_setting">Connessione attraverso Bluetooth</string>
<string name="bluetooth_setting_enabled">Quando i contatti sono vicini</string>
<string name="bluetooth_setting_disabled">Solo con l\'aggiunta di contatti</string>
<string name="tor_network_setting">Connetti via Tor</string>
<string name="tor_network_setting_never">Mai</string>
<string name="tor_network_setting_wifi">Solo se usando Wi-Fi</string>
<string name="tor_network_setting_always">Nel caso di uso Wi-Fi o dati mobile</string>
<!--Settings Security and Panic-->
<string name="security_settings_title">Sicurezza</string>
<string name="change_password">Cambia password</string>
@@ -303,65 +224,37 @@
<string name="choose_new_password">Scegli la tua password:</string>
<string name="confirm_new_password">Conferma la tua nuova password:</string>
<string name="password_changed">La password è stata cambiata.</string>
<string name="panic_setting">Impostazione pulsante panico</string>
<string name="panic_setting_title">Pulsante panico</string>
<string name="panic_setting_hint">Configura come Briar reagirà quando userai un\'app pulsante panico</string>
<string name="panic_app_setting_title">App Pulsante Panico</string>
<string name="unknown_app">un\'app sconosciuta</string>
<string name="panic_app_setting_summary">Nessun app è stata impostata</string>
<string name="panic_app_setting_none">Nessuno</string>
<string name="dialog_title_connect_panic_app">Conferma App Panico</string>
<string name="dialog_message_connect_panic_app">Sei sicuro di voler consentire %1$s di attivare le azioni distruttive del pulsante panico?</string>
<string name="lock_setting_title">Esci</string>
<string name="lock_setting_summary">Uscire da Briar se viene premuto un pulsante di panico</string>
<string name="purge_setting_title">Elimina Account</string>
<string name="purge_setting_summary">Cancella il tuo account Briar se un panic button è premuto. Attenzione: ciò cancellerà permanentemente le tue identità, contatti e messaggi</string>
<string name="uninstall_setting_title">Disinstalla Briar</string>
<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_private_messages_setting">Mostra avvertenze per i messaggi privati</string>
<string name="notify_forum_posts_setting">Mostra avvertenze per post di forum</string>
<string name="notify_blog_posts_setting">Mostra avvertenze 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>
<string name="choose_ringtone_title">Scegli suoneria</string>
<!--Settings Feedback-->
<string name="feedback_settings_title">Feedback</string>
<string name="send_feedback">Invia feedback</string>
<!--Link Warning-->
<string name="link_warning_title">Attenzione Link</string>
<string name="link_warning_intro">Stai per aprire il seguente link con un\' app esterna</string>
<string name="link_warning_text">Ciò può essere usato per identificarti. Pensa se ti fidi della persona che ti ha inviato questo link e considera se aprirlo con Orfox.</string>
<string name="link_warning_open_link">Apri Link</string>
<!--Crash Reporter-->
<string name="crash_report_title">Rapporto Crash Briar</string>
<string name="briar_crashed">Spiacenti, Briar è crashato</string>
<string name="not_your_fault">Questo non è colpa tua.</string>
<string name="please_send_report">Per favore aiutaci a migliorare Briar mandandoci un report del crash.</string>
<string name="report_is_encrypted">Vi promettiamo che il rapporto è cifrato e inviato in modo sicuro.</string>
<string name="feedback_title">Feedback</string>
<string name="describe_crash">Descrivi cosa è successo (facoltativo)</string>
<string name="enter_feedback">Immetti il tuo feedback</string>
<string name="optional_contact_email">Il tuo indirizzo email (facoltativo)</string>
<string name="include_debug_report_crash">Includere dati anonimi riguardo al crash</string>
<string name="include_debug_report_feedback">Includere dati anonimi riguardo al tuo dispositivo</string>
<string name="could_not_load_report_data">Non è stato possibile caricare i dati del report.</string>
<string name="send_report">Invia report</string>
<string name="close">Chiudi</string>
<string name="dev_report_saved">Report salvato. Verrà spedito la prossima volta che loggherai in Briar.</string>
<!--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

@@ -12,7 +12,7 @@
<string name="enter_password">Masukkan kata laluan anda:</string>
<string name="startup_failed_notification_title">Briar gagal dimulakan</string>
<string name="startup_failed_notification_text">Anda perlu pasang ulang Briar.</string>
<string name="expiry_date_reached">Aplikasi ini telah tamat tempoh.\nSila pasang ulang versi baru.</string>
<string name="expiry_warning">Aplikasi ini telah tamat tempoh.\nSila pasang ulang versi baru.</string>
<!--Navigation Drawer-->
<string name="contact_list_button">Kenalan</string>
<string name="forums_button">Forum</string>

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_date_reached">Este software expirou.\nObrigado por testar!</string>
<string name="expiry_warning">Esse programa expirou.\nPor favor instale uma versão mais recente.</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,7 +117,8 @@
<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_message_text">Você pode escrever uma mensagem que será enviada para %1$s e %2$s junto com sua apresentação:</string>
<string name="introduction_message_hint">Digitar 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>
@@ -148,7 +149,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_create_group_hint">Adicionar um nome para 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>
@@ -180,7 +181,6 @@
<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>
@@ -190,8 +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_title">Novo fórum</string>
<string name="choose_forum_name">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>
@@ -215,7 +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_message">Você pode escrever um convite que será enviado aos contatos selecionados.</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>
@@ -230,7 +230,6 @@
<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>
@@ -247,11 +246,12 @@
<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_comment_hint">Adicione um comentario (opcional)</string>
<string name="blogs_reblog_button">Reblog</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Compartilhar Blog</string>
@@ -262,12 +262,11 @@
<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 \"%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 de %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>
@@ -277,9 +276,6 @@
<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>
@@ -288,10 +284,6 @@
<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>
@@ -316,17 +308,11 @@
<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_private_messages_setting">Mostrar alertas para mensagens privadas</string>
<string name="notify_group_messages_setting">Mostrar alertas para mensagens nos Grupos </string>
<string name="notify_forum_posts_setting">Mostrar alertas para postagens nos fórums</string>
<string name="notify_blog_posts_setting">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>
@@ -358,5 +344,4 @@
<!--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

@@ -1,153 +0,0 @@
<?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 хранится зашифрованным в вашем устройстве, а не в облачном сервисе, поэтому мы не можем восстановить ваш пароль. Хотите ли вы удалить ваш аккаунт и начать заново? Осторожно: Ваши личности, контакты и сообщения будут навсегда потеряны.</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>
<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>
<!--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">Видимо, вы впервые здесь и пока что не имеете контактов.
Нажмите на значок + наверху и следуйте инструкциям, чтобы добавить несколько друзей в ваш список.
Пожалуйста, помните: вы можете добавлять новые контакты только лично, лицо к лицу, чтобы в будущем не позволить никому выдать себя за вас или читать ваши сообщения.</string>
<string name="date_no_private_messages">Сообщений нет.</string>
<string name="message_hint">Печатает сообщение</string>
<string name="delete_contact">Удалить контакт</string>
<string name="dialog_title_delete_contact">Подтвердите удаление контакта</string>
<string name="dialog_message_delete_contact">Вы уверены, что хотите удалить этот контакт и все связанные с ним сообщения?</string>
<string name="contact_deleted_toast">Контакт удален</string>
<!--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>
<!--Private Groups-->
<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>
<!--Private Groups Revealing Contacts-->
<!--Forums-->
<!--Forum Sharing-->
<!--Blogs-->
<!--Blog Sharing-->
<!--RSS Feeds-->
<!--Settings Network-->
<!--Settings Security and Panic-->
<!--Settings Notifications-->
<!--Settings Feedback-->
<!--Link Warning-->
<!--Crash Reporter-->
<!--Sign Out-->
<!--Screen Filters & Tapjacking-->
</resources>

View File

@@ -22,6 +22,7 @@
<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>
@@ -116,6 +117,8 @@
<string name="introduction_onboarding_text">Kontaktet tuaja mund tia prezantoni njëri-tjetrit, që kështu të mos u duhet të takohen realisht për të mundur të lidhen në Briar.</string>
<string name="introduction_activity_title">Përzgjidhni Kontakt</string>
<string name="introduction_message_title">Prezantoni Kontakte</string>
<string name="introduction_message_text">Mund të hartoni një mesazh për tu dërguar te %1$s dhe %2$s tok me prezantimin tuaj:</string>
<string name="introduction_message_hint">Shtypni mesazh (në daçi)</string>
<string name="introduction_button">Bëje prezantimin</string>
<string name="introduction_sent">Prezantimi juaj u dërgua.</string>
<string name="introduction_error">Pati një gabim me bërjen e prezantimit.</string>
@@ -146,6 +149,7 @@
<string name="groups_create_group_title">Krijoni Grup Privat</string>
<string name="groups_create_group_button">Krijoje Grupin</string>
<string name="groups_create_group_invitation_button">Dërgoje Ftesën</string>
<string name="groups_create_group_hint">Shtoni një emër për grupin tuaj privat</string>
<string name="groups_invitation_sent">Ftesa e grupit u dërgua</string>
<string name="groups_message_sent">Mesazhi u dërgua</string>
<string name="groups_member_list">Listë Anëtarësh</string>
@@ -187,6 +191,8 @@
<string name="groups_reveal_invisible">Marrëdhënia e kontaktit sështë e dukshme për grupin</string>
<!--Forums-->
<string name="no_forums">Ende skeni forume.\n\nPse nuk krijoni vetë një të tillë duke prekur ikonën + në krye?\n\nMund edhe tu kërkoni kontakteve tuaj të ndajnë me ju forume.</string>
<string name="create_forum_title">Forum i Ri</string>
<string name="choose_forum_name">Zgjidhni një emër për forumin tuaj:</string>
<string name="create_forum_button">Krijoje Forumin</string>
<string name="forum_created_toast">Forumi u krijua</string>
<string name="no_forum_posts">Ky forum është i zbrazët.\n\nPërdorni ikonën penë që të hartoni postimin tuaj të parë.\n\nNdiheni vetëm? Ndajeni këtë forum me kontaktet tuaj!</string>
@@ -210,6 +216,7 @@
<string name="activity_share_toolbar_header">Zgjidhni Kontakte</string>
<string name="no_contacts_selector">Duket se jeni i ri këtu dhe skeni ende kontakte.\n\nJu lutemi, rikthehuni këtu pasi të keni shtuar kontaktin tuaj të parë.</string>
<string name="forum_shared_snackbar">Forumi u nda me kontaktet e zgjedhur</string>
<string name="forum_share_message">Mundeni të hartoni një mesazh ftese (në daçi) që do tu dërgohet kontakteve të përzgjedhur.</string>
<string name="forum_share_error">Pati një gabim në ndarjen e këtij forumi me të tjerët.</string>
<string name="forum_invitation_received">%1$s ndau me ju forumin \"%2$s\".</string>
<string name="forum_invitation_sent">Ndatë me \"%2$s\" forumin %1$s.</string>
@@ -241,10 +248,12 @@
<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>
<string name="blogs_blog_removed">Blogu u Hoq</string>
<string name="blogs_reblog_comment_hint">Shtoni një koment opsional</string>
<string name="blogs_reblog_button">Riblogojeni</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Ndajeni Blogun Me të Tjerë</string>
@@ -255,6 +264,8 @@
<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>
@@ -307,6 +318,10 @@
<string name="uninstall_setting_summary">Kjo lyp ripohim dorazi, në rast akti paniku</string>
<!--Settings Notifications-->
<string name="notification_settings_title">Njoftime</string>
<string name="notify_private_messages_setting">Shfaq sinjalizime për mesazhe private</string>
<string name="notify_group_messages_setting">Shfaq sinjalizime për mesazhe grupi</string>
<string name="notify_forum_posts_setting">Shfaq sinjalizime për postime forumi</string>
<string name="notify_blog_posts_setting">Shfaq sinjalizime për postime blogu</string>
<string name="notify_vibration_setting">Dridhu</string>
<string name="notify_sound_setting">Tingull</string>
<string name="notify_sound_setting_default">Zilja parazgjedhje</string>
@@ -339,4 +354,8 @@
<!--Sign Out-->
<string name="progress_title_logout">Po dilet nga Briar-i…</string>
<!--Screen Filters & Tapjacking-->
<string name="screen_filter_title">U pikas filtër ekrani</string>
<string name="screen_filter_body">Aplikacionet vijuese kanë leje të vizatojnë përmbi aplikacione të tjera:\n\n%1$s \n\nBriar-i nuk do të reagojë ndaj prekjesh, kur mbi të po vizaton një tjetër aplikacion.
Nëse hasni probleme, provoni ti mbyllni këto aplikacione, kur përdorni Briar-in.\n</string>
<string name="checkbox_dont_show_again">Mos më sinjalizo më për këto aplikacione</string>
</resources>

View File

@@ -1,342 +0,0 @@
<?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>

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