Compare commits

...

31 Commits

Author SHA1 Message Date
akwizgran
e83d8bb700 Bump version numbers for 1.0.3 release. 2018-05-14 21:52:16 +01:00
akwizgran
d1ce0d0628 Merge branch '1215-low-memory-hide-ui' into 'master'
Clear the UI when memory is critically low

See merge request akwizgran/briar!786
2018-05-14 14:53:04 +00:00
akwizgran
d73ec3cd88 Merge branch 'disable-expiry' into 'master'
Disable expiry for release builds

See merge request akwizgran/briar!797
2018-05-14 14:51:40 +00:00
akwizgran
71c66c843b Merge branch '1219-commit-shared-prefs' into 'master'
Commit shared preferences, clear instead of deleting

See merge request akwizgran/briar!794
2018-05-14 14:18:49 +00:00
akwizgran
bd19272099 Throw exception if account exists when beginning setup. 2018-05-14 14:20:13 +01:00
akwizgran
b77b885a94 Commit shared preferences, clear instead of deleting. 2018-05-14 14:20:12 +01:00
akwizgran
1fc4f657c7 Merge branch '1219-account-exists' into 'master'
Add logging to debug account creation and deletion

See merge request akwizgran/briar!793
2018-05-14 13:19:20 +00:00
akwizgran
df7d48d54d Fix test expectations. 2018-05-14 12:35:03 +01:00
akwizgran
1987dcb936 Make field that's used on background thread volatile. 2018-05-14 12:34:32 +01:00
akwizgran
f3b69a26f8 Remove unused exception declarations. 2018-05-14 12:31:48 +01:00
akwizgran
5e0ca10dae Add logging to debug account setup. 2018-05-14 12:31:46 +01:00
akwizgran
685496fb15 Extract DatabaseConfig implementation. 2018-05-14 12:30:57 +01:00
akwizgran
1521cdd258 Move expiry date to TestingConstants. 2018-05-14 12:24:37 +01:00
akwizgran
80561910b1 Disable expiry for release builds. 2018-05-14 12:03:30 +01:00
akwizgran
bffb5c94ed Merge branch '1229-setup-crash' into 'master'
Store nickname and password across screen rotations

Closes #1229

See merge request akwizgran/briar!796
2018-05-14 09:57:12 +00:00
akwizgran
c19f7c27b1 Merge branch 'stream-writer-interface' into 'master'
Send end of stream marker when sync session finishes

See merge request akwizgran/briar!790
2018-05-11 10:55:32 +00:00
akwizgran
9a5a1489ef Remove a redundant method. 2018-05-11 11:41:49 +01:00
akwizgran
648793e092 Add javadoc. 2018-05-11 11:36:49 +01:00
akwizgran
e10742a23d Store nickname and password across screen rotations. 2018-05-11 11:36:04 +01:00
akwizgran
32ada51831 Log transport ID with number of connected contacts. 2018-05-10 12:31:54 +01:00
akwizgran
7734a62c3e Interrupt outgoing session when incoming session ends. 2018-05-10 12:29:45 +01:00
akwizgran
e516c329a1 Bump version numbers for 1.0.2 release. 2018-05-09 16:59:09 +01:00
Torsten Grote
b839041d5a Update translations 2018-05-09 09:56:15 -03:00
Torsten Grote
65de8707b7 Merge branch '1225-improve-setup-ux' into 'master'
Remove circle, make button flat to improve setup UX

Closes #1225

See merge request akwizgran/briar!792
2018-05-09 10:41:17 +00:00
akwizgran
dc5bd39ce4 Remove circle, make button flat to improve setup UX. 2018-05-09 10:50:23 +01:00
akwizgran
3c4513b9c7 Convert test to BrambleMockTestCase. 2018-05-08 15:02:07 +01:00
akwizgran
5320737d49 Send end of stream marker when sync session finishes. 2018-05-08 14:41:53 +01:00
akwizgran
0ad9415850 Merge branch 'fix-javadoc' into 'master'
Fix random javadoc errors

See merge request akwizgran/briar!789
2018-05-08 12:56:25 +00:00
goapunk
6f1fba44b6 Fix random javadoc errors 2018-05-08 14:42:14 +02:00
akwizgran
ed53544226 Clear the UI in onLowMemory() if SDK_INT < 16. 2018-05-04 12:18:52 +01:00
akwizgran
6da45a4585 Clear the UI when memory is critically low. 2018-05-04 12:04:13 +01:00
53 changed files with 660 additions and 283 deletions

View File

@@ -14,8 +14,8 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 26
versionCode 10001
versionName "1.0.1"
versionCode 10003
versionName "1.0.3"
consumerProguardFiles 'proguard-rules.txt'
}

View File

@@ -1,20 +1,29 @@
package org.briarproject.bramble.util;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.provider.Settings;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Scanner;
import java.util.logging.Logger;
import static android.content.Context.MODE_PRIVATE;
import static java.util.logging.Level.INFO;
public class AndroidUtils {
private static final Logger LOG =
Logger.getLogger(AndroidUtils.class.getName());
// Fake Bluetooth address returned by BluetoothAdapter on API 23 and later
private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00";
@@ -35,6 +44,7 @@ public class AndroidUtils {
public static String getBluetoothAddress(Context ctx,
BluetoothAdapter adapter) {
// Return the adapter's address if it's valid and not fake
@SuppressLint("HardwareIds")
String address = adapter.getAddress();
if (isValidBluetoothAddress(address)) return address;
// Return the address from settings if it's valid and not fake
@@ -51,17 +61,77 @@ public class AndroidUtils {
&& !address.equals(FAKE_BLUETOOTH_ADDRESS);
}
public static void deleteAppData(Context ctx) {
public static void logDataDirContents(Context ctx) {
if (LOG.isLoggable(INFO)) {
LOG.info("Contents of data directory:");
logFileOrDir(new File(ctx.getApplicationInfo().dataDir));
}
}
private static void logFileOrDir(File f) {
LOG.info(f.getAbsolutePath() + " " + f.length());
if (f.isDirectory()) {
File[] children = f.listFiles();
if (children == null) {
LOG.info("Could not list files in " + f.getAbsolutePath());
} else {
for (File child : children) logFileOrDir(child);
}
}
}
public static File getSharedPrefsFile(Context ctx, String name) {
File dataDir = new File(ctx.getApplicationInfo().dataDir);
File prefsDir = new File(dataDir, "shared_prefs");
return new File(prefsDir, name + ".xml");
}
public static void logFileContents(File f) {
if (LOG.isLoggable(INFO)) {
LOG.info("Contents of " + f.getAbsolutePath() + ":");
try {
Scanner s = new Scanner(f);
while (s.hasNextLine()) LOG.info(s.nextLine());
s.close();
} catch (FileNotFoundException e) {
LOG.info(f.getAbsolutePath() + " not found");
}
}
}
@SuppressLint("ApplySharedPref")
public static void deleteAppData(Context ctx, SharedPreferences... clear) {
// Clear and commit shared preferences
for (SharedPreferences prefs : clear) {
boolean cleared = prefs.edit().clear().commit();
if (LOG.isLoggable(INFO)) {
if (cleared) LOG.info("Cleared shared preferences");
else LOG.info("Could not clear shared preferences");
}
}
// Delete files, except lib and shared_prefs directories
File dataDir = new File(ctx.getApplicationInfo().dataDir);
if (LOG.isLoggable(INFO))
LOG.info("Deleting app data from " + dataDir.getAbsolutePath());
File[] children = dataDir.listFiles();
if (children != null) {
for (File child : children) {
if (!child.getName().equals("lib"))
String name = child.getName();
if (!name.equals("lib") && !name.equals("shared_prefs")) {
if (LOG.isLoggable(INFO))
LOG.info("Deleting " + child.getAbsolutePath());
IoUtils.deleteFileOrDir(child);
}
}
} else if (LOG.isLoggable(INFO)) {
LOG.info("Could not list files in " + dataDir.getAbsolutePath());
}
// Recreate the cache dir as some OpenGL drivers expect it to exist
new File(dataDir, "cache").mkdir();
boolean recreated = new File(dataDir, "cache").mkdir();
if (LOG.isLoggable(INFO)) {
if (recreated) LOG.info("Recreated cache dir");
else LOG.info("Could not recreate cache dir");
}
}
public static File getReportDir(Context ctx) {

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* A key pair consisting of a {@link PublicKey} and a {@link PrivateKey).
* A key pair consisting of a {@link PublicKey} and a {@link PrivateKey}.
*/
@Immutable
@NotNullByDefault

View File

@@ -2,9 +2,9 @@ package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.InputStream;
import java.io.OutputStream;
@NotNullByDefault
public interface SyncSessionFactory {
@@ -12,8 +12,8 @@ public interface SyncSessionFactory {
SyncSession createIncomingSession(ContactId c, InputStream in);
SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency,
OutputStream out);
StreamWriter streamWriter);
SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
int maxIdleTime, OutputStream out);
int maxIdleTime, StreamWriter streamWriter);
}

View File

@@ -7,12 +7,12 @@ package org.briarproject.bramble.api.system;
public interface Clock {
/**
* @see {@link System#currentTimeMillis()}
* @see System#currentTimeMillis()
*/
long currentTimeMillis();
/**
* @see {@link Thread#sleep(long)}
* @see Thread#sleep(long)
*/
void sleep(long milliseconds) throws InterruptedException;
}

View File

@@ -0,0 +1,19 @@
package org.briarproject.bramble.api.transport;
import java.io.IOException;
import java.io.OutputStream;
/**
* An interface for writing data to a transport connection. Data will be
* encrypted and authenticated before being written to the connection.
*/
public interface StreamWriter {
OutputStream getOutputStream();
/**
* Sends the end of stream marker, informing the recipient that no more
* data will be sent. The connection is flushed but not closed.
*/
void sendEndOfStream() throws IOException;
}

View File

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

View File

@@ -9,25 +9,37 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.INFO;
@NotNullByDefault
public class IoUtils {
private static final Logger LOG = Logger.getLogger(IoUtils.class.getName());
public static void deleteFileOrDir(File f) {
if (f.isFile()) {
f.delete();
delete(f);
} else if (f.isDirectory()) {
File[] children = f.listFiles();
if (children != null)
for (File child : children) deleteFileOrDir(child);
f.delete();
delete(f);
}
}
public static void copyAndClose(InputStream in, OutputStream out)
throws IOException {
private static void delete(File f) {
boolean deleted = f.delete();
if (LOG.isLoggable(INFO)) {
if (deleted) LOG.info("Deleted " + f.getAbsolutePath());
else LOG.info("Could not delete " + f.getAbsolutePath());
}
}
public static void copyAndClose(InputStream in, OutputStream out) {
byte[] buf = new byte[4096];
try {
while (true) {

View File

@@ -30,6 +30,7 @@ import org.briarproject.bramble.api.record.RecordWriter;
import org.briarproject.bramble.api.record.RecordWriterFactory;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.EOFException;
@@ -152,11 +153,11 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
recordReaderFactory.createRecordReader(streamReader);
// Create the writers
OutputStream streamWriter =
StreamWriter streamWriter =
streamWriterFactory.createContactExchangeStreamWriter(out,
alice ? aliceHeaderKey : bobHeaderKey);
RecordWriter recordWriter =
recordWriterFactory.createRecordWriter(streamWriter);
recordWriterFactory.createRecordWriter(streamWriter.getOutputStream());
// Derive the nonces to be signed
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret,
@@ -184,8 +185,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
localSignature, localTimestamp);
recordWriter.flush();
}
// Close the outgoing stream
recordWriter.close();
// Send EOF on the outgoing stream
streamWriter.sendEndOfStream();
// Skip any remaining records from the incoming stream
try {
while (true) recordReader.readRecord();

View File

@@ -10,37 +10,37 @@ import java.security.GeneralSecurityException;
interface Signature {
/**
* @see {@link java.security.Signature#initSign(java.security.PrivateKey)}
* @see java.security.Signature#initSign(java.security.PrivateKey)
*/
void initSign(PrivateKey k) throws GeneralSecurityException;
/**
* @see {@link java.security.Signature#initVerify(java.security.PublicKey)}
* @see java.security.Signature#initVerify(java.security.PublicKey)
*/
void initVerify(PublicKey k) throws GeneralSecurityException;
/**
* @see {@link java.security.Signature#update(byte)}
* @see java.security.Signature#update(byte)
*/
void update(byte b) throws GeneralSecurityException;
/**
* @see {@link java.security.Signature#update(byte[])}
* @see java.security.Signature#update(byte[])
*/
void update(byte[] b) throws GeneralSecurityException;
/**
* @see {@link java.security.Signature#update(byte[], int, int)}
* @see java.security.Signature#update(byte[], int, int)
*/
void update(byte[] b, int off, int len) throws GeneralSecurityException;
/**
* @see {@link java.security.Signature#sign()}
* @see java.security.Signature#sign()}
*/
byte[] sign() throws GeneralSecurityException;
/**
* @see {@link java.security.Signature#verify(byte[])}
* @see java.security.Signature#verify(byte[])
*/
boolean verify(byte[] signature) throws GeneralSecurityException;
}

View File

@@ -34,8 +34,8 @@ import javax.annotation.Nullable;
* A low-level interface to the database (DatabaseComponent provides a
* high-level interface). Most operations take a transaction argument, which is
* obtained by calling {@link #startTransaction()}. Every transaction must be
* terminated by calling either {@link #abortTransaction(T)} or
* {@link #commitTransaction(T)}, even if an exception is thrown.
* terminated by calling either {@link #abortTransaction(Object) abortTransaction(T)} or
* {@link #commitTransaction(Object) commitTransaction(T)}, even if an exception is thrown.
*/
@NotNullByDefault
interface Database<T> {

View File

@@ -14,12 +14,12 @@ import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -101,7 +101,7 @@ class ConnectionManagerImpl implements ConnectionManager {
private SyncSession createSimplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w) throws IOException {
OutputStream streamWriter = streamWriterFactory.createStreamWriter(
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx);
return syncSessionFactory.createSimplexOutgoingSession(
ctx.getContactId(), w.getMaxLatency(), streamWriter);
@@ -109,7 +109,7 @@ class ConnectionManagerImpl implements ConnectionManager {
private SyncSession createDuplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w) throws IOException {
OutputStream streamWriter = streamWriterFactory.createStreamWriter(
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx);
return syncSessionFactory.createDuplexOutgoingSession(
ctx.getContactId(), w.getMaxLatency(), w.getMaxIdleTime(),
@@ -300,8 +300,8 @@ class ConnectionManagerImpl implements ConnectionManager {
}
private void disposeReader(boolean exception, boolean recognised) {
if (exception && outgoingSession != null)
outgoingSession.interrupt();
// Interrupt the outgoing session so it finishes cleanly
if (outgoingSession != null) outgoingSession.interrupt();
try {
reader.dispose(exception, recognised);
} catch (IOException e) {
@@ -310,6 +310,8 @@ class ConnectionManagerImpl implements ConnectionManager {
}
private void disposeWriter(boolean exception) {
// Interrupt the incoming session if an exception occurred,
// otherwise wait for the end of stream marker
if (exception && incomingSession != null)
incomingSession.interrupt();
try {
@@ -407,8 +409,8 @@ class ConnectionManagerImpl implements ConnectionManager {
}
private void disposeReader(boolean exception, boolean recognised) {
if (exception && outgoingSession != null)
outgoingSession.interrupt();
// Interrupt the outgoing session so it finishes cleanly
if (outgoingSession != null) outgoingSession.interrupt();
try {
reader.dispose(exception, recognised);
} catch (IOException e) {
@@ -417,6 +419,8 @@ class ConnectionManagerImpl implements ConnectionManager {
}
private void disposeWriter(boolean exception) {
// Interrupt the incoming session if an exception occurred,
// otherwise wait for the end of stream marker
if (exception && incomingSession != null)
incomingSession.interrupt();
try {

View File

@@ -106,7 +106,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
if (m == null) return Collections.emptyList();
List<ContactId> ids = new ArrayList<>(m.keySet());
if (LOG.isLoggable(INFO))
LOG.info(ids.size() + " contacts connected");
LOG.info(ids.size() + " contacts connected: " + t);
return ids;
} finally {
lock.unlock();

View File

@@ -23,6 +23,7 @@ import org.briarproject.bramble.api.sync.event.MessageSharedEvent;
import org.briarproject.bramble.api.sync.event.MessageToAckEvent;
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.IOException;
import java.util.Collection;
@@ -67,6 +68,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
private final Clock clock;
private final ContactId contactId;
private final int maxLatency, maxIdleTime;
private final StreamWriter streamWriter;
private final SyncRecordWriter recordWriter;
private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
@@ -81,7 +83,8 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
DuplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
EventBus eventBus, Clock clock, ContactId contactId, int maxLatency,
int maxIdleTime, SyncRecordWriter recordWriter) {
int maxIdleTime, StreamWriter streamWriter,
SyncRecordWriter recordWriter) {
this.db = db;
this.dbExecutor = dbExecutor;
this.eventBus = eventBus;
@@ -89,6 +92,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
this.contactId = contactId;
this.maxLatency = maxLatency;
this.maxIdleTime = maxIdleTime;
this.streamWriter = streamWriter;
this.recordWriter = recordWriter;
writerTasks = new LinkedBlockingQueue<>();
}
@@ -149,7 +153,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
dataToFlush = true;
}
}
if (dataToFlush) recordWriter.flush();
streamWriter.sendEndOfStream();
} catch (InterruptedException e) {
LOG.info("Interrupted while waiting for a record to write");
Thread.currentThread().interrupt();

View File

@@ -63,7 +63,11 @@ class IncomingSession implements SyncSession, EventListener {
eventBus.addListener(this);
try {
// Read records until interrupted or EOF
while (!interrupted && !recordReader.eof()) {
while (!interrupted) {
if (recordReader.eof()) {
LOG.info("End of stream");
return;
}
if (recordReader.hasAck()) {
Ack a = recordReader.readAck();
dbExecutor.execute(new ReceiveAck(a));

View File

@@ -15,6 +15,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Ack;
import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.IOException;
import java.util.Collection;
@@ -51,6 +52,7 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
private final EventBus eventBus;
private final ContactId contactId;
private final int maxLatency;
private final StreamWriter streamWriter;
private final SyncRecordWriter recordWriter;
private final AtomicInteger outstandingQueries;
private final BlockingQueue<ThrowingRunnable<IOException>> writerTasks;
@@ -58,13 +60,14 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
private volatile boolean interrupted = false;
SimplexOutgoingSession(DatabaseComponent db, Executor dbExecutor,
EventBus eventBus, ContactId contactId,
int maxLatency, SyncRecordWriter recordWriter) {
EventBus eventBus, ContactId contactId, int maxLatency,
StreamWriter streamWriter, SyncRecordWriter recordWriter) {
this.db = db;
this.dbExecutor = dbExecutor;
this.eventBus = eventBus;
this.contactId = contactId;
this.maxLatency = maxLatency;
this.streamWriter = streamWriter;
this.recordWriter = recordWriter;
outstandingQueries = new AtomicInteger(2); // One per type of record
writerTasks = new LinkedBlockingQueue<>();
@@ -85,7 +88,7 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
if (task == CLOSE) break;
task.run();
}
recordWriter.flush();
streamWriter.sendEndOfStream();
} catch (InterruptedException e) {
LOG.info("Interrupted while waiting for a record to write");
Thread.currentThread().interrupt();

View File

@@ -12,6 +12,7 @@ import org.briarproject.bramble.api.sync.SyncRecordWriterFactory;
import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.InputStream;
import java.io.OutputStream;
@@ -53,19 +54,21 @@ class SyncSessionFactoryImpl implements SyncSessionFactory {
@Override
public SyncSession createSimplexOutgoingSession(ContactId c,
int maxLatency, OutputStream out) {
int maxLatency, StreamWriter streamWriter) {
OutputStream out = streamWriter.getOutputStream();
SyncRecordWriter recordWriter =
recordWriterFactory.createRecordWriter(out);
return new SimplexOutgoingSession(db, dbExecutor, eventBus, c,
maxLatency, recordWriter);
maxLatency, streamWriter, recordWriter);
}
@Override
public SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
int maxIdleTime, OutputStream out) {
int maxIdleTime, StreamWriter streamWriter) {
OutputStream out = streamWriter.getOutputStream();
SyncRecordWriter recordWriter =
recordWriterFactory.createRecordWriter(out);
return new DuplexOutgoingSession(db, dbExecutor, eventBus, clock, c,
maxLatency, maxIdleTime, recordWriter);
maxLatency, maxIdleTime, streamWriter, recordWriter);
}
}

View File

@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.OutputStream;
@@ -23,14 +24,14 @@ class StreamWriterFactoryImpl implements StreamWriterFactory {
}
@Override
public OutputStream createStreamWriter(OutputStream out,
public StreamWriter createStreamWriter(OutputStream out,
StreamContext ctx) {
return new StreamWriterImpl(
streamEncrypterFactory.createStreamEncrypter(out, ctx));
}
@Override
public OutputStream createContactExchangeStreamWriter(OutputStream out,
public StreamWriter createContactExchangeStreamWriter(OutputStream out,
SecretKey headerKey) {
return new StreamWriterImpl(
streamEncrypterFactory.createContactExchangeStreamDecrypter(out,

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.crypto.StreamEncrypter;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.IOException;
import java.io.OutputStream;
@@ -17,7 +18,7 @@ import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYL
*/
@NotThreadSafe
@NotNullByDefault
class StreamWriterImpl extends OutputStream {
class StreamWriterImpl extends OutputStream implements StreamWriter {
private final StreamEncrypter encrypter;
private final byte[] payload;
@@ -29,6 +30,17 @@ class StreamWriterImpl extends OutputStream {
payload = new byte[MAX_PAYLOAD_LENGTH];
}
@Override
public OutputStream getOutputStream() {
return this;
}
@Override
public void sendEndOfStream() throws IOException {
writeFrame(true);
encrypter.flush();
}
@Override
public void close() throws IOException {
writeFrame(true);

View File

@@ -7,11 +7,10 @@ import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.sync.Ack;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.bramble.test.TestUtils;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import java.util.Arrays;
@@ -19,33 +18,27 @@ import java.util.Collections;
import java.util.concurrent.Executor;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
public class SimplexOutgoingSessionTest extends BrambleTestCase {
public class SimplexOutgoingSessionTest extends BrambleMockTestCase {
private final Mockery context;
private final DatabaseComponent db;
private final Executor dbExecutor;
private final EventBus eventBus;
private final ContactId contactId;
private final MessageId messageId;
private final int maxLatency;
private final SyncRecordWriter recordWriter;
private static final int MAX_LATENCY = Integer.MAX_VALUE;
public SimplexOutgoingSessionTest() {
context = new Mockery();
db = context.mock(DatabaseComponent.class);
dbExecutor = new ImmediateExecutor();
eventBus = context.mock(EventBus.class);
recordWriter = context.mock(SyncRecordWriter.class);
contactId = new ContactId(234);
messageId = new MessageId(TestUtils.getRandomId());
maxLatency = Integer.MAX_VALUE;
}
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final EventBus eventBus = context.mock(EventBus.class);
private final StreamWriter streamWriter = context.mock(StreamWriter.class);
private final SyncRecordWriter recordWriter =
context.mock(SyncRecordWriter.class);
private final Executor dbExecutor = new ImmediateExecutor();
private final ContactId contactId = new ContactId(234);
private final MessageId messageId = new MessageId(getRandomId());
@Test
public void testNothingToSend() throws Exception {
SimplexOutgoingSession session = new SimplexOutgoingSession(db,
dbExecutor, eventBus, contactId, maxLatency, recordWriter);
dbExecutor, eventBus, contactId, MAX_LATENCY, streamWriter,
recordWriter);
Transaction noAckTxn = new Transaction(null, false);
Transaction noMsgTxn = new Transaction(null, false);
@@ -63,19 +56,17 @@ public class SimplexOutgoingSessionTest extends BrambleTestCase {
oneOf(db).startTransaction(false);
will(returnValue(noMsgTxn));
oneOf(db).generateBatch(with(noMsgTxn), with(contactId),
with(any(int.class)), with(maxLatency));
with(any(int.class)), with(MAX_LATENCY));
will(returnValue(null));
oneOf(db).commitTransaction(noMsgTxn);
oneOf(db).endTransaction(noMsgTxn);
// Flush the output stream
oneOf(recordWriter).flush();
// Send the end of stream marker
oneOf(streamWriter).sendEndOfStream();
// Remove listener
oneOf(eventBus).removeListener(session);
}});
session.run();
context.assertIsSatisfied();
}
@Test
@@ -83,7 +74,8 @@ public class SimplexOutgoingSessionTest extends BrambleTestCase {
Ack ack = new Ack(Collections.singletonList(messageId));
byte[] raw = new byte[1234];
SimplexOutgoingSession session = new SimplexOutgoingSession(db,
dbExecutor, eventBus, contactId, maxLatency, recordWriter);
dbExecutor, eventBus, contactId, MAX_LATENCY, streamWriter,
recordWriter);
Transaction ackTxn = new Transaction(null, false);
Transaction noAckTxn = new Transaction(null, false);
Transaction msgTxn = new Transaction(null, false);
@@ -104,7 +96,7 @@ public class SimplexOutgoingSessionTest extends BrambleTestCase {
oneOf(db).startTransaction(false);
will(returnValue(msgTxn));
oneOf(db).generateBatch(with(msgTxn), with(contactId),
with(any(int.class)), with(maxLatency));
with(any(int.class)), with(MAX_LATENCY));
will(returnValue(Arrays.asList(raw)));
oneOf(db).commitTransaction(msgTxn);
oneOf(db).endTransaction(msgTxn);
@@ -120,18 +112,16 @@ public class SimplexOutgoingSessionTest extends BrambleTestCase {
oneOf(db).startTransaction(false);
will(returnValue(noMsgTxn));
oneOf(db).generateBatch(with(noMsgTxn), with(contactId),
with(any(int.class)), with(maxLatency));
with(any(int.class)), with(MAX_LATENCY));
will(returnValue(null));
oneOf(db).commitTransaction(noMsgTxn);
oneOf(db).endTransaction(noMsgTxn);
// Flush the output stream
oneOf(recordWriter).flush();
// Send the end of stream marker
oneOf(streamWriter).sendEndOfStream();
// Remove listener
oneOf(eventBus).removeListener(session);
}});
session.run();
context.assertIsSatisfied();
}
}

View File

@@ -19,6 +19,7 @@ import org.briarproject.bramble.api.sync.SyncRecordWriter;
import org.briarproject.bramble.api.sync.SyncRecordWriterFactory;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
@@ -27,7 +28,6 @@ import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collection;
@@ -102,18 +102,18 @@ public class SyncIntegrationTest extends BrambleTestCase {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamContext ctx = new StreamContext(contactId, transportId, tagKey,
headerKey, streamNumber);
OutputStream streamWriter = streamWriterFactory.createStreamWriter(out,
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(out,
ctx);
SyncRecordWriter recordWriter = recordWriterFactory.createRecordWriter(
streamWriter);
streamWriter.getOutputStream());
recordWriter.writeAck(new Ack(messageIds));
recordWriter.writeMessage(message.getRaw());
recordWriter.writeMessage(message1.getRaw());
recordWriter.writeOffer(new Offer(messageIds));
recordWriter.writeRequest(new Request(messageIds));
recordWriter.flush();
streamWriter.sendEndOfStream();
return out.toByteArray();
}

View File

@@ -238,12 +238,13 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 26
versionCode 10001
versionName "1.0.1"
versionCode 10003
versionName "1.0.3"
applicationId "org.briarproject.briar.android"
resValue "string", "app_package", "org.briarproject.briar.android"
resValue "string", "app_name", "Briar"
buildConfigField "String", "GitHash", "\"${getGitHash()}\""
buildConfigField "Long", "BuildTimestamp", "${System.currentTimeMillis()}L"
}
buildTypes {

View File

@@ -0,0 +1,94 @@
package org.briarproject.briar.android;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.INFO;
@NotNullByDefault
class AndroidDatabaseConfig implements DatabaseConfig {
private static final Logger LOG =
Logger.getLogger(AndroidDatabaseConfig.class.getName());
private final File dir;
@Nullable
private volatile SecretKey key = null;
@Nullable
private volatile String nickname = null;
AndroidDatabaseConfig(File dir) {
this.dir = dir;
}
@Override
public boolean databaseExists() {
// FIXME should not run on UiThread #620
if (!dir.isDirectory()) {
if (LOG.isLoggable(INFO))
LOG.info(dir.getAbsolutePath() + " is not a directory");
return false;
}
File[] files = dir.listFiles();
if (LOG.isLoggable(INFO)) {
if (files == null) {
LOG.info("Could not list files in " + dir.getAbsolutePath());
} else {
LOG.info("Files in " + dir.getAbsolutePath() + ":");
for (File f : files) LOG.info(f.getName());
}
LOG.info("Database exists: " + (files != null && files.length > 0));
}
return files != null && files.length > 0;
}
@Override
public File getDatabaseDirectory() {
File dir = this.dir;
if (LOG.isLoggable(INFO))
LOG.info("Database directory: " + dir.getAbsolutePath());
return dir;
}
@Override
public void setEncryptionKey(SecretKey key) {
LOG.info("Setting database key");
this.key = key;
}
@Override
public void setLocalAuthorName(String nickname) {
LOG.info("Setting local author name");
this.nickname = nickname;
}
@Override
@Nullable
public String getLocalAuthorName() {
String nickname = this.nickname;
if (LOG.isLoggable(INFO))
LOG.info("Local author name has been set: " + (nickname != null));
return nickname;
}
@Override
@Nullable
public SecretKey getEncryptionKey() {
SecretKey key = this.key;
if (LOG.isLoggable(INFO))
LOG.info("Database key has been set: " + (key != null));
return key;
}
@Override
public long getMaxSize() {
return Long.MAX_VALUE;
}
}

View File

@@ -5,7 +5,6 @@ import android.content.SharedPreferences;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
@@ -23,7 +22,6 @@ import org.briarproject.briar.api.android.ScreenFilterMonitor;
import java.io.File;
import java.security.GeneralSecurityException;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -87,51 +85,7 @@ public class AppModule {
File dir = app.getApplicationContext().getDir("db", MODE_PRIVATE);
@MethodsNotNullByDefault
@ParametersNotNullByDefault
DatabaseConfig databaseConfig = new DatabaseConfig() {
private volatile SecretKey key;
private volatile String nickname;
@Override
public boolean databaseExists() {
// FIXME should not run on UiThread #620
if (!dir.isDirectory()) return false;
File[] files = dir.listFiles();
return files != null && files.length > 0;
}
@Override
public File getDatabaseDirectory() {
return dir;
}
@Override
public void setEncryptionKey(SecretKey key) {
this.key = key;
}
@Override
public void setLocalAuthorName(String nickname) {
this.nickname = nickname;
}
@Override
@Nullable
public String getLocalAuthorName() {
return nickname;
}
@Override
@Nullable
public SecretKey getEncryptionKey() {
return key;
}
@Override
public long getMaxSize() {
return Long.MAX_VALUE;
}
};
DatabaseConfig databaseConfig = new AndroidDatabaseConfig(dir);
return databaseConfig;
}
@@ -204,4 +158,5 @@ public class AppModule {
lifecycleManager.registerService(dozeWatchdog);
return dozeWatchdog;
}
}

View File

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

View File

@@ -22,7 +22,6 @@ import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.R;
import org.briarproject.briar.android.logout.HideUiActivity;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.android.splash.SplashScreenActivity;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -44,6 +43,7 @@ import static android.os.Build.VERSION.SDK_INT;
import static android.support.v4.app.NotificationCompat.CATEGORY_SERVICE;
import static android.support.v4.app.NotificationCompat.PRIORITY_MIN;
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
@@ -221,18 +221,52 @@ public class BriarService extends Service {
public void onLowMemory() {
super.onLowMemory();
LOG.warning("Memory is low");
// Clear the UI - this is done in onTrimMemory() if SDK_INT >= 16
if (SDK_INT < 16) hideUi();
}
private void shutdownFromBackground() {
// Stop the service
stopSelf();
// Hide the UI
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (level == TRIM_MEMORY_UI_HIDDEN) {
LOG.info("Trim memory: UI hidden");
} else if (level == TRIM_MEMORY_BACKGROUND) {
LOG.info("Trim memory: added to LRU list");
} else if (level == TRIM_MEMORY_MODERATE) {
LOG.info("Trim memory: near middle of LRU list");
} else if (level == TRIM_MEMORY_COMPLETE) {
LOG.info("Trim memory: near end of LRU list");
} else if (SDK_INT >= 16) {
if (level == TRIM_MEMORY_RUNNING_MODERATE) {
LOG.info("Trim memory: running moderately low");
} else if (level == TRIM_MEMORY_RUNNING_LOW) {
LOG.info("Trim memory: running low");
} else if (level == TRIM_MEMORY_RUNNING_CRITICAL) {
LOG.info("Trim memory: running critically low");
// Clear the UI to save some memory
hideUi();
} else if (LOG.isLoggable(INFO)) {
LOG.info("Trim memory: unknown level " + level);
}
} else if (LOG.isLoggable(INFO)) {
LOG.info("Trim memory: unknown level " + level);
}
}
private void hideUi() {
Intent i = new Intent(this, HideUiActivity.class);
i.addFlags(FLAG_ACTIVITY_NEW_TASK
| FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| FLAG_ACTIVITY_NO_ANIMATION
| FLAG_ACTIVITY_CLEAR_TASK);
startActivity(i);
}
private void shutdownFromBackground() {
// Stop the service
stopSelf();
// Hide the UI
hideUi();
// Wait for shutdown to complete, then exit
new Thread(() -> {
try {
@@ -245,25 +279,6 @@ public class BriarService extends Service {
}).start();
}
// TODO: Remove if low memory shutdowns are not appropriate
private void showLowMemoryShutdownNotification() {
androidExecutor.runOnUiThread(() -> {
NotificationCompat.Builder b = new NotificationCompat.Builder(
BriarService.this, FAILURE_CHANNEL_ID);
b.setSmallIcon(android.R.drawable.stat_notify_error);
b.setContentTitle(getText(
R.string.low_memory_shutdown_notification_title));
b.setContentText(getText(
R.string.low_memory_shutdown_notification_text));
Intent i = new Intent(this, SplashScreenActivity.class);
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
b.setAutoCancel(true);
Object o = getSystemService(NOTIFICATION_SERVICE);
NotificationManager nm = (NotificationManager) o;
nm.notify(FAILURE_NOTIFICATION_ID, b.build());
});
}
/**
* Waits for all services to start before returning.
*/

View File

@@ -14,7 +14,6 @@ import android.content.pm.Signature;
import android.support.annotation.UiThread;
import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.util.StringUtils;
@@ -196,7 +195,7 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor, Service {
}
@Override
public void startService() throws ServiceException {
public void startService() {
if (used.getAndSet(true)) throw new IllegalStateException();
androidExecutor.runOnUiThread(() -> {
IntentFilter filter = new IntentFilter();
@@ -212,7 +211,7 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor, Service {
}
@Override
public void stopService() throws ServiceException {
public void stopService() {
androidExecutor.runOnUiThread(() -> {
if (receiver != null) app.unregisterReceiver(receiver);
});

View File

@@ -33,4 +33,12 @@ public interface TestingConstants {
* intentionally.
*/
boolean PREVENT_SCREENSHOTS = !IS_DEBUG_BUILD;
/**
* Debug and beta builds expire after 90 days. Final release builds expire
* after 292 million years.
*/
long EXPIRY_DATE = IS_DEBUG_BUILD || IS_BETA_BUILD ?
BuildConfig.BuildTimestamp + 90 * 24 * 60 * 60 * 1000L :
Long.MAX_VALUE;
}

View File

@@ -1,18 +1,27 @@
package org.briarproject.briar.android.controller;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.v7.preference.PreferenceManager;
import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.AndroidUtils;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
@NotNullByDefault
public class ConfigControllerImpl implements ConfigController {
private static final Logger LOG =
Logger.getLogger(ConfigControllerImpl.class.getName());
private static final String PREF_DB_KEY = "key";
private final SharedPreferences briarPrefs;
@@ -23,39 +32,46 @@ public class ConfigControllerImpl implements ConfigController {
DatabaseConfig databaseConfig) {
this.briarPrefs = briarPrefs;
this.databaseConfig = databaseConfig;
}
@Override
@Nullable
public String getEncryptedDatabaseKey() {
return briarPrefs.getString(PREF_DB_KEY, null);
String key = briarPrefs.getString(PREF_DB_KEY, null);
if (LOG.isLoggable(INFO))
LOG.info("Got database key from preferences: " + (key != null));
return key;
}
@Override
@SuppressLint("ApplySharedPref")
public void storeEncryptedDatabaseKey(String hex) {
SharedPreferences.Editor editor = briarPrefs.edit();
editor.putString(PREF_DB_KEY, hex);
editor.apply();
LOG.info("Storing database key in preferences");
briarPrefs.edit().putString(PREF_DB_KEY, hex).commit();
}
@Override
public void deleteAccount(Context ctx) {
SharedPreferences.Editor editor = briarPrefs.edit();
editor.clear();
editor.apply();
AndroidUtils.deleteAppData(ctx);
LOG.info("Deleting account");
SharedPreferences defaultPrefs =
PreferenceManager.getDefaultSharedPreferences(ctx);
AndroidUtils.deleteAppData(ctx, briarPrefs, defaultPrefs);
AndroidUtils.logDataDirContents(ctx);
}
@Override
public boolean accountExists() {
String hex = getEncryptedDatabaseKey();
return hex != null && databaseConfig.databaseExists();
boolean exists = hex != null && databaseConfig.databaseExists();
if (LOG.isLoggable(INFO)) LOG.info("Account exists: " + exists);
return exists;
}
@Override
public boolean accountSignedIn() {
return databaseConfig.getEncryptionKey() != null;
boolean signedIn = databaseConfig.getEncryptionKey() != null;
if (LOG.isLoggable(INFO)) LOG.info("Signed in: " + signedIn);
return signedIn;
}
}

View File

@@ -8,15 +8,21 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import javax.annotation.Nullable;
import static android.view.inputmethod.EditorInfo.IME_ACTION_NEXT;
import static android.view.inputmethod.EditorInfo.IME_ACTION_NONE;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.briar.android.util.UiUtils.setError;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class AuthorNameFragment extends SetupFragment {
private final static String TAG = AuthorNameFragment.class.getName();
@@ -30,8 +36,9 @@ public class AuthorNameFragment extends SetupFragment {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
getActivity().setTitle(getString(R.string.setup_title));
View v = inflater.inflate(R.layout.fragment_setup_author_name,
container, false);
@@ -75,6 +82,7 @@ public class AuthorNameFragment extends SetupFragment {
@Override
public void onClick(View view) {
setupController.setAuthorName(authorNameInput.getText().toString());
setupController.showPasswordFragment();
}
}

View File

@@ -10,7 +10,8 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
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.login.PowerView.OnCheckedChangedListener;
@@ -21,7 +22,8 @@ import static android.view.View.VISIBLE;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
@NotNullByDefault
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class DozeFragment extends SetupFragment
implements OnCheckedChangedListener {

View File

@@ -11,15 +11,21 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.ProgressBar;
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.util.UiUtils;
import javax.annotation.Nullable;
import static android.content.Context.INPUT_METHOD_SERVICE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class PasswordFragment extends SetupFragment {
private final static String TAG = PasswordFragment.class.getName();
@@ -37,8 +43,9 @@ public class PasswordFragment extends SetupFragment {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
getActivity().setTitle(getString(R.string.setup_password_intro));
View v = inflater.inflate(R.layout.fragment_setup_password, container,
false);
@@ -105,16 +112,17 @@ public class PasswordFragment extends SetupFragment {
@Override
public void onClick(View view) {
if (!setupController.needToShowDozeFragment()) {
nextButton.setVisibility(INVISIBLE);
progressBar.setVisibility(VISIBLE);
}
String password = passwordEntry.getText().toString();
IBinder token = passwordEntry.getWindowToken();
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
((InputMethodManager) o).hideSoftInputFromWindow(token, 0);
setupController.setPassword(password);
setupController.showDozeOrCreateAccount();
setupController.setPassword(passwordEntry.getText().toString());
if (setupController.needToShowDozeFragment()) {
setupController.showDozeFragment();
} else {
nextButton.setVisibility(INVISIBLE);
progressBar.setVisibility(VISIBLE);
setupController.createAccount();
}
}
}

View File

@@ -89,9 +89,9 @@ abstract class PowerView extends ConstraintLayout {
public void setChecked(boolean checked) {
this.checked = checked;
if (checked) {
checkImage.setImageResource(R.drawable.ic_check_white);
checkImage.setVisibility(VISIBLE);
} else {
checkImage.setImageResource(R.drawable.contact_disconnected);
checkImage.setVisibility(INVISIBLE);
}
if (onCheckedChangedListener != null) {
onCheckedChangedListener.onCheckedChanged();

View File

@@ -4,30 +4,46 @@ import android.annotation.TargetApi;
import android.content.Intent;
import android.os.Bundle;
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.activity.BaseActivity;
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class SetupActivity extends BaseActivity
implements BaseFragmentListener {
private static final String STATE_KEY_AUTHOR_NAME = "authorName";
private static final String STATE_KEY_PASSWORD = "password";
@Inject
SetupController setupController;
@Nullable
private String authorName, password;
@Override
public void onCreate(Bundle state) {
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
// fade-in after splash screen instead of default animation
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
setContentView(R.layout.activity_fragment_container);
if (state == null) {
if (setupController.accountExists())
throw new AssertionError();
showInitialFragment(AuthorNameFragment.newInstance());
} else {
authorName = state.getString(STATE_KEY_AUTHOR_NAME);
password = state.getString(STATE_KEY_PASSWORD);
}
}
@@ -37,16 +53,46 @@ public class SetupActivity extends BaseActivity
setupController.setSetupActivity(this);
}
public void showPasswordFragment() {
@Override
protected void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
if (authorName != null)
state.putString(STATE_KEY_AUTHOR_NAME, authorName);
if (password != null)
state.putString(STATE_KEY_PASSWORD, password);
}
@Nullable
String getAuthorName() {
return authorName;
}
void setAuthorName(String authorName) {
this.authorName = authorName;
}
@Nullable
String getPassword() {
return password;
}
void setPassword(String password) {
this.password = password;
}
void showPasswordFragment() {
if (authorName == null) throw new IllegalStateException();
showNextFragment(PasswordFragment.newInstance());
}
@TargetApi(23)
public void showDozeFragment() {
void showDozeFragment() {
if (authorName == null) throw new IllegalStateException();
if (password == null) throw new IllegalStateException();
showNextFragment(DozeFragment.newInstance());
}
public void showApp() {
void showApp() {
Intent i = new Intent(this, OpenDatabaseActivity.class);
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
startActivity(i);

View File

@@ -1,10 +1,9 @@
package org.briarproject.briar.android.login;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.controller.handler.ResultHandler;
@NotNullByDefault
public interface SetupController {
public interface SetupController extends PasswordController {
void setSetupActivity(SetupActivity setupActivity);
@@ -14,17 +13,20 @@ public interface SetupController {
void setPassword(String password);
float estimatePasswordStrength(String password);
/**
* This should be called after the author name has been set.
*/
void showPasswordFragment();
/**
* This should be called after the author name and the password have been
* set. It decides whether to ask for doze exception or create the account
* right away.
* set.
*/
void showDozeOrCreateAccount();
void showDozeFragment();
/**
* This should be called after the author name and the password have been
* set.
*/
void createAccount();
void createAccount(ResultHandler<Void> resultHandler);
}

View File

@@ -9,10 +9,12 @@ import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.briar.android.controller.handler.ResultHandler;
import org.briarproject.briar.android.controller.handler.UiResultHandler;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
@@ -20,10 +22,11 @@ import javax.inject.Inject;
public class SetupControllerImpl extends PasswordControllerImpl
implements SetupController {
private static final Logger LOG =
Logger.getLogger(SetupControllerImpl.class.getName());
@Nullable
private String authorName, password;
@Nullable
private SetupActivity setupActivity;
private volatile SetupActivity setupActivity;
@Inject
SetupControllerImpl(SharedPreferences briarPrefs,
@@ -41,6 +44,7 @@ public class SetupControllerImpl extends PasswordControllerImpl
@Override
public boolean needToShowDozeFragment() {
SetupActivity setupActivity = this.setupActivity;
if (setupActivity == null) throw new IllegalStateException();
return DozeView.needsToBeShown(setupActivity) ||
HuaweiView.needsToBeShown(setupActivity);
@@ -48,28 +52,35 @@ public class SetupControllerImpl extends PasswordControllerImpl
@Override
public void setAuthorName(String authorName) {
this.authorName = authorName;
SetupActivity setupActivity = this.setupActivity;
if (setupActivity == null) throw new IllegalStateException();
setupActivity.setAuthorName(authorName);
}
@Override
public void setPassword(String password) {
SetupActivity setupActivity = this.setupActivity;
if (setupActivity == null) throw new IllegalStateException();
setupActivity.setPassword(password);
}
@Override
public void showPasswordFragment() {
SetupActivity setupActivity = this.setupActivity;
if (setupActivity == null) throw new IllegalStateException();
setupActivity.showPasswordFragment();
}
@Override
public void setPassword(String password) {
this.password = password;
}
@Override
public void showDozeOrCreateAccount() {
public void showDozeFragment() {
SetupActivity setupActivity = this.setupActivity;
if (setupActivity == null) throw new IllegalStateException();
if (needToShowDozeFragment()) {
setupActivity.showDozeFragment();
} else {
createAccount();
}
setupActivity.showDozeFragment();
}
@Override
public void createAccount() {
SetupActivity setupActivity = this.setupActivity;
UiResultHandler<Void> resultHandler =
new UiResultHandler<Void>(setupActivity) {
@Override
@@ -82,16 +93,24 @@ public class SetupControllerImpl extends PasswordControllerImpl
createAccount(resultHandler);
}
@Override
public void createAccount(ResultHandler<Void> resultHandler) {
if (authorName == null || password == null)
throw new IllegalStateException();
// Package access for testing
void createAccount(ResultHandler<Void> resultHandler) {
SetupActivity setupActivity = this.setupActivity;
if (setupActivity == null) throw new IllegalStateException();
String authorName = setupActivity.getAuthorName();
if (authorName == null) throw new IllegalStateException();
String password = setupActivity.getPassword();
if (password == null) throw new IllegalStateException();
cryptoExecutor.execute(() -> {
LOG.info("Creating account");
AndroidUtils.logDataDirContents(setupActivity);
databaseConfig.setLocalAuthorName(authorName);
SecretKey key = crypto.generateSecretKey();
databaseConfig.setEncryptionKey(key);
String hex = encryptDatabaseKey(key, password);
storeEncryptedDatabaseKey(hex);
LOG.info("Created account");
AndroidUtils.logDataDirContents(setupActivity);
resultHandler.onResult(null);
});
}

View File

@@ -28,7 +28,7 @@ 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.TestingConstants.EXPIRY_DATE;
import static org.briarproject.briar.android.TestingConstants.IS_BETA_BUILD;
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
import static org.briarproject.briar.android.controller.BriarControllerImpl.DOZE_ASK_AGAIN;

View File

@@ -8,6 +8,7 @@ import android.support.v7.preference.PreferenceManager;
import android.transition.Fade;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BaseActivity;
@@ -19,7 +20,7 @@ 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.EXPIRY_DATE;
public class SplashScreenActivity extends BaseActivity {
@@ -44,9 +45,11 @@ public class SplashScreenActivity extends BaseActivity {
setContentView(R.layout.splash);
if (configController.accountSignedIn()) {
LOG.info("Already signed in, not showing splash screen");
startActivity(new Intent(this, OpenDatabaseActivity.class));
finish();
} else {
LOG.info("Showing splash screen");
new Handler().postDelayed(() -> {
startNextActivity();
supportFinishAfterTransition();
@@ -64,9 +67,12 @@ public class SplashScreenActivity extends BaseActivity {
LOG.info("Expired");
startActivity(new Intent(this, ExpiredActivity.class));
} else {
AndroidUtils.logDataDirContents(this);
if (configController.accountExists()) {
LOG.info("Account exists");
startActivity(new Intent(this, OpenDatabaseActivity.class));
} else {
LOG.info("Account does not exist");
configController.deleteAccount(this);
startActivity(new Intent(this, SetupActivity.class));
}
@@ -74,8 +80,10 @@ public class SplashScreenActivity extends BaseActivity {
}
private void setPreferencesDefaults() {
androidExecutor.runOnBackgroundThread(() ->
PreferenceManager.setDefaultValues(SplashScreenActivity.this,
R.xml.panic_preferences, false));
androidExecutor.runOnBackgroundThread(() -> {
PreferenceManager.setDefaultValues(SplashScreenActivity.this,
R.xml.panic_preferences, false);
LOG.info("Finished setting panic preference defaults");
});
}
}

View File

@@ -46,7 +46,7 @@ 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.BuildConfig.APPLICATION_ID;
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
@MethodsNotNullByDefault
@ParametersNotNullByDefault

View File

@@ -20,10 +20,12 @@
<ImageView
android:id="@+id/checkImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/contact_disconnected"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_margin="8dp"
android:src="@drawable/ic_check_white"
android:tint="?colorControlNormal"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="@+id/button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/button"
@@ -34,9 +36,7 @@
style="@style/BriarButton.Default"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_margin="8dp"
app:layout_constraintEnd_toStartOf="@+id/helpButton"
app:layout_constraintStart_toEndOf="@+id/checkImage"
app:layout_constraintTop_toBottomOf="@+id/textView"
@@ -44,11 +44,13 @@
<ImageButton
android:id="@+id/helpButton"
style="@style/BriarButton.Default"
android:layout_width="48dp"
android:layout_height="wrap_content"
style="@style/BriarButtonFlat.Positive"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_margin="8dp"
android:contentDescription="@string/help"
android:src="@drawable/ic_help_outline_white"
android:tint="@color/briar_button_positive"
app:layout_constraintBottom_toBottomOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/button"/>

View File

@@ -31,7 +31,11 @@
<string name="dialog_title_lost_password">Passwort vergessen</string>
<string name="dialog_message_lost_password">Dein Briar-Konto ist auf deinem Gerät verschlüsselt und nicht in der Cloud gespeichert, deshalb kannst du dein Passwort nicht zurücksetzen. Willst du dein Konto löschen und neu beginnen?\n\nAchtung: Deine bestehenden Identitäten, Kontakte und Nachrichten gehen dann für immer verloren.</string>
<string name="startup_failed_notification_title">Briar konnte nicht gestartet werden</string>
<string name="startup_failed_notification_text">Für weitere Informationen, hier klicken.</string>
<string name="startup_failed_activity_title">Fehler beim Starten von Briar</string>
<string name="startup_failed_db_error">Aus irgendeinem Grund ist deine Briar-Datenbank irreparabel beschädigt. Dein Konto, deine Daten und alle deinen Kontakte sind verloren. Leider musst du Briar neu installieren oder ein neues Konto einrichten, indem du ,Ich habe mein Passwort vergessen\' auswählst, wenn du zur Eingabe deines Passworts aufgefordert wirst. </string>
<string name="startup_failed_data_too_old_error">Dein Konto wurde mit einer alten Version dieser App erstellt und kann mit dieser Version nicht geöffnet werden. Installiere entweder die alte Version oder richte ein neues Konto ein, indem du \"Ich habe mein Passwort vergessen\" auswählst, wenn du zur Eingabe deines Passworts aufgefordert wirst. </string>
<string name="startup_failed_data_too_new_error">Diese Version der App ist zu alt. Bitte führe eine Aktualisierung auf die neueste Version der App durch und versuch es dann noch mal.</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 deines 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 Testversion von Briar. Dein Konto läuft in %d Tag ab und kann nicht verlängert werden.</item>
@@ -39,6 +43,11 @@
</plurals>
<string name="expiry_update">Das Ablaufdatum des Tests wurde verlängert. Dein Konto läuft nun in %d Tagen ab.</string>
<string name="expiry_date_reached">Diese Software ist abgelaufen.\nDanke dass du Briar getestet hast!</string>
<string name="download_briar">Lade bitte Version 1.0 herunter, um Briar weiterhin zu nutzen.</string>
<string name="create_new_account">Du wirst ein neues Konto erstellen müssen, wobei du jedoch wieder den selben Benutzernamen verwenden kannst.</string>
<string name="download_briar_button">Lade Briar 1.0 herunter</string>
<string name="startup_open_database">Datenbank wird entschlüsselt...</string>
<string name="startup_migrate_database">Datenbank wird aktualisiert...</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Navigationsleiste öffnen</string>
<string name="nav_drawer_close_description">Navigationsleiste schliessen</string>
@@ -94,6 +103,7 @@
<string name="fix">Behoben</string>
<string name="help">Hilfe</string>
<!--Contacts and Private Conversations-->
<string name="no_contacts">Du hast noch keine Kontakte\n\nTippe auf das +-Symbol um einen Kontakt hinzuzufügen</string>
<string name="date_no_private_messages">Keine Nachrichten.</string>
<string name="message_hint">Nachricht eingeben</string>
<string name="delete_contact">Kontakt löschen</string>
@@ -112,6 +122,7 @@
<string name="contact_already_exists">Kontakt %s existiert bereits</string>
<string name="contact_exchange_failed">Kontaktaustausch fehlgeschlagen</string>
<string name="qr_code_invalid">Der QR-Code ist ungültig</string>
<string name="qr_code_unsupported">Der QR-Code, den du versuchst zu scannen, gehört zu einer alten Version von %s welche nicht mehr unterstützt wird.\n\nBitte versichert euch, dass bei euch beiden die neueste Version läuft und versucht es dann erneut.</string>
<string name="camera_error">Kamerafehler</string>
<string name="connecting_to_device">Verbinde mit Gerät\u2026</string>
<string name="authenticating_with_device">Authentifiziere Gerät\u2026</string>
@@ -208,7 +219,9 @@
<string name="btn_reply">Antworten</string>
<string name="forum_leave">Forum verlassen</string>
<string name="dialog_title_leave_forum">Verlassen des Forums bestätigen</string>
<string name="dialog_message_leave_forum">Bist du sicher, dass du dieses Forum verlassen willst?\n\nKontakte, mit denen du dieses Forum geteilt hast, werden keine Updates mehr von diesem Forum bekommen.</string>
<string name="dialog_button_leave">Verlassen</string>
<string name="forum_left_toast">Forum wurde verlassen</string>
<!--Forum Sharing-->
<string name="forum_share_button">Forum teilen</string>
<string name="contacts_selected">Ausgewählte Kontakte</string>
@@ -219,6 +232,9 @@
<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>
<string name="forum_invitations_title">Foreneinladungen</string>
<string name="forum_invitation_exists">Du hast bereits eine Einladung zu diesem Forum angenommen.\n\nMehrere Einladungen anzunehmen, wird deine Verbindung zu diesem Forum schneller und zuverlässiger machen.</string>
<string name="forum_joined_toast">Dem Forum beigetreten</string>
<string name="forum_declined_toast">Einladung abgelehnt</string>
<string name="shared_by_format">Geteilt durch %s</string>
<string name="forum_invitation_already_sharing">Bereits geteilt.</string>
<string name="forum_invitation_response_accepted_sent">Du hast die Forumseinladung von %s akzeptiert.</string>
@@ -236,12 +252,14 @@
<!--Blogs-->
<string name="read_more">weiterlesen</string>
<string name="blogs_write_blog_post">Blogbeitrag erstellen</string>
<string name="blogs_write_blog_post_body_hint">Gib deinen Blogbeitrag ein</string>
<string name="blogs_publish_blog_post">Veröffentlichen</string>
<string name="blogs_blog_post_created">Blogbeitrag erstellt</string>
<string name="blogs_blog_post_received">Neuen Blogbeitrag empfangen</string>
<string name="blogs_blog_post_scroll_to">Scrolle zu</string>
<string name="blogs_remove_blog">Blog entfernen</string>
<string name="blogs_remove_blog_ok">Aufheben</string>
<string name="blogs_blog_removed">Blog wurde entfernt</string>
<string name="blogs_reblog_comment_hint">Kommentar hinzufügen (optional)</string>
<string name="blogs_reblog_button">Rebloggen</string>
<!--Blog Sharing-->
@@ -256,6 +274,8 @@
<string name="blogs_sharing_invitation_received">%1$shat den Blog \"%2$s\" mit dir geteilt.</string>
<string name="blogs_sharing_invitation_sent">Du teilst den Blog \"%1$s\" mit %2$s.</string>
<string name="blogs_sharing_invitations_title">Blogeinladungen</string>
<string name="blogs_sharing_joined_toast">Blog abonniert</string>
<string name="blogs_sharing_declined_toast">Einladung abgelehnt</string>
<string name="sharing_status_blog">Jeder Abonnent eines Blogs kann diesen mit seinen Kontakten teilen. Du teilst diesen Blog mit den folgenden Kontakten. Möglicherweise gibt es Abonnenten die nicht sichtbar sind.</string>
<!--RSS Feeds-->
<string name="blogs_rss_feeds_import">RSS-Feed importieren</string>
@@ -356,4 +376,6 @@
<string name="permission_camera_denied_toast">Berechtigung für Kamera wurde nicht gewährt</string>
<string name="qr_code">QR-Code</string>
<!--Low Memory Notification-->
<string name="low_memory_shutdown_notification_title">Von Briar abgemeldet</string>
<string name="low_memory_shutdown_notification_text">Mangels Speicherplatz abgemeldet.</string>
</resources>

View File

@@ -134,6 +134,7 @@
<string name="introduction_onboarding_title">Présenter vos contacts</string>
<string name="introduction_onboarding_text">Vous pouvez présenter vos contacts mutuellement, afin quils naient pas à se rencontrer en personne pour se connecter les uns aux autres avec Briar.</string>
<string name="introduction_activity_title">Sélectionner un contact </string>
<string name="introduction_not_possible">Une présentation est déjà en cours avec ces contacts. Veuillez dabord lui permettre de se terminer. Si vous ou vos contacts êtes rarement en ligne, cela peut prendre du temps.</string>
<string name="introduction_message_title">Présenter des contacts</string>
<string name="introduction_message_hint">Ajouter un message (facultatif)</string>
<string name="introduction_button">Faire les présentations</string>
@@ -145,6 +146,7 @@
<string name="introduction_request_exists_received">%1$s a demandé de vous présenter à %2$s, mais %2$s est déjà dans votre liste de contacts. Puisque %1$s pourrait ne pas le savoir, vous pouvez tout de même répondre :</string>
<string name="introduction_request_answered_received">%1$s a demandé de vous présenter à %2$s.</string>
<string name="introduction_response_accepted_sent">Vous avez accepté dêtre présenté à %1$s.</string>
<string name="introduction_response_accepted_sent_info">Avant que %1$s ne soit ajouté à vos contacts, ce contact doit aussi accepter la présentation. Cela peut prendre du temps. </string>
<string name="introduction_response_declined_sent">Vous avez refusé dêtre présenté à %1$s.</string>
<string name="introduction_response_accepted_received">%1$s a accepté dêtre présenté à %2$s.</string>
<string name="introduction_response_declined_received">%1$s a refusé dêtre présenté à %2$s.</string>

View File

@@ -1,6 +1,9 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<!--Setup-->
<string name="setup_title">La benvenguda a Briar</string>
<string name="setup_next">Seguent</string>
<string name="setup_password_intro">Causissètz un senhal</string>
<string name="choose_nickname">Causir un escais-nom</string>
<string name="choose_password">Causir un senhal</string>
<string name="confirm_password">Confirmar lo senhal</string>
@@ -8,6 +11,9 @@
<string name="password_too_weak">Lo senhal es tròp feble</string>
<string name="passwords_do_not_match">Los senhals correspondon pas</string>
<string name="create_account_button">Crear un compte</string>
<string name="more_info">Mai dinformacions</string>
<string name="don_t_ask_again">Demandar pas mai</string>
<string name="setup_huawei_button">Projècte Briar</string>
<!--Login-->
<string name="enter_password">Picatz vòstre senhal:</string>
<string name="try_again">Senhal incorècte, tornatz ensajar</string>
@@ -18,9 +24,14 @@
Volètz suprimir vòstre compte e ne crear un nòu?\n
\nMèfi:vòstra identitat, vòstres contactes e messatges seràn perduts per totjorn.</string>
<string name="startup_failed_notification_title">Briar a pas pogut aviar</string>
<string name="startup_failed_notification_text">Tocatz per mai dinformacions.</string>
<string name="startup_failed_activity_title">Fracàs de laviada de Briar.</string>
<string name="startup_failed_service_error">Briar a pas pogut aviar un modul necessari. Tornar installar Briar pòt resolver aquò. Que aquò siá dich:perdretz vòstre compte e totas las donadas ligadas a aqueste. Briar utiliza pas de servidor centralizat per salvar sas donadas.</string>
<string name="expiry_date_reached">Vòstre logicial sacabèt.\nMercés daver ensajat!</string>
<string name="create_new_account">Vos caldrà crear un nòu compte, mas podètz emplegar lo meteis escais-nom.</string>
<string name="download_briar_button">Telecargar Briar 1.0</string>
<string name="startup_open_database">Deschirament de la basa de donadas…</string>
<string name="startup_migrate_database">Mesa a nivèl de la basa de donadas…</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Dobrir lo panèl de navigacion</string>
<string name="nav_drawer_close_description">Tampar lo panèl de navigacion</string>
@@ -76,6 +87,7 @@ Volètz suprimir vòstre compte e ne crear un nòu?\n
<string name="help">Ajuda</string>
<!--Contacts and Private Conversations-->
<string name="date_no_private_messages">Cap messatge</string>
<string name="no_private_messages">Cap de messatge de mostrar</string>
<string name="message_hint">Picatz lo messatge</string>
<string name="delete_contact">Suprimir lo contacte</string>
<string name="dialog_title_delete_contact">Confirmatz la supression del contacte</string>
@@ -93,6 +105,7 @@ Volètz suprimir vòstre compte e ne crear un nòu?\n
<string name="contact_already_exists">Lo contacte %s existís ja</string>
<string name="contact_exchange_failed">Lescambi de contacte a fracassat</string>
<string name="qr_code_invalid">Lo QR còdi es invalid</string>
<string name="camera_error">Error de camèra</string>
<string name="connecting_to_device">Connexion a laparelh\u2026</string>
<string name="authenticating_with_device">Autentificacion amb laparelh\u2026</string>
<string name="connection_aborted_remote">Vòstre contacte a copat la connexion!Poiriá arribar que qualquun ensage dinterferir amb aquela</string>
@@ -188,6 +201,7 @@ Volètz suprimir vòstre compte e ne crear un nòu?\n
<string name="forum_leave">Quitar lo fòrum</string>
<string name="dialog_title_leave_forum">Confirmar la sortida del fòrum</string>
<string name="dialog_button_leave">Quitar</string>
<string name="forum_left_toast">Quitar lo forum</string>
<!--Forum Sharing-->
<string name="forum_share_button">Partejar lo fòrum</string>
<string name="contacts_selected">Contactes seleccionats</string>
@@ -326,5 +340,10 @@ Volètz suprimir vòstre compte e ne crear un nòu?\n
<!--Screen Filters & Tapjacking-->
<string name="screen_filter_title">Superposicion detectada</string>
<!--Permission Requests-->
<string name="permission_camera_title">Permission de la camèra</string>
<string name="permission_camera_request_body">Per numerizar lo còdi QR cal que Briar aja laccès a la camèra.</string>
<string name="permission_camera_denied_toast">La permission a la camèra es pas estada donada</string>
<string name="qr_code">Còdi QR</string>
<!--Low Memory Notification-->
<string name="low_memory_shutdown_notification_title">Desconnectat de Briar</string>
</resources>

View File

@@ -2,7 +2,7 @@
<resources>
<!--Setup-->
<string name="setup_title">Добро пожаловать в Briar</string>
<string name="setup_name_explanation">Ваш ник будет показан рядом с любым контентом, который вы публикуете. Его нельзя изменить после создания аккаунта.</string>
<string name="setup_name_explanation">Ваш псевдоним будет показан рядом с любым публикуемым контентом. Его нельзя изменить после создания аккаунта.</string>
<string name="setup_next">Вперед</string>
<string name="setup_password_intro">Придумайте пароль</string>
<string name="setup_password_explanation">Ваша учетная запись Briar хранится в зашифрованном виде только на устройстве. Если вы забудете свой пароль или удалите Briar, то не сможете восстановить свою учетную запись.\n\nПридумайте длинный пароль, который трудно угадать, например четыре случайных слова или десять случайных букв, цифр и символов.</string>
@@ -168,11 +168,11 @@
<item quantity="other">%d новых контактов добавлено.</item>
</plurals>
<!--Private Groups-->
<string name="groups_list_empty">Нет групп для отображения\n\nTКоснитесь значка + для создания группы, или попросите ваши контакты поделиться с вами группами</string>
<string name="groups_list_empty">Нет групп для отображения\n\nКоснитесь значка + для создания группы, или попросите ваши контакты поделиться с вами группами</string>
<string name="groups_created_by">Создано %s</string>
<plurals name="messages">
<item quantity="one">%d сообщение</item>
<item quantity="few">%d сообщений</item>
<item quantity="few">%d сообщения</item>
<item quantity="many">%d сообщений</item>
<item quantity="other">%d сообщений</item>
</plurals>
@@ -225,7 +225,7 @@
<string name="groups_reveal_visible_revealed_by_contact">Связь между контактами видна группе (раскрывается %s)</string>
<string name="groups_reveal_invisible">Связь между контактами не видна группе</string>
<!--Forums-->
<string name="no_forums">Нет форумов для отображения\n\nTКоснитесь значка + для создания форума, или попросите ваши контакты поделиться с вами форумами</string>
<string name="no_forums">Нет форумов для отображения\n\nКоснитесь значка + для создания форума, или попросите ваши контакты поделиться с вами форумами</string>
<string name="create_forum_title">Создать форум</string>
<string name="choose_forum_hint">Придумайте имя для вашего форума</string>
<string name="create_forum_button">Создать форум</string>
@@ -283,12 +283,12 @@
<string name="blogs_write_blog_post">Написать в блоге</string>
<string name="blogs_write_blog_post_body_hint">Напишите свой пост в блоге</string>
<string name="blogs_publish_blog_post">Опубликовать</string>
<string name="blogs_blog_post_created">Запись блога создана</string>
<string name="blogs_blog_post_received">Появилась новая запись блога</string>
<string name="blogs_blog_post_created">Пост создан</string>
<string name="blogs_blog_post_received">Появился новый пост в блоге</string>
<string name="blogs_blog_post_scroll_to">Перейти</string>
<string name="blogs_feed_empty_state">Нет постов для отображения\n\nПосты ваших контактов и блогов, на которые вы подписаны, появятся здесь\n\nКоснитесь значка пера, чтобы написать сообщение</string>
<string name="blogs_remove_blog">Удалить блог</string>
<string name="blogs_remove_blog_dialog_message">Вы уверены, что хотите удалить этот блог?\n\nПосты будут удалены с вашего устройства, но не с устройств других пользователей.\n\nВсе контакты, с которыми вы поделились этим блогом, могут перестать получать обновления.</string>
<string name="blogs_remove_blog_dialog_message">Вы уверены, что хотите удалить этот блог?\n\nПосты будут удалены только с вашего устройства.\n\nВсе контакты, с которыми вы поделились этим блогом, могут перестать получать обновления.</string>
<string name="blogs_remove_blog_ok">Убрать</string>
<string name="blogs_blog_removed">Блог удален</string>
<string name="blogs_reblog_comment_hint">Добавить комментарий (необязательно)</string>
@@ -318,7 +318,7 @@
<string name="blogs_rss_feeds_manage_author">Автор:</string>
<string name="blogs_rss_feeds_manage_updated">Последнее обновление:</string>
<string name="blogs_rss_remove_feed">Удалить RSS-канал</string>
<string name="blogs_rss_remove_feed_dialog_message">Вы уверены, что хотите удалить эту ленту?\n\nПосты будут удалены с вашего устройства, но не с устройств других пользователей.\n\nВсе контакты, с которыми вы поделились этой лентой, могут перестать получать обновления.</string>
<string name="blogs_rss_remove_feed_dialog_message">Вы уверены, что хотите удалить эту ленту?\n\nПосты будут удалены только с вашего устройства.\n\nВсе контакты, с которыми вы поделились этой лентой, могут перестать получать обновления.</string>
<string name="blogs_rss_remove_feed_ok">Убрать</string>
<string name="blogs_rss_feeds_manage_delete_error">Не удалось удалить RSS-канал!</string>
<string name="blogs_rss_feeds_manage_empty_state">Нет RSS-лент для отображения\n\nКоснитесь значка + для импорта ленты</string>
@@ -356,8 +356,8 @@
<string name="uninstall_setting_summary">Это потребует вашего подтверждения</string>
<!--Settings Notifications-->
<string name="notification_settings_title">Уведомления</string>
<string name="notify_private_messages_setting_title">Приватные сообщения</string>
<string name="notify_private_messages_setting_summary">Показывать оповещения для приватных сообщений</string>
<string name="notify_private_messages_setting_title">Личные сообщения</string>
<string name="notify_private_messages_setting_summary">Показывать оповещения для личных сообщений</string>
<string name="notify_private_messages_setting_summary_26">Настройка оповещений для личных сообщений</string>
<string name="notify_group_messages_setting_title">Групповые сообщения</string>
<string name="notify_group_messages_setting_summary">Показывать оповещения для групповых сообщений</string>

View File

@@ -421,8 +421,4 @@
<string name="permission_camera_denied_toast">Camera permission was not granted</string>
<string name="qr_code">QR code</string>
<string name="show_qr_code_fullscreen">Show QR code fullscreen</string>
<!-- Low Memory Notification -->
<string name="low_memory_shutdown_notification_title">Signed out of Briar</string>
<string name="low_memory_shutdown_notification_text">Signed out due to lack of memory.</string>
</resources>

View File

@@ -54,7 +54,8 @@ public class PasswordControllerImplTest extends BrambleMockTestCase {
oneOf(briarPrefs).edit();
will(returnValue(editor));
oneOf(editor).putString("key", newEncryptedHex);
oneOf(editor).apply();
will(returnValue(editor));
oneOf(editor).commit();
}});
PasswordControllerImpl p = new PasswordControllerImpl(briarPrefs,

View File

@@ -73,7 +73,7 @@ public class PasswordFragmentTest {
// assert controller has been called properly
verify(setupController, times(1)).setPassword(safePass);
verify(setupController, times(1)).showDozeOrCreateAccount();
verify(setupController, times(1)).createAccount();
}
@Test

View File

@@ -15,22 +15,8 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static junit.framework.Assert.assertEquals;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.NONE;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_STRONG;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.STRONG;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.WEAK;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
@RunWith(RobolectricTestRunner.class)
@Config(sdk = 21, application = TestBriarApplication.class,

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android.login;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
@@ -8,10 +9,13 @@ import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.bramble.test.TestUtils;
import org.jmock.Expectations;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.After;
import org.junit.Test;
import java.io.File;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -40,6 +44,7 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
private final String encryptedHex = "010203";
private final byte[] encryptedBytes = new byte[] {1, 2, 3};
private final SecretKey key = getSecretKey();
private final File testDir = TestUtils.getTestDirectory();
public SetupControllerImplTest() {
context.setImposteriser(ClassImposteriser.INSTANCE);
@@ -47,10 +52,22 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
}
@Test
@SuppressWarnings("ResultOfMethodCallIgnored")
public void testCreateAccount() {
context.checking(new Expectations() {{
// Setting the author name shows the password fragment
oneOf(setupActivity).showPasswordFragment();
// Allow the contents of the data directory to be logged
allowing(setupActivity).getApplicationInfo();
will(returnValue(new ApplicationInfo() {{
dataDir = testDir.getAbsolutePath();
}}));
// Set the author name and password
oneOf(setupActivity).setAuthorName(authorName);
oneOf(setupActivity).setPassword(password);
// Get the author name and password
oneOf(setupActivity).getAuthorName();
will(returnValue(authorName));
oneOf(setupActivity).getPassword();
will(returnValue(password));
// Generate a database key
oneOf(crypto).generateSecretKey();
will(returnValue(key));
@@ -64,7 +81,8 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
oneOf(briarPrefs).edit();
will(returnValue(editor));
oneOf(editor).putString("key", encryptedHex);
oneOf(editor).apply();
will(returnValue(editor));
oneOf(editor).commit();
}});
SetupControllerImpl s = new SetupControllerImpl(briarPrefs,
@@ -77,4 +95,9 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
s.createAccount(result -> called.set(true));
assertTrue(called.get());
}
@After
public void tearDown() {
TestUtils.deleteTestDirectory(testDir);
}
}

View File

@@ -32,7 +32,7 @@ public interface BlogManager {
int MINOR_VERSION = 0;
/**
* Adds the given {@link Blog).}
* Adds the given {@link Blog}.
*/
void addBlog(Blog b) throws DbException;

View File

@@ -16,6 +16,7 @@ import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.identity.IdentityModule;
@@ -39,7 +40,6 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.test.TestPluginConfigModule.MAX_LATENCY;
@@ -69,7 +69,6 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
alice = DaggerSimplexMessagingIntegrationTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(aliceDir)).build();
injectEagerSingletons(alice);
alice.inject(new SystemModule.EagerSingletons());
bob = DaggerSimplexMessagingIntegrationTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(bobDir)).build();
injectEagerSingletons(bob);
@@ -159,7 +158,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
// Create a stream writer
StreamWriterFactory streamWriterFactory =
device.getStreamWriterFactory();
OutputStream streamWriter =
StreamWriter streamWriter =
streamWriterFactory.createStreamWriter(out, ctx);
// Create an outgoing sync session
SyncSessionFactory syncSessionFactory = device.getSyncSessionFactory();
@@ -167,7 +166,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
contactId, MAX_LATENCY, streamWriter);
// Write whatever needs to be written
session.run();
streamWriter.close();
streamWriter.sendEndOfStream();
// Return the contents of the stream
return out.toByteArray();
}

View File

@@ -23,6 +23,7 @@ import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.sync.event.MessageStateChangedEvent;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.identity.IdentityModule;
@@ -340,9 +341,10 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone
LOG.info("TEST: Sending message from " + from + " to " + to);
ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamWriter streamWriter = new TestStreamWriter(out);
// Create an outgoing sync session
SyncSession sessionFrom =
fromSync.createSimplexOutgoingSession(toId, MAX_LATENCY, out);
SyncSession sessionFrom = fromSync.createSimplexOutgoingSession(toId,
MAX_LATENCY, streamWriter);
// Write whatever needs to be written
sessionFrom.run();
out.close();

View File

@@ -0,0 +1,25 @@
package org.briarproject.briar.test;
import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.IOException;
import java.io.OutputStream;
class TestStreamWriter implements StreamWriter {
private final OutputStream out;
TestStreamWriter(OutputStream out) {
this.out = out;
}
@Override
public OutputStream getOutputStream() {
return out;
}
@Override
public void sendEndOfStream() throws IOException {
out.flush();
}
}