mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Merge branch 'social-backup-restore-activity' into 'social-backup-poc'
Social backup restore activity See merge request briar/briar!1444
This commit is contained in:
9
.idea/codeStyles/Project.xml
generated
9
.idea/codeStyles/Project.xml
generated
@@ -31,15 +31,6 @@
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value />
|
||||
</option>
|
||||
<option name="PACKAGES_IMPORT_LAYOUT">
|
||||
<value>
|
||||
<package name="" alias="false" withSubpackages="true" />
|
||||
<package name="java" alias="false" withSubpackages="true" />
|
||||
<package name="javax" alias="false" withSubpackages="true" />
|
||||
<package name="kotlin" alias="false" withSubpackages="true" />
|
||||
<package name="" alias="true" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.api.account;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.DecryptionException;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.identity.Identity;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
@@ -43,6 +44,17 @@ public interface AccountManager {
|
||||
*/
|
||||
boolean createAccount(String name, String password);
|
||||
|
||||
/**
|
||||
* Restores a given identity by registering it with the
|
||||
* {@link IdentityManager}. Creates a database key, encrypts it with the
|
||||
* given password and stores it on disk. {@link #accountExists()} will
|
||||
* return true after this method returns true.
|
||||
* @param identity
|
||||
* @param password
|
||||
* @return
|
||||
*/
|
||||
boolean restoreAccount(Identity identity, String password);
|
||||
|
||||
/**
|
||||
* Deletes all account state from disk. {@link #accountExists()} will
|
||||
* return false after this method returns.
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.api.contact;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.UnsupportedVersionException;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.ContactExistsException;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
@@ -48,6 +49,9 @@ public interface ContactManager {
|
||||
SecretKey rootKey, long timestamp, boolean alice, boolean verified,
|
||||
boolean active) throws DbException;
|
||||
|
||||
ContactId addContact(Transaction txn, Author remote, AuthorId local,
|
||||
PublicKey handshake, boolean verified) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a contact associated with the given local and remote pseudonyms,
|
||||
* replacing the given pending contact, derives and stores handshake mode
|
||||
|
||||
@@ -24,6 +24,19 @@ public interface HandshakeManager {
|
||||
HandshakeResult handshake(PendingContactId p, InputStream in,
|
||||
StreamWriter out) throws DbException, IOException;
|
||||
|
||||
/**
|
||||
* Handshakes with the given contact. Returns an ephemeral master key
|
||||
* authenticated with both parties' handshake key pairs and a flag
|
||||
* indicating whether the local peer is Alice or Bob.
|
||||
*
|
||||
* @param in An incoming stream for the handshake, which must be secured in
|
||||
* handshake mode
|
||||
* @param out An outgoing stream for the handshake, which must be secured
|
||||
* in handshake mode
|
||||
*/
|
||||
HandshakeResult handshake(ContactId c, InputStream in, StreamWriter out)
|
||||
throws DbException, IOException;
|
||||
|
||||
class HandshakeResult {
|
||||
|
||||
private final SecretKey masterKey;
|
||||
|
||||
@@ -35,6 +35,20 @@ public interface KeyManager {
|
||||
ContactId c, SecretKey rootKey, long timestamp, boolean alice,
|
||||
boolean active) throws DbException;
|
||||
|
||||
/**
|
||||
* Derives and stores a set of rotation mode transport keys for
|
||||
* communicating with the given contact over each transport and returns the
|
||||
* key set IDs.
|
||||
* <p/>
|
||||
* {@link StreamContext StreamContexts} for the contact can be created
|
||||
* after this method has returned.
|
||||
*
|
||||
* @param alice True if the local party is Alice
|
||||
* @param active Whether the derived keys can be used for outgoing streams
|
||||
*/
|
||||
Map<TransportId, KeySetId> addRotationKeys(ContactId c, SecretKey rootKey,
|
||||
long timestamp, boolean alice, boolean active) throws DbException;
|
||||
|
||||
/**
|
||||
* Informs the key manager that a new contact has been added. Derives and
|
||||
* stores a set of handshake mode transport keys for communicating with the
|
||||
|
||||
@@ -176,6 +176,18 @@ class AccountManagerImpl implements AccountManager {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean restoreAccount(Identity identity, String password) {
|
||||
synchronized (stateChangeLock) {
|
||||
if (hasDatabaseKey())
|
||||
throw new AssertionError("Already have a database key");
|
||||
identityManager.registerIdentity(identity);
|
||||
SecretKey key = crypto.generateSecretKey();
|
||||
if (!encryptAndStoreDatabaseKey(key, password)) return false;
|
||||
databaseKey = key;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("stateChangeLock")
|
||||
private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) {
|
||||
byte[] plaintext = key.getBytes();
|
||||
|
||||
@@ -76,7 +76,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
||||
ioExecutor.execute(new IncomingDuplexSyncConnection(keyManager,
|
||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
||||
syncSessionFactory, transportPropertyManager, ioExecutor,
|
||||
t, d));
|
||||
t, d, handshakeManager));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,7 +101,7 @@ class ConnectionManagerImpl implements ConnectionManager {
|
||||
ioExecutor.execute(new OutgoingDuplexSyncConnection(keyManager,
|
||||
connectionRegistry, streamReaderFactory, streamWriterFactory,
|
||||
syncSessionFactory, transportPropertyManager, ioExecutor,
|
||||
secureRandom, c, t, d));
|
||||
secureRandom, handshakeManager, c, t, d));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.connection;
|
||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
||||
import org.briarproject.bramble.api.connection.InterruptibleConnection;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.HandshakeManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
||||
|
||||
@@ -2,6 +2,8 @@ package org.briarproject.bramble.connection;
|
||||
|
||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.HandshakeManager;
|
||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
@@ -13,9 +15,11 @@ 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.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
@@ -24,6 +28,10 @@ import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
@NotNullByDefault
|
||||
class IncomingDuplexSyncConnection extends DuplexSyncConnection
|
||||
implements Runnable {
|
||||
private final HandshakeManager handshakeManager;
|
||||
|
||||
// FIXME: Exchange timestamp as part of handshake protocol?
|
||||
private static final long TIMESTAMP = 1617235200; // 1 April 2021 00:00 UTC
|
||||
|
||||
IncomingDuplexSyncConnection(KeyManager keyManager,
|
||||
ConnectionRegistry connectionRegistry,
|
||||
@@ -32,10 +40,12 @@ class IncomingDuplexSyncConnection extends DuplexSyncConnection
|
||||
SyncSessionFactory syncSessionFactory,
|
||||
TransportPropertyManager transportPropertyManager,
|
||||
Executor ioExecutor, TransportId transportId,
|
||||
DuplexTransportConnection connection) {
|
||||
DuplexTransportConnection connection,
|
||||
HandshakeManager handshakeManager) {
|
||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
||||
streamWriterFactory, syncSessionFactory,
|
||||
transportPropertyManager, ioExecutor, transportId, connection);
|
||||
this.handshakeManager = handshakeManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,10 +64,22 @@ class IncomingDuplexSyncConnection extends DuplexSyncConnection
|
||||
return;
|
||||
}
|
||||
if (ctx.isHandshakeMode()) {
|
||||
// TODO: Support handshake mode for contacts
|
||||
LOG.warning("Received handshake tag, expected rotation mode");
|
||||
onReadError(true);
|
||||
return;
|
||||
if (!performHandshake(ctx, contactId)) {
|
||||
LOG.warning("Handshake failed");
|
||||
return;
|
||||
}
|
||||
// Allocate a rotation mode stream context
|
||||
ctx = allocateStreamContext(contactId, transportId);
|
||||
if (ctx == null) {
|
||||
LOG.warning("Could not allocate stream context");
|
||||
onWriteError();
|
||||
return;
|
||||
}
|
||||
if (ctx.isHandshakeMode()) {
|
||||
LOG.warning("Got handshake mode context after handshaking");
|
||||
onWriteError();
|
||||
return;
|
||||
}
|
||||
}
|
||||
connectionRegistry.registerIncomingConnection(contactId, transportId,
|
||||
this);
|
||||
@@ -103,5 +125,33 @@ class IncomingDuplexSyncConnection extends DuplexSyncConnection
|
||||
onWriteError();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean performHandshake(StreamContext ctxIn, ContactId contactId) {
|
||||
// Allocate the outgoing stream context
|
||||
StreamContext ctxOut =
|
||||
allocateStreamContext(contactId, transportId);
|
||||
if (ctxOut == null) {
|
||||
LOG.warning("Could not allocate stream context");
|
||||
onReadError(true);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
InputStream in = streamReaderFactory.createStreamReader(
|
||||
reader.getInputStream(), ctxIn);
|
||||
// Flush the output stream to send the outgoing stream header
|
||||
StreamWriter out = streamWriterFactory.createStreamWriter(
|
||||
writer.getOutputStream(), ctxOut);
|
||||
out.getOutputStream().flush();
|
||||
HandshakeManager.HandshakeResult result =
|
||||
handshakeManager.handshake(contactId, in, out);
|
||||
keyManager.addRotationKeys(contactId, result.getMasterKey(),
|
||||
TIMESTAMP, result.isAlice(), true);
|
||||
return true;
|
||||
} catch (IOException | DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
onReadError(true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.connection;
|
||||
|
||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.HandshakeManager;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
@@ -14,9 +15,11 @@ 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.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -28,21 +31,31 @@ import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
class OutgoingDuplexSyncConnection extends DuplexSyncConnection
|
||||
implements Runnable {
|
||||
|
||||
// FIXME: Exchange timestamp as part of handshake protocol?
|
||||
private static final long TIMESTAMP = 1617235200; // 1 April 2021 00:00 UTC
|
||||
|
||||
private final SecureRandom secureRandom;
|
||||
private final HandshakeManager handshakeManager;
|
||||
private final ContactId contactId;
|
||||
|
||||
OutgoingDuplexSyncConnection(KeyManager keyManager,
|
||||
OutgoingDuplexSyncConnection(
|
||||
KeyManager keyManager,
|
||||
ConnectionRegistry connectionRegistry,
|
||||
StreamReaderFactory streamReaderFactory,
|
||||
StreamWriterFactory streamWriterFactory,
|
||||
SyncSessionFactory syncSessionFactory,
|
||||
TransportPropertyManager transportPropertyManager,
|
||||
Executor ioExecutor, SecureRandom secureRandom, ContactId contactId,
|
||||
TransportId transportId, DuplexTransportConnection connection) {
|
||||
Executor ioExecutor,
|
||||
SecureRandom secureRandom,
|
||||
HandshakeManager handshakeManager,
|
||||
ContactId contactId,
|
||||
TransportId transportId,
|
||||
DuplexTransportConnection connection) {
|
||||
super(keyManager, connectionRegistry, streamReaderFactory,
|
||||
streamWriterFactory, syncSessionFactory,
|
||||
transportPropertyManager, ioExecutor, transportId, connection);
|
||||
this.secureRandom = secureRandom;
|
||||
this.handshakeManager = handshakeManager;
|
||||
this.contactId = contactId;
|
||||
}
|
||||
|
||||
@@ -56,10 +69,22 @@ class OutgoingDuplexSyncConnection extends DuplexSyncConnection
|
||||
return;
|
||||
}
|
||||
if (ctx.isHandshakeMode()) {
|
||||
// TODO: Support handshake mode for contacts
|
||||
LOG.warning("Cannot use handshake mode stream context");
|
||||
onWriteError();
|
||||
return;
|
||||
if (!performHandshake(ctx)) {
|
||||
LOG.warning("Handshake failed");
|
||||
return;
|
||||
}
|
||||
// Allocate a rotation mode stream context
|
||||
ctx = allocateStreamContext(contactId, transportId);
|
||||
if (ctx == null) {
|
||||
LOG.warning("Could not allocate stream context");
|
||||
onWriteError();
|
||||
return;
|
||||
}
|
||||
if (ctx.isHandshakeMode()) {
|
||||
LOG.warning("Got handshake mode context after handshaking");
|
||||
onWriteError();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Start the incoming session on another thread
|
||||
Priority priority = generatePriority();
|
||||
@@ -127,6 +152,57 @@ class OutgoingDuplexSyncConnection extends DuplexSyncConnection
|
||||
}
|
||||
}
|
||||
|
||||
private boolean performHandshake(StreamContext ctxOut) {
|
||||
// Flush the output stream to send the outgoing stream header
|
||||
StreamWriter out;
|
||||
try {
|
||||
out = streamWriterFactory.createStreamWriter(
|
||||
writer.getOutputStream(), ctxOut);
|
||||
out.getOutputStream().flush();
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
onWriteError();
|
||||
return false;
|
||||
}
|
||||
// Read and recognise the tag
|
||||
StreamContext ctxIn = recogniseTag(reader, transportId);
|
||||
// Unrecognised tags are suspicious in this case
|
||||
if (ctxIn == null) {
|
||||
LOG.warning("Unrecognised tag for returning stream");
|
||||
onReadError();
|
||||
return false;
|
||||
}
|
||||
// Check that the stream comes from the expected contact
|
||||
ContactId inContactId = ctxIn.getContactId();
|
||||
if (contactId == null) {
|
||||
LOG.warning("Expected contact tag, got rendezvous tag");
|
||||
onReadError();
|
||||
return false;
|
||||
}
|
||||
if (!inContactId.equals(contactId)) {
|
||||
LOG.warning("Wrong contact ID for returning stream");
|
||||
onReadError();
|
||||
return false;
|
||||
}
|
||||
// TODO: Register the connection, close it if it's redundant
|
||||
// Handshake and exchange contacts
|
||||
try {
|
||||
InputStream in = streamReaderFactory.createStreamReader(
|
||||
reader.getInputStream(), ctxIn);
|
||||
HandshakeManager.HandshakeResult result =
|
||||
handshakeManager.handshake(contactId, in, out);
|
||||
keyManager.addRotationKeys(contactId, result.getMasterKey(),
|
||||
TIMESTAMP, result.isAlice(), true);
|
||||
return true;
|
||||
} catch (IOException | DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
onWriteError();
|
||||
return false;
|
||||
} finally {
|
||||
// TODO: Unregister the connection
|
||||
}
|
||||
}
|
||||
|
||||
private void onReadError() {
|
||||
// 'Recognised' is always true for outgoing connections
|
||||
onReadError(true);
|
||||
|
||||
@@ -119,6 +119,15 @@ class ContactManagerImpl implements ContactManager, EventListener {
|
||||
verified, active));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContactId addContact(Transaction txn, Author remote, AuthorId local,
|
||||
PublicKey handshake, boolean verified) throws DbException {
|
||||
ContactId c = db.addContact(txn, remote, local, handshake, verified);
|
||||
Contact contact = db.getContact(txn, c);
|
||||
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHandshakeLink() throws DbException {
|
||||
KeyPair keyPair = db.transactionWithResult(true,
|
||||
|
||||
@@ -3,6 +3,8 @@ package org.briarproject.bramble.contact;
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.Predicate;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.contact.HandshakeManager;
|
||||
import org.briarproject.bramble.api.contact.PendingContact;
|
||||
@@ -88,6 +90,27 @@ class HandshakeManagerImpl implements HandshakeManager {
|
||||
});
|
||||
PublicKey theirStaticPublicKey = keys.getFirst();
|
||||
KeyPair ourStaticKeyPair = keys.getSecond();
|
||||
return handshake(theirStaticPublicKey, ourStaticKeyPair, in, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandshakeResult handshake(ContactId c, InputStream in,
|
||||
StreamWriter out) throws DbException, IOException {
|
||||
Pair<PublicKey, KeyPair> keys = db.transactionWithResult(true, txn -> {
|
||||
Contact contact = contactManager.getContact(txn, c);
|
||||
PublicKey handshakePublicKey = contact.getHandshakePublicKey();
|
||||
if (handshakePublicKey == null) throw new DbException();
|
||||
KeyPair keyPair = identityManager.getHandshakeKeys(txn);
|
||||
return new Pair<>(handshakePublicKey, keyPair);
|
||||
});
|
||||
PublicKey theirStaticPublicKey = keys.getFirst();
|
||||
KeyPair ourStaticKeyPair = keys.getSecond();
|
||||
return handshake(theirStaticPublicKey, ourStaticKeyPair, in, out);
|
||||
}
|
||||
|
||||
private HandshakeResult handshake(PublicKey theirStaticPublicKey,
|
||||
KeyPair ourStaticKeyPair, InputStream in, StreamWriter out)
|
||||
throws IOException {
|
||||
boolean alice = transportCrypto.isAlice(theirStaticPublicKey,
|
||||
ourStaticKeyPair);
|
||||
RecordReader recordReader = recordReaderFactory.createRecordReader(in);
|
||||
|
||||
@@ -101,9 +101,9 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TransportId, KeySetId> addRotationKeys(
|
||||
Transaction txn, ContactId c, SecretKey rootKey, long timestamp,
|
||||
boolean alice, boolean active) throws DbException {
|
||||
public Map<TransportId, KeySetId> addRotationKeys(Transaction txn,
|
||||
ContactId c, SecretKey rootKey, long timestamp, boolean alice,
|
||||
boolean active) throws DbException {
|
||||
Map<TransportId, KeySetId> ids = new HashMap<>();
|
||||
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
|
||||
TransportId t = e.getKey();
|
||||
@@ -114,6 +114,14 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
||||
return ids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TransportId, KeySetId> addRotationKeys(ContactId c,
|
||||
SecretKey rootKey, long timestamp, boolean alice, boolean active)
|
||||
throws DbException {
|
||||
return db.transactionWithResult(false, txn ->
|
||||
addRotationKeys(txn, c, rootKey, timestamp, alice, active));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
|
||||
PublicKey theirPublicKey, KeyPair ourKeyPair)
|
||||
@@ -137,7 +145,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
||||
PendingContactId p, PublicKey theirPublicKey, KeyPair ourKeyPair)
|
||||
throws DbException, GeneralSecurityException {
|
||||
SecretKey staticMasterKey = transportCrypto
|
||||
.deriveStaticMasterKey(theirPublicKey, ourKeyPair);
|
||||
.deriveStaticMasterKey(theirPublicKey, ourKeyPair);
|
||||
SecretKey rootKey =
|
||||
transportCrypto.deriveHandshakeRootKey(staticMasterKey, true);
|
||||
boolean alice = transportCrypto.isAlice(theirPublicKey, ourKeyPair);
|
||||
|
||||
@@ -174,6 +174,14 @@
|
||||
android:value="org.briarproject.briar.android.account.NewOrRecoverActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="org.briarproject.briar.android.socialbackup.recover.RestoreAccountActivity"
|
||||
android:label="@string/activity_name_restore_account"
|
||||
android:parentActivityName="org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="org.briarproject.briar.android.conversation.ConversationActivity"
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.briarproject.briar.android.account;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
interface DozeHelper {
|
||||
public interface DozeHelper {
|
||||
|
||||
boolean needToShowDozeFragment(Context context);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
class DozeView extends PowerView {
|
||||
public class DozeView extends PowerView {
|
||||
|
||||
@Nullable
|
||||
private Runnable onButtonClickListener;
|
||||
|
||||
@@ -21,7 +21,7 @@ import static android.os.Build.VERSION.SDK_INT;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
class HuaweiView extends PowerView {
|
||||
public class HuaweiView extends PowerView {
|
||||
|
||||
private final static String PACKAGE_NAME = "com.huawei.systemmanager";
|
||||
private final static String CLASS_NAME =
|
||||
|
||||
@@ -24,7 +24,7 @@ import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
abstract class PowerView extends ConstraintLayout {
|
||||
public abstract class PowerView extends ConstraintLayout {
|
||||
|
||||
private final TextView textView;
|
||||
private final ImageView checkImage;
|
||||
@@ -156,7 +156,7 @@ abstract class PowerView extends ConstraintLayout {
|
||||
};
|
||||
}
|
||||
|
||||
interface OnCheckedChangedListener {
|
||||
public interface OnCheckedChangedListener {
|
||||
void onCheckedChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import static org.briarproject.briar.android.account.SetupViewModel.State.SET_PA
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public
|
||||
class SetupViewModel extends AndroidViewModel {
|
||||
enum State {AUTHOR_NAME, SET_PASSWORD, DOZE, CREATED, FAILED}
|
||||
|
||||
|
||||
@@ -85,8 +85,10 @@ import org.briarproject.briar.android.socialbackup.CustodianSelectorFragment;
|
||||
import org.briarproject.briar.android.socialbackup.DistributedBackupActivity;
|
||||
import org.briarproject.briar.android.socialbackup.ExistingBackupFragment;
|
||||
import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardActivity;
|
||||
import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardErrorFragment;
|
||||
import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardFragment;
|
||||
import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardSuccessFragment;
|
||||
import org.briarproject.briar.android.socialbackup.recover.OwnerRecoveryModeErrorFragment;
|
||||
import org.briarproject.briar.android.socialbackup.recover.OwnerRecoveryModeExplainerFragment;
|
||||
import org.briarproject.briar.android.socialbackup.recover.OwnerRecoveryModeMainFragment;
|
||||
import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardActivity;
|
||||
@@ -94,8 +96,13 @@ import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardFragm
|
||||
import org.briarproject.briar.android.socialbackup.ShardsSentFragment;
|
||||
import org.briarproject.briar.android.socialbackup.ThresholdSelectorFragment;
|
||||
import org.briarproject.briar.android.socialbackup.creation.CreateBackupModule;
|
||||
import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardSuccessFragment;
|
||||
import org.briarproject.briar.android.socialbackup.recover.RestoreAccountActivity;
|
||||
import org.briarproject.briar.android.socialbackup.recover.RestoreAccountDozeFragment;
|
||||
import org.briarproject.briar.android.socialbackup.recover.RestoreAccountSetPasswordFragment;
|
||||
import org.briarproject.briar.android.splash.SplashScreenActivity;
|
||||
import org.briarproject.briar.android.test.TestDataActivity;
|
||||
import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
@@ -206,6 +213,8 @@ public interface ActivityComponent {
|
||||
|
||||
void inject(OwnerRecoveryModeMainFragment ownerRecoveryModeMainFragment);
|
||||
|
||||
void inject(RestoreAccountActivity restoreAccountActivity);
|
||||
|
||||
// Fragments
|
||||
|
||||
void inject(AuthorNameFragment fragment);
|
||||
@@ -287,4 +296,14 @@ public interface ActivityComponent {
|
||||
void inject(OwnerReturnShardFragment ownerReturnShardFragment);
|
||||
|
||||
void inject(CustodianReturnShardSuccessFragment custodianReturnShardSuccessFragment);
|
||||
|
||||
void inject(RestoreAccountSetPasswordFragment restoreAccountSetPasswordFragment);
|
||||
|
||||
void inject(RestoreAccountDozeFragment restoreAccountDozeFragment);
|
||||
|
||||
void inject(OwnerReturnShardSuccessFragment ownerReturnShardSuccessFragment);
|
||||
|
||||
void inject(OwnerRecoveryModeErrorFragment ownerRecoveryModeErrorFragment);
|
||||
|
||||
void inject(CustodianReturnShardErrorFragment custodianReturnShardErrorFragment);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import javax.inject.Inject;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID;
|
||||
|
||||
@@ -30,6 +31,7 @@ public class CustodianReturnShardActivity extends BriarActivity
|
||||
private CustodianReturnShardViewModel viewModel;
|
||||
private static final Logger LOG =
|
||||
getLogger(CustodianReturnShardActivity.class.getName());
|
||||
private ContactId contactId;
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
@@ -50,33 +52,61 @@ public class CustodianReturnShardActivity extends BriarActivity
|
||||
Intent intent = getIntent();
|
||||
int id = intent.getIntExtra(CONTACT_ID, -1);
|
||||
if (id == -1) throw new IllegalStateException("No ContactId");
|
||||
ContactId contactId = new ContactId(id);
|
||||
contactId = new ContactId(id);
|
||||
|
||||
try {
|
||||
viewModel.start(contactId);
|
||||
} catch (IOException e) {
|
||||
// TODO improve this
|
||||
Toast.makeText(this,
|
||||
"It looks like you are not connected to a Wifi network",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
// } catch (IOException e) {
|
||||
// // TODO improve this
|
||||
// Toast.makeText(this,
|
||||
// "It looks like you are not connected to a Wifi network",
|
||||
// Toast.LENGTH_SHORT).show();
|
||||
// showInitialFragment(new CustodianReturnShardErrorFragment());
|
||||
} catch (DbException e) {
|
||||
Toast.makeText(this,
|
||||
"You do not hold a backup piece for this contact",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
}
|
||||
showInitialFragment(new CustodianRecoveryModeExplainerFragment());
|
||||
}
|
||||
showInitialFragment(new CustodianRecoveryModeExplainerFragment());
|
||||
|
||||
viewModel.getContinueClicked().observeEvent(this, clicked -> {
|
||||
if (clicked) beginTransfer();
|
||||
});
|
||||
|
||||
viewModel.getShowCameraFragment().observeEvent(this, show -> {
|
||||
if (show) showCameraFragment();
|
||||
});
|
||||
viewModel.getSuccessDismissed().observeEvent(this, dismissed -> {
|
||||
if (dismissed) finish();
|
||||
});
|
||||
viewModel.getErrorTryAgain().observeEvent(this, tryAgain -> {
|
||||
if (tryAgain) beginTransfer();
|
||||
});
|
||||
viewModel.getState()
|
||||
.observe(this, this::onReturnShardStateChanged);
|
||||
}
|
||||
|
||||
private void beginTransfer() {
|
||||
try {
|
||||
viewModel.beginTransfer();
|
||||
} catch (IOException e) {
|
||||
Toast.makeText(this,
|
||||
"It looks like you are not connected to a Wifi network",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
if (fm.findFragmentByTag(CustodianReturnShardFragment.TAG) == null) {
|
||||
BaseFragment f = new CustodianReturnShardErrorFragment();
|
||||
fm.beginTransaction()
|
||||
.replace(R.id.fragmentContainer, f, f.getUniqueTag())
|
||||
.addToBackStack(f.getUniqueTag())
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void onReturnShardStateChanged(CustodianTask.State state) {
|
||||
if (state instanceof CustodianTask.State.Success) {
|
||||
CustodianReturnShardSuccessFragment fragment = new CustodianReturnShardSuccessFragment();
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package org.briarproject.briar.android.socialbackup.recover;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
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.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class CustodianReturnShardErrorFragment extends BaseFragment {
|
||||
public static final String TAG =
|
||||
CustodianReturnShardErrorFragment.class.getName();
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
|
||||
private CustodianReturnShardViewModel viewModel;
|
||||
|
||||
@Override
|
||||
public void injectFragment(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
|
||||
.get(CustodianReturnShardViewModel.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_recovery_custodian_error_explainer,
|
||||
container, false);
|
||||
Button button = view.findViewById(R.id.button);
|
||||
button.setOnClickListener(e -> viewModel.onErrorTryAgain());
|
||||
// TODO cancel button
|
||||
// button.setOnClickListener(e -> viewModel.onErrorCancelled());
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueTag() {
|
||||
return TAG;
|
||||
}
|
||||
}
|
||||
@@ -51,13 +51,14 @@ public class CustodianReturnShardViewModel extends AndroidViewModel
|
||||
private final SocialBackupManager socialBackupManager;
|
||||
private final DatabaseComponent db;
|
||||
final QrCodeDecoder qrCodeDecoder;
|
||||
private boolean wasContinueClicked = false;
|
||||
private boolean qrCodeRead = false;
|
||||
private WifiManager wifiManager;
|
||||
private final MutableLiveEvent<Boolean > continueClicked = new MutableLiveEvent<>();
|
||||
private final MutableLiveEvent<Boolean> showCameraFragment =
|
||||
new MutableLiveEvent<>();
|
||||
private final MutableLiveEvent<Boolean> successDismissed =
|
||||
new MutableLiveEvent<>();
|
||||
private final MutableLiveEvent<Boolean> errorTryAgain = new MutableLiveEvent<>();
|
||||
private final MutableLiveData<CustodianTask.State> state =
|
||||
new MutableLiveData<>();
|
||||
private final CustodianTask task;
|
||||
@@ -110,12 +111,8 @@ public class CustodianReturnShardViewModel extends AndroidViewModel
|
||||
}
|
||||
}
|
||||
|
||||
public void start(ContactId contactId) throws DbException, IOException {
|
||||
InetAddress inetAddress = getWifiIpv4Address();
|
||||
LOG.info("Client InetAddress: " + inetAddress);
|
||||
if (inetAddress == null)
|
||||
throw new IOException("Cannot get IP on local wifi");
|
||||
|
||||
public void start(ContactId contactId) throws DbException {
|
||||
// TODO this should be transactionWithResult
|
||||
db.transaction(false, txn -> {
|
||||
if (!socialBackupManager.amCustodian(txn, contactId)) {
|
||||
throw new DbException();
|
||||
@@ -123,8 +120,6 @@ public class CustodianReturnShardViewModel extends AndroidViewModel
|
||||
returnShardPayloadBytes = socialBackupManager
|
||||
.getReturnShardPayloadBytes(txn, contactId);
|
||||
});
|
||||
task.cancel();
|
||||
task.start(this, returnShardPayloadBytes);
|
||||
}
|
||||
|
||||
@IoExecutor
|
||||
@@ -149,11 +144,31 @@ public class CustodianReturnShardViewModel extends AndroidViewModel
|
||||
}
|
||||
}
|
||||
|
||||
public void beginTransfer() throws IOException {
|
||||
InetAddress inetAddress = getWifiIpv4Address();
|
||||
LOG.info("Client InetAddress: " + inetAddress);
|
||||
if (inetAddress == null)
|
||||
throw new IOException("Cannot get IP on local wifi");
|
||||
|
||||
task.cancel();
|
||||
task.start(this, returnShardPayloadBytes);
|
||||
//TODO camera permissions
|
||||
showCameraFragment.setEvent(true);
|
||||
}
|
||||
@UiThread
|
||||
public void onContinueClicked() {
|
||||
wasContinueClicked = true;
|
||||
continueClicked.setEvent(true);
|
||||
// checkPermissions.setEvent(true);
|
||||
showCameraFragment.setEvent(true);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void onErrorCancelled() {
|
||||
errorTryAgain.postEvent(false);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void onErrorTryAgain() {
|
||||
errorTryAgain.postEvent(true);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@@ -186,4 +201,10 @@ public class CustodianReturnShardViewModel extends AndroidViewModel
|
||||
qrCodeRead = true;
|
||||
}
|
||||
}
|
||||
|
||||
public MutableLiveEvent<Boolean> getErrorTryAgain() {
|
||||
return errorTryAgain;
|
||||
}
|
||||
|
||||
public MutableLiveEvent<Boolean> getContinueClicked() { return continueClicked; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package org.briarproject.briar.android.socialbackup.recover;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
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.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class OwnerRecoveryModeErrorFragment extends BaseFragment {
|
||||
public static final String TAG =
|
||||
OwnerRecoveryModeErrorFragment.class.getName();
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
|
||||
private OwnerReturnShardViewModel viewModel;
|
||||
|
||||
@Override
|
||||
public void injectFragment(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
|
||||
.get(OwnerReturnShardViewModel.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_recovery_custodian_error_explainer,
|
||||
container, false);
|
||||
Button button = view.findViewById(R.id.button);
|
||||
button.setOnClickListener(e -> viewModel.onErrorTryAgain());
|
||||
// TODO cancel button
|
||||
// button.setOnClickListener(e -> viewModel.onErrorCancelled());
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueTag() {
|
||||
return TAG;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.briar.android.socialbackup.recover;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
@@ -23,6 +24,10 @@ import javax.inject.Inject;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@@ -72,6 +77,12 @@ public class OwnerReturnShardActivity extends BaseActivity
|
||||
showNextFragment(new OwnerRecoveryModeMainFragment());
|
||||
}
|
||||
});
|
||||
viewModel.getSuccessDismissed().observeEvent(this, success -> {
|
||||
if (success) onSuccessDismissed();
|
||||
});
|
||||
viewModel.getErrorTryAgain().observeEvent(this, tryAgain -> {
|
||||
if (tryAgain) onBackPressed();
|
||||
});
|
||||
viewModel.getState()
|
||||
.observe(this, this::onReturnShardStateChanged);
|
||||
}
|
||||
@@ -120,6 +131,13 @@ public class OwnerReturnShardActivity extends BaseActivity
|
||||
}
|
||||
}
|
||||
|
||||
private void onSuccessDismissed() {
|
||||
finish();
|
||||
Intent i = new Intent(this, RestoreAccountActivity.class);
|
||||
i.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP |
|
||||
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_TASK_ON_HOME);
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
private void onReturnShardStateChanged(SecretOwnerTask.State state) {
|
||||
if (state instanceof SecretOwnerTask.State.Success) {
|
||||
@@ -131,9 +149,8 @@ public class OwnerReturnShardActivity extends BaseActivity
|
||||
Toast.LENGTH_SHORT).show();
|
||||
if (added && viewModel.canRecover()) {
|
||||
LOG.info("Secret key recovered");
|
||||
int version = 0;
|
||||
try {
|
||||
version = viewModel.recover();
|
||||
viewModel.recover();
|
||||
} catch (GeneralSecurityException e) {
|
||||
LOG.warning("Unable to decrypt backup" + e.toString());
|
||||
Toast.makeText(this,
|
||||
@@ -148,20 +165,16 @@ public class OwnerReturnShardActivity extends BaseActivity
|
||||
Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
Toast.makeText(this,
|
||||
"Account recovered! " + version,
|
||||
Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
showNextFragment(new OwnerReturnShardSuccessFragment());
|
||||
return;
|
||||
}
|
||||
onBackPressed();
|
||||
} else if (state instanceof SecretOwnerTask.State.Failure) {
|
||||
// TODO error screen, handle reason
|
||||
Toast.makeText(this,
|
||||
"Shard return failed!",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
onBackPressed();
|
||||
// showNextFragment(new OwnerRecoveryModeExplainerFragment());
|
||||
// Toast.makeText(this,
|
||||
// "Shard return failed!",
|
||||
// Toast.LENGTH_SHORT).show();
|
||||
// onBackPressed();
|
||||
showNextFragment(new OwnerRecoveryModeErrorFragment());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public class OwnerReturnShardFragment extends BaseFragment
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_keyagreement_qr, container,
|
||||
return inflater.inflate(R.layout.fragment_recovery_owner_qr, container,
|
||||
false);
|
||||
}
|
||||
|
||||
@@ -134,13 +134,13 @@ public class OwnerReturnShardFragment extends BaseFragment
|
||||
private void onReturnShardStateChanged(
|
||||
@Nullable SecretOwnerTask.State state) {
|
||||
if (state instanceof SecretOwnerTask.State.Listening) {
|
||||
status.setText(R.string.waiting_for_contact_to_scan);
|
||||
Bitmap qrCode = viewModel.getQrCodeBitmap();
|
||||
qrCodeView.setQrCode(qrCode);
|
||||
} else if (state instanceof SecretOwnerTask.State.ReceivingShard) {
|
||||
statusView.setVisibility(VISIBLE);
|
||||
status.setText(R.string.connecting_to_device);
|
||||
} else if (state instanceof SecretOwnerTask.State.SendingAck) {
|
||||
status.setText(R.string.waiting_for_contact_to_scan);
|
||||
status.setText(R.string.recovery_sending_ack);
|
||||
} else if (state instanceof SecretOwnerTask.State.Success) {
|
||||
status.setText("Success");
|
||||
} else if (state instanceof SecretOwnerTask.State.Failure) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.briar.android.socialbackup.recover;
|
||||
|
||||
import org.briarproject.briar.android.viewmodel.ViewModelKey;
|
||||
import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount;
|
||||
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import dagger.Binds;
|
||||
@@ -17,4 +18,9 @@ public abstract class OwnerReturnShardModule {
|
||||
abstract ViewModel bindOwnerReturnShardViewModel(
|
||||
OwnerReturnShardViewModel ownerReturnShardViewModel);
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(RestoreAccountViewModel.class)
|
||||
abstract ViewModel bindRestoreAccountViewModel(
|
||||
RestoreAccountViewModel restoreAccountViewModel);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.briarproject.briar.android.socialbackup.recover;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
public class OwnerReturnShardSuccessFragment extends BaseFragment {
|
||||
|
||||
public static final String TAG =
|
||||
OwnerReturnShardSuccessFragment.class.getName();
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
|
||||
private OwnerReturnShardViewModel viewModel;
|
||||
|
||||
@Override
|
||||
public void injectFragment(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
|
||||
.get(OwnerReturnShardViewModel.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_recovery_owner_success,
|
||||
container, false);
|
||||
|
||||
Button button = view.findViewById(R.id.button);
|
||||
button.setOnClickListener(e -> viewModel.onSuccessDismissed());
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueTag() {
|
||||
return TAG;
|
||||
}
|
||||
}
|
||||
@@ -14,19 +14,14 @@ import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.briar.android.contact.add.nearby.QrCodeUtils;
|
||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||
import org.briarproject.briar.api.socialbackup.BackupPayload;
|
||||
import org.briarproject.briar.api.socialbackup.DarkCrystal;
|
||||
import org.briarproject.briar.api.socialbackup.ReturnShardPayload;
|
||||
import org.briarproject.briar.api.socialbackup.Shard;
|
||||
import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount;
|
||||
import org.briarproject.briar.api.socialbackup.recovery.SecretOwnerTask;
|
||||
import org.briarproject.briar.socialbackup.BackupPayloadDecoder;
|
||||
import org.briarproject.briar.socialbackup.SocialBackup;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -51,23 +46,22 @@ class OwnerReturnShardViewModel extends AndroidViewModel
|
||||
@SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19
|
||||
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
||||
|
||||
// private ReturnShardPayload returnShardPayload;
|
||||
|
||||
private final AndroidExecutor androidExecutor;
|
||||
private final Executor ioExecutor;
|
||||
private final SecretOwnerTask task;
|
||||
private final DarkCrystal darkCrystal;
|
||||
private final BackupPayloadDecoder backupPayloadDecoder;
|
||||
private final RestoreAccount restoreAccount;
|
||||
|
||||
private final MutableLiveEvent<Boolean> errorTryAgain =
|
||||
new MutableLiveEvent<>();
|
||||
private final MutableLiveEvent<Boolean> showQrCodeFragment =
|
||||
new MutableLiveEvent<>();
|
||||
private final MutableLiveEvent<Boolean> successDismissed = new MutableLiveEvent<>();
|
||||
private final MutableLiveData<SecretOwnerTask.State> state =
|
||||
new MutableLiveData<>();
|
||||
private final MutableLiveEvent<Boolean> startClicked =
|
||||
new MutableLiveEvent<>();
|
||||
private boolean wasContinueClicked = false;
|
||||
private boolean isActivityResumed = false;
|
||||
private ArrayList<ReturnShardPayload> recoveredShards = new ArrayList<>();
|
||||
private Bitmap qrCodeBitmap;
|
||||
private WifiManager wifiManager;
|
||||
private SecretKey secretKey;
|
||||
@@ -76,14 +70,12 @@ class OwnerReturnShardViewModel extends AndroidViewModel
|
||||
OwnerReturnShardViewModel(Application app,
|
||||
AndroidExecutor androidExecutor,
|
||||
SecretOwnerTask task,
|
||||
DarkCrystal darkCrystal,
|
||||
BackupPayloadDecoder backupPayloadDecoder,
|
||||
RestoreAccount restoreAccount,
|
||||
@IoExecutor Executor ioExecutor) {
|
||||
super(app);
|
||||
this.androidExecutor = androidExecutor;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.backupPayloadDecoder = backupPayloadDecoder;
|
||||
this.darkCrystal = darkCrystal;
|
||||
this.restoreAccount = restoreAccount;
|
||||
this.task = task;
|
||||
wifiManager = (WifiManager) app.getSystemService(WIFI_SERVICE);
|
||||
|
||||
@@ -132,6 +124,11 @@ class OwnerReturnShardViewModel extends AndroidViewModel
|
||||
startShardReturn();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onSuccessDismissed() {
|
||||
successDismissed.setEvent(true);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void startShardReturn() {
|
||||
// If we return to the intro fragment, the continue button needs to be
|
||||
@@ -163,6 +160,10 @@ class OwnerReturnShardViewModel extends AndroidViewModel
|
||||
});
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void onErrorTryAgain() {
|
||||
errorTryAgain.setEvent(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true in onPostResume() and false in onPause(). This prevents the
|
||||
@@ -196,7 +197,7 @@ class OwnerReturnShardViewModel extends AndroidViewModel
|
||||
}
|
||||
|
||||
public int getNumberOfShards() {
|
||||
return recoveredShards.size();
|
||||
return restoreAccount.getNumberOfShards();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -227,38 +228,22 @@ class OwnerReturnShardViewModel extends AndroidViewModel
|
||||
|
||||
// TODO figure out how to actually use a hash set for these objects
|
||||
public boolean addToShardSet(ReturnShardPayload toAdd) {
|
||||
boolean found = false;
|
||||
for (ReturnShardPayload returnShardPayload : recoveredShards) {
|
||||
if (toAdd.equals(returnShardPayload)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) recoveredShards.add(toAdd);
|
||||
return !found;
|
||||
return restoreAccount.addReturnShardPayload(toAdd);
|
||||
}
|
||||
|
||||
public boolean canRecover() {
|
||||
ArrayList<Shard> shards = new ArrayList();
|
||||
for (ReturnShardPayload returnShardPayload : recoveredShards) {
|
||||
// TODO check shards all have same secret id
|
||||
shards.add(returnShardPayload.getShard());
|
||||
}
|
||||
try {
|
||||
secretKey = darkCrystal.combineShards(shards);
|
||||
} catch (GeneralSecurityException e) {
|
||||
// TODO handle error message
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return restoreAccount.canRecover();
|
||||
}
|
||||
|
||||
public int recover() throws FormatException, GeneralSecurityException {
|
||||
if (secretKey == null) throw new GeneralSecurityException();
|
||||
// TODO find backup with highest version number
|
||||
BackupPayload backupPayload = recoveredShards.get(0).getBackupPayload();
|
||||
SocialBackup decodedBackup = backupPayloadDecoder.decodeBackupPayload(secretKey, backupPayload);
|
||||
int version = decodedBackup.getVersion();
|
||||
return version;
|
||||
return restoreAccount.recover();
|
||||
}
|
||||
|
||||
public MutableLiveEvent<Boolean> getSuccessDismissed() {
|
||||
return successDismissed;
|
||||
}
|
||||
|
||||
public MutableLiveEvent<Boolean> getErrorTryAgain() {
|
||||
return errorTryAgain;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package org.briarproject.briar.android.socialbackup.recover;
|
||||
|
||||
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;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
|
||||
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
|
||||
import static org.briarproject.briar.android.socialbackup.recover.RestoreAccountViewModel.State;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class RestoreAccountActivity extends BaseActivity
|
||||
implements BaseFragment.BaseFragmentListener {
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
RestoreAccountViewModel viewModel;
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
|
||||
viewModel = new ViewModelProvider(this, viewModelFactory)
|
||||
.get(RestoreAccountViewModel.class);
|
||||
viewModel.getState().observeEvent(this, this::onStateChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle state) {
|
||||
super.onCreate(state);
|
||||
setContentView(R.layout.activity_fragment_container);
|
||||
}
|
||||
|
||||
private void onStateChanged(RestoreAccountViewModel.State state) {
|
||||
if (state == State.SET_PASSWORD) {
|
||||
showInitialFragment(RestoreAccountSetPasswordFragment.newInstance());
|
||||
} else if (state == State.DOZE) {
|
||||
showDozeFragment();
|
||||
} else if (state == State.CREATED || state == State.FAILED) {
|
||||
// TODO: Show an error if failed
|
||||
showApp();
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(23)
|
||||
void showDozeFragment() {
|
||||
showNextFragment(RestoreAccountDozeFragment.newInstance());
|
||||
}
|
||||
|
||||
void showApp() {
|
||||
Intent i = new Intent(this, ENTRY_ACTIVITY);
|
||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME |
|
||||
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(i);
|
||||
supportFinishAfterTransition();
|
||||
overridePendingTransition(R.anim.screen_new_in, R.anim.screen_old_out);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void runOnDbThread(Runnable runnable) {
|
||||
throw new RuntimeException("Don't use this deprecated method here.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package org.briarproject.briar.android.socialbackup.recover;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
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.account.DozeView;
|
||||
import org.briarproject.briar.android.account.HuaweiView;
|
||||
import org.briarproject.briar.android.account.PowerView;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.util.UiUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import static android.view.View.INVISIBLE;
|
||||
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;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class RestoreAccountDozeFragment extends RestoreAccountFragment
|
||||
implements PowerView.OnCheckedChangedListener {
|
||||
|
||||
private final static String TAG = RestoreAccountDozeFragment.class.getName();
|
||||
|
||||
private DozeView dozeView;
|
||||
private HuaweiView huaweiView;
|
||||
private Button next;
|
||||
private boolean secondAttempt = false;
|
||||
|
||||
public static RestoreAccountDozeFragment newInstance() {
|
||||
return new RestoreAccountDozeFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectFragment(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
requireActivity().setTitle(getString(R.string.setup_doze_title));
|
||||
setHasOptionsMenu(false);
|
||||
View v = inflater.inflate(R.layout.fragment_setup_doze, container,
|
||||
false);
|
||||
dozeView = v.findViewById(R.id.dozeView);
|
||||
dozeView.setOnCheckedChangedListener(this);
|
||||
huaweiView = v.findViewById(R.id.huaweiView);
|
||||
huaweiView.setOnCheckedChangedListener(this);
|
||||
next = v.findViewById(R.id.next);
|
||||
ProgressBar progressBar = v.findViewById(R.id.progress);
|
||||
|
||||
dozeView.setOnButtonClickListener(this::askForDozeWhitelisting);
|
||||
next.setOnClickListener(this);
|
||||
|
||||
viewModel.getIsCreatingAccount()
|
||||
.observe(getViewLifecycleOwner(), isCreatingAccount -> {
|
||||
if (isCreatingAccount) {
|
||||
next.setVisibility(INVISIBLE);
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getHelpText() {
|
||||
return getString(R.string.setup_doze_explanation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int request, int result,
|
||||
@Nullable Intent data) {
|
||||
super.onActivityResult(request, result, data);
|
||||
if (request == REQUEST_DOZE_WHITELISTING) {
|
||||
if (!dozeView.needsToBeShown() || secondAttempt) {
|
||||
dozeView.setChecked(true);
|
||||
} else if (getContext() != null) {
|
||||
secondAttempt = true;
|
||||
showOnboardingDialog(getContext(), getHelpText());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckedChanged() {
|
||||
next.setEnabled(dozeView.isChecked() && huaweiView.isChecked());
|
||||
}
|
||||
|
||||
@SuppressLint("BatteryLife")
|
||||
private void askForDozeWhitelisting() {
|
||||
if (getContext() == null) return;
|
||||
Intent i = UiUtils.getDozeWhitelistingIntent(getContext());
|
||||
startActivityForResult(i, REQUEST_DOZE_WHITELISTING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
viewModel.dozeExceptionConfirmed();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package org.briarproject.briar.android.socialbackup.recover;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
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.fragment.BaseFragment;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_NEXT;
|
||||
import static org.briarproject.briar.android.util.UiUtils.enterPressed;
|
||||
import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
abstract class RestoreAccountFragment extends BaseFragment implements TextWatcher,
|
||||
TextView.OnEditorActionListener, View.OnClickListener {
|
||||
|
||||
private final static String STATE_KEY_CLICKED = "setupFragmentClicked";
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
RestoreAccountViewModel viewModel;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
viewModel = new ViewModelProvider(requireActivity())
|
||||
.get(RestoreAccountViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.help_action, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.action_help) {
|
||||
showOnboardingDialog(getContext(), getHelpText());
|
||||
return true;
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract String getHelpText();
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count,
|
||||
int after) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before,
|
||||
int count) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView textView, int actionId,
|
||||
@Nullable KeyEvent keyEvent) {
|
||||
if (actionId == IME_ACTION_NEXT || actionId == IME_ACTION_DONE ||
|
||||
enterPressed(actionId, keyEvent)) {
|
||||
onClick(textView);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package org.briarproject.briar.android.socialbackup.recover;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
|
||||
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.StrengthMeter;
|
||||
|
||||
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 android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
||||
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
|
||||
import static org.briarproject.briar.android.util.UiUtils.setError;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class RestoreAccountSetPasswordFragment extends RestoreAccountFragment {
|
||||
private final static String TAG = RestoreAccountSetPasswordFragment.class.getName();
|
||||
|
||||
private TextInputLayout passwordEntryWrapper;
|
||||
private TextInputLayout passwordConfirmationWrapper;
|
||||
private TextInputEditText passwordEntry;
|
||||
private TextInputEditText passwordConfirmation;
|
||||
private StrengthMeter strengthMeter;
|
||||
private Button nextButton;
|
||||
|
||||
public static RestoreAccountSetPasswordFragment newInstance() {
|
||||
return new RestoreAccountSetPasswordFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectFragment(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
requireActivity().setTitle(getString(R.string.setup_password_intro));
|
||||
View v = inflater.inflate(R.layout.fragment_setup_password, container,
|
||||
false);
|
||||
|
||||
strengthMeter = v.findViewById(R.id.strength_meter);
|
||||
passwordEntryWrapper = v.findViewById(R.id.password_entry_wrapper);
|
||||
passwordEntry = v.findViewById(R.id.password_entry);
|
||||
passwordConfirmationWrapper =
|
||||
v.findViewById(R.id.password_confirm_wrapper);
|
||||
passwordConfirmation = v.findViewById(R.id.password_confirm);
|
||||
nextButton = v.findViewById(R.id.next);
|
||||
ProgressBar progressBar = v.findViewById(R.id.progress);
|
||||
|
||||
passwordEntry.addTextChangedListener(this);
|
||||
passwordConfirmation.addTextChangedListener(this);
|
||||
nextButton.setOnClickListener(this);
|
||||
|
||||
if (!viewModel.needToShowDozeFragment()) {
|
||||
nextButton.setText(R.string.create_account_button);
|
||||
passwordConfirmation.setImeOptions(IME_ACTION_DONE);
|
||||
}
|
||||
|
||||
viewModel.getIsCreatingAccount()
|
||||
.observe(getViewLifecycleOwner(), isCreatingAccount -> {
|
||||
if (isCreatingAccount) {
|
||||
nextButton.setVisibility(INVISIBLE);
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
// this also avoids the keyboard popping up
|
||||
passwordEntry.setFocusable(false);
|
||||
passwordConfirmation.setFocusable(false);
|
||||
}
|
||||
});
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getHelpText() {
|
||||
return getString(R.string.setup_password_explanation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence authorName, int i, int i1, int i2) {
|
||||
String password1 = passwordEntry.getText().toString();
|
||||
String password2 = passwordConfirmation.getText().toString();
|
||||
boolean passwordsMatch = password1.equals(password2);
|
||||
|
||||
strengthMeter
|
||||
.setVisibility(password1.length() > 0 ? VISIBLE : INVISIBLE);
|
||||
float strength = viewModel.estimatePasswordStrength(password1);
|
||||
strengthMeter.setStrength(strength);
|
||||
boolean strongEnough = strength >= QUITE_WEAK;
|
||||
|
||||
setError(passwordEntryWrapper, getString(R.string.password_too_weak),
|
||||
password1.length() > 0 && !strongEnough);
|
||||
setError(passwordConfirmationWrapper,
|
||||
getString(R.string.passwords_do_not_match),
|
||||
password2.length() > 0 && !passwordsMatch);
|
||||
|
||||
boolean enabled = passwordsMatch && strongEnough;
|
||||
nextButton.setEnabled(enabled);
|
||||
passwordConfirmation.setOnEditorActionListener(enabled ? this : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
IBinder token = passwordEntry.getWindowToken();
|
||||
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
|
||||
((InputMethodManager) o).hideSoftInputFromWindow(token, 0);
|
||||
viewModel.setPassword(passwordEntry.getText().toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package org.briarproject.briar.android.socialbackup.recover;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import org.briarproject.bramble.api.account.AccountManager;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.identity.Identity;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.android.account.DozeHelper;
|
||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||
import org.briarproject.briar.api.socialbackup.SocialBackup;
|
||||
import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
class RestoreAccountViewModel extends AndroidViewModel {
|
||||
|
||||
enum State {SET_PASSWORD, DOZE, CREATED, FAILED}
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(RestoreAccountViewModel.class.getName());
|
||||
|
||||
@Nullable
|
||||
private String password;
|
||||
private final MutableLiveEvent<State>
|
||||
state = new MutableLiveEvent<>();
|
||||
private final MutableLiveData<Boolean> isCreatingAccount =
|
||||
new MutableLiveData<>(false);
|
||||
|
||||
private final AccountManager accountManager;
|
||||
private final ContactManager contactManager;
|
||||
private final Executor ioExecutor;
|
||||
private final PasswordStrengthEstimator strengthEstimator;
|
||||
private final DozeHelper dozeHelper;
|
||||
private final RestoreAccount restoreAccount;
|
||||
|
||||
@Inject
|
||||
RestoreAccountViewModel(Application app,
|
||||
AccountManager accountManager,
|
||||
ContactManager contactManager,
|
||||
RestoreAccount restoreAccount,
|
||||
@IoExecutor Executor ioExecutor,
|
||||
PasswordStrengthEstimator strengthEstimator,
|
||||
DozeHelper dozeHelper) {
|
||||
super(app);
|
||||
this.accountManager = accountManager;
|
||||
this.contactManager = contactManager;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.strengthEstimator = strengthEstimator;
|
||||
this.dozeHelper = dozeHelper;
|
||||
this.restoreAccount = restoreAccount;
|
||||
|
||||
ioExecutor.execute(() -> {
|
||||
if (accountManager.accountExists()) {
|
||||
throw new AssertionError();
|
||||
} else {
|
||||
state.postEvent(State.SET_PASSWORD);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
LiveEvent<State> getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
LiveData<Boolean> getIsCreatingAccount() {
|
||||
return isCreatingAccount;
|
||||
}
|
||||
|
||||
void setPassword(String password) {
|
||||
this.password = password;
|
||||
if (needToShowDozeFragment()) {
|
||||
state.setEvent(State.DOZE);
|
||||
} else {
|
||||
createAccount();
|
||||
}
|
||||
}
|
||||
|
||||
float estimatePasswordStrength(String password) {
|
||||
return strengthEstimator.estimateStrength(password);
|
||||
}
|
||||
|
||||
boolean needToShowDozeFragment() {
|
||||
return dozeHelper.needToShowDozeFragment(getApplication());
|
||||
}
|
||||
|
||||
void dozeExceptionConfirmed() {
|
||||
createAccount();
|
||||
}
|
||||
|
||||
private void createAccount() {
|
||||
if (password == null) throw new IllegalStateException();
|
||||
isCreatingAccount.setValue(true);
|
||||
SocialBackup socialBackup = restoreAccount.getSocialBackup();
|
||||
if (socialBackup == null) {
|
||||
LOG.warning("Cannot retrieve social backup");
|
||||
state.postEvent(State.FAILED);
|
||||
}
|
||||
Identity identity = socialBackup.getIdentity();
|
||||
ioExecutor.execute(() -> {
|
||||
if (accountManager.restoreAccount(identity, password)) {
|
||||
LOG.info("Restored account");
|
||||
try {
|
||||
restoreAccount.addContactsToDb();
|
||||
} catch (DbException e) {
|
||||
LOG.warning("Cannot retrieve social backup");
|
||||
state.postEvent(State.FAILED);
|
||||
}
|
||||
state.postEvent(State.CREATED);
|
||||
} else {
|
||||
LOG.warning("Failed to create account");
|
||||
state.postEvent(State.FAILED);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="409dp"
|
||||
android:height="162dp"
|
||||
android:viewportHeight="161.7"
|
||||
android:viewportWidth="409.2">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M369.8,157.4l-4.3,-4.3l-7.1,-2.4c-3.9,-1.3 -8.7,-3 -10.7,-3.7l-3.7,-1.3l3.5,-0.2c8.2,-0.4 13,-4 14.3,-10.9c0.8,-4.1 1.1,-17.3 0.8,-33c-0.2,-8.1 -0.2,-15.4 0,-16.3c0.1,-0.9 0.5,-2.4 0.9,-3.4c1.2,-3.5 0.3,-11.9 -1.9,-17.6c-0.3,-0.9 -1.9,-4.2 -3.5,-7.4c-4.2,-8.2 -4.5,-8.9 -4.9,-10.5c-0.5,-1.8 -0.2,-5.4 0.5,-6.8c0.7,-1.3 2.2,-2.9 3.2,-3.5c1.3,-0.7 2.6,0.1 4.7,2.9c3.4,4.5 14,19.4 15.7,22.2c3.7,6 6,11.2 8,18.8c0.7,2.5 1.9,7 2.7,10.1c0.8,3.1 2.7,10.2 4.1,15.8l2.6,10.2l4.6,5.2c2.6,2.9 5.8,6.5 7.2,8c1.4,1.6 2.5,3 2.5,3.2c0,0.3 -34.5,29.3 -34.9,29.3C374.2,161.7 372.2,159.7 369.8,157.4zM275.9,141c-1.3,-0.6 -2.2,-1.4 -2.9,-2.3c-2.1,-2.7 -2,2.4 -1.9,-68.5l0.1,-64l0.7,-1.2c1,-1.9 2,-2.9 3.7,-3.9l1.6,-0.9l37.8,-0.1c42.5,-0.1 39.4,-0.2 42.1,2.2c0.9,0.8 1.8,2 2.2,2.9c0.7,1.6 0.7,1.6 0.8,14.2l0.1,12.6l-1.8,-0.1c-1.4,-0.1 -2.1,0 -3.2,0.5c-2,1 -3.9,2.9 -5.1,5.1l-1,2l0,-12.8l0,-12.8h-33.6h-33.6v51.3v51.3h33.6h33.6l0.1,-34.4c0.1,-33 0.1,-34.4 0.6,-32.9c0.3,0.8 1.8,4 3.4,7c5.5,10.6 5.4,9.9 5.4,47.2c0,27.6 -0.1,30 -1.7,33.1c-1.1,2.2 -2.7,3.7 -5.1,4.7l-1.7,0.7L314,141.8l-36.2,0.1L275.9,141L275.9,141zM318.3,135.9c2.9,-1.3 4.5,-3.7 4.4,-6.6c0,-4.1 -3.1,-7.2 -7.1,-7.2c-2.1,0 -3.6,0.6 -5.2,2.2c-2.2,2.2 -2.8,5.4 -1.3,8.3c0.7,1.4 2.5,3 4,3.5C314.6,136.6 317,136.6 318.3,135.9z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M39.4,157.4l4.3,-4.3l7.1,-2.4c3.9,-1.3 8.7,-3 10.7,-3.7l3.7,-1.3l-3.5,-0.2c-8.2,-0.4 -13,-4 -14.3,-10.9c-0.8,-4.1 -1.1,-17.3 -0.8,-33c0.2,-8.1 0.2,-15.4 0,-16.3c-0.1,-0.9 -0.5,-2.4 -0.9,-3.4c-1.2,-3.5 -0.3,-11.9 1.9,-17.6c0.3,-0.9 1.9,-4.2 3.5,-7.4c4.2,-8.2 4.5,-8.9 4.9,-10.5c0.5,-1.8 0.2,-5.4 -0.5,-6.8c-0.7,-1.3 -2.2,-2.9 -3.2,-3.5c-1.3,-0.7 -2.6,0.1 -4.7,2.9c-3.4,4.5 -14,19.4 -15.7,22.2c-3.7,6 -6,11.2 -8,18.8c-0.7,2.5 -1.9,7 -2.7,10.1c-0.8,3.1 -2.7,10.2 -4.1,15.8l-2.6,10.2l-4.6,5.2c-2.6,2.9 -5.8,6.5 -7.2,8s-2.5,3 -2.5,3.2c0,0.3 34.5,29.3 34.9,29.3C35,161.7 37.1,159.7 39.4,157.4zM133.3,141c1.3,-0.6 2.2,-1.4 2.9,-2.3c2.1,-2.7 2,2.4 1.9,-68.5l-0.1,-64l-0.7,-1.2c-1,-1.9 -2,-2.9 -3.7,-3.9l-1.6,-0.9l-37.8,-0.1c-42.5,-0.1 -39.4,-0.2 -42.1,2.2c-0.9,0.8 -1.8,2 -2.2,2.9c-0.7,1.6 -0.7,1.6 -0.8,14.2L49,32l1.8,-0.1c1.4,-0.1 2.1,0 3.2,0.5c2,1 3.9,2.9 5.1,5.1l1,2l0,-12.8l0,-12.8h33.6h33.6v51.3v51.3L93.8,116.5L60.2,116.5l-0.1,-34.4c-0.1,-33 -0.1,-34.4 -0.6,-32.9c-0.3,0.8 -1.8,4 -3.4,7c-5.5,10.6 -5.4,9.9 -5.4,47.2c0,27.6 0.1,30 1.7,33.1c1.1,2.2 2.7,3.7 5.1,4.7l1.7,0.7l36.2,0.1l36.2,0.1L133.3,141L133.3,141zM90.9,135.9c-2.9,-1.3 -4.5,-3.7 -4.4,-6.6c0,-4.1 3.1,-7.2 7.1,-7.2c2.1,0 3.6,0.6 5.2,2.2c2.2,2.2 2.8,5.4 1.3,8.3c-0.7,1.4 -2.5,3 -4,3.5C94.6,136.6 92.3,136.6 90.9,135.9z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M80.5,63h2.3v2.3h2.3v2.3L73.6,67.6v-2.3h2.3v-4.6h4.6L80.5,63L80.5,63zM110.5,83.8h2.3v-2.3h-2.3L110.5,83.8zM82.8,63h2.3v-2.3h-2.3L82.8,63zM115.1,83.8h2.3v-2.3h-2.3L115.1,83.8zM87.4,86.1L92,86.1L92,83.8h-4.6L87.4,86.1zM108.2,86.1L108.2,83.8h-2.3v2.3L108.2,86.1zM99,86.1h2.3v-4.6L99,81.5L99,86.1zM80.5,56.1v2.3h6.9v-2.3L80.5,56.1zM78.2,58.4v-2.3h-4.6v4.6h2.3v-2.3L78.2,58.4zM85.1,53.8L69,53.8v-16.1h16.1L85.1,53.8zM82.8,40L71.3,40v11.5h11.5L82.8,40zM73.6,81.5h6.9v-6.9h-6.9L73.6,81.5zM96.6,79.1v2.3L99,81.4v-2.3L96.6,79.1zM80.5,42.3h-6.9v6.9h6.9L80.5,42.3zM117.4,37.7L117.4,53.8L101.3,53.8v-16.1L117.4,37.7zM115.1,40L103.6,40v11.5h11.5L115.1,40zM69,69.9h16.1v16.1L69,86L69,69.9zM71.3,83.8h11.5v-11.5L71.3,72.3L71.3,83.8zM71.3,56.1L69,56.1v11.5h2.3L71.3,56.1zM101.3,67.6v2.3h2.3v-2.3L101.3,67.6zM94.3,76.9v-2.3L92,74.6v2.3h-4.6v4.6L92,81.5v2.3h2.3v-4.6h2.3v-2.3L94.3,76.9zM87.4,46.9L92,46.9v-2.3h-4.6L87.4,46.9zM105.9,65.3h4.6v2.3h2.3v-6.9h-2.3v-4.6h-2.3v6.9h-6.9v2.3h2.3v2.3h2.3L105.9,65.3zM108.2,72.2h-2.3v-2.3h-2.3v4.6h-6.9v2.3h4.6v4.6h2.3v2.3h2.3v-4.6h9.2v-2.3h-6.9L108.2,72.2zM108.2,72.2h2.3v-4.6h-2.3L108.2,72.2zM89.7,72.2v-2.3L92,69.9v-2.3h2.3v-2.3h2.3v-4.6h6.9v-4.6h-2.3v2.3L99,58.4v-9.2h-2.3v-4.6L99,44.6v-6.9h-2.3v4.6h-2.3v-4.6h-6.9v4.6h2.3v-2.3L92,40v4.6h2.3v6.9h2.3v2.3h-2.3v4.6L92,58.4L92,53.8h-2.3v-2.3h-2.3v4.6h2.3v2.3h-2.3v6.9h2.3v-4.6L92,60.7v4.6h-2.3v2.3h-2.3v6.9L92,74.5v-2.3L89.7,72.2zM115.1,74.5v-2.3h-4.6v2.3L115.1,74.5zM112.8,42.3h-6.9v6.9h6.9L112.8,42.3zM94.3,72.2L99,72.2v-2.3h-2.3v-2.3h-2.3L94.4,72.2zM99,67.6v-2.3h-2.3v2.3L99,67.6zM112.8,58.4h4.6v-2.3h-4.6L112.8,58.4zM115.1,76.9h2.3v-2.3h-2.3L115.1,76.9zM115.1,63h2.3v-2.3h-2.3L115.1,63zM94.3,51.5L92,51.5v2.3h2.3L94.3,51.5zM94.3,51.5"/>
|
||||
<!-- <path-->
|
||||
<!-- android:fillColor="#FF000000"-->
|
||||
<!-- android:pathData="M303.5,63h2.3v2.3h2.3v2.3h-11.5v-2.3h2.3v-4.6h4.6L303.5,63L303.5,63zM333.5,83.8h2.3v-2.3h-2.3L333.5,83.8zM305.8,63h2.3v-2.3h-2.3L305.8,63zM338.1,83.8h2.3v-2.3h-2.3L338.1,83.8zM310.4,86.1h4.6L315,83.8h-4.6L310.4,86.1zM331.2,86.1L331.2,83.8h-2.3v2.3L331.2,86.1zM322,86.1h2.3v-4.6L322,81.5L322,86.1zM303.5,56.1v2.3h6.9v-2.3L303.5,56.1zM301.2,58.4v-2.3h-4.6v4.6h2.3v-2.3L301.2,58.4zM308.1,53.8L292,53.8v-16.1h16.1L308.1,53.8zM305.8,40h-11.5v11.5h11.5L305.8,40zM296.6,81.5h6.9v-6.9h-6.9L296.6,81.5zM319.6,79.1v2.3h2.3v-2.3L319.6,79.1zM303.5,42.3h-6.9v6.9h6.9L303.5,42.3zM340.4,37.7L340.4,53.8h-16.1v-16.1L340.4,37.7zM338.1,40h-11.5v11.5h11.5L338.1,40zM292,69.9h16.1v16.1L292,86L292,69.9zM294.3,83.8h11.5v-11.5h-11.5L294.3,83.8zM294.3,56.1L292,56.1v11.5h2.3L294.3,56.1zM324.3,67.6v2.3h2.3v-2.3L324.3,67.6zM317.3,76.9v-2.3L315,74.6v2.3h-4.6v4.6h4.6v2.3h2.3v-4.6h2.3v-2.3L317.3,76.9zM310.4,46.9h4.6v-2.3h-4.6L310.4,46.9zM328.9,65.3h4.6v2.3h2.3v-6.9h-2.3v-4.6h-2.3v6.9h-6.9v2.3h2.3v2.3h2.3L328.9,65.3zM331.2,72.2h-2.3v-2.3h-2.3v4.6h-6.9v2.3h4.6v4.6h2.3v2.3h2.3v-4.6h9.2v-2.3h-6.9L331.2,72.2zM331.2,72.2h2.3v-4.6h-2.3L331.2,72.2zM312.7,72.2v-2.3h2.3v-2.3h2.3v-2.3h2.3v-4.6h6.9v-4.6h-2.3v2.3L322,58.4v-9.2h-2.3v-4.6h2.3v-6.9h-2.3v4.6h-2.3v-4.6h-6.9v4.6h2.3v-2.3h2.3v4.6h2.3v6.9h2.3v2.3h-2.3v4.6L315,58.4L315,53.8h-2.3v-2.3h-2.3v4.6h2.3v2.3h-2.3v6.9h2.3v-4.6h2.3v4.6h-2.3v2.3h-2.3v6.9h4.6v-2.3L312.7,72.2zM338.1,74.5v-2.3h-4.6v2.3L338.1,74.5zM335.8,42.3h-6.9v6.9h6.9L335.8,42.3zM317.3,72.2h4.6v-2.3h-2.3v-2.3h-2.3L317.3,72.2zM322,67.6v-2.3h-2.3v2.3L322,67.6zM335.8,58.4h4.6v-2.3h-4.6L335.8,58.4zM338.1,76.9h2.3v-2.3h-2.3L338.1,76.9zM338.1,63h2.3v-2.3h-2.3L338.1,63zM317.3,51.5L315,51.5v2.3h2.3L317.3,51.5zM317.3,51.5"/>-->
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M179.6,48.9l-20.6,18l20.6,16.7v-5.2L199,78.4v-24.3h-19.3L179.7,48.9z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M229.4,83.7l20.6,-18l-20.6,-16.7v5.2L210,54.2v24.3h19.3L229.3,83.7z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="409.2dp"
|
||||
android:height="161.7dp"
|
||||
android:viewportHeight="161.7"
|
||||
android:viewportWidth="409.2">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M369.8,157.4l-4.3,-4.3l-7.1,-2.4c-3.9,-1.3 -8.7,-3 -10.7,-3.7l-3.7,-1.3l3.5,-0.2c8.2,-0.4 13,-4 14.3,-10.9c0.8,-4.1 1.1,-17.3 0.8,-33c-0.2,-8.1 -0.2,-15.4 0,-16.3c0.1,-0.9 0.5,-2.4 0.9,-3.4c1.2,-3.5 0.3,-11.9 -1.9,-17.6c-0.3,-0.9 -1.9,-4.2 -3.5,-7.4c-4.2,-8.2 -4.5,-8.9 -4.9,-10.5c-0.5,-1.8 -0.2,-5.4 0.5,-6.8c0.7,-1.3 2.2,-2.9 3.2,-3.5c1.3,-0.7 2.6,0.1 4.7,2.9c3.4,4.5 14,19.4 15.7,22.2c3.7,6 6,11.2 8,18.8c0.7,2.5 1.9,7 2.7,10.1c0.8,3.1 2.7,10.2 4.1,15.8l2.6,10.2l4.6,5.2c2.6,2.9 5.8,6.5 7.2,8c1.4,1.6 2.5,3 2.5,3.2c0,0.3 -34.5,29.3 -34.9,29.3C374.2,161.7 372.2,159.7 369.8,157.4zM275.9,141c-1.3,-0.6 -2.2,-1.4 -2.9,-2.3c-2.1,-2.7 -2,2.4 -1.9,-68.5l0.1,-64l0.7,-1.2c1,-1.9 2,-2.9 3.7,-3.9l1.6,-0.9l37.8,-0.1c42.5,-0.1 39.4,-0.2 42.1,2.2c0.9,0.8 1.8,2 2.2,2.9c0.7,1.6 0.7,1.6 0.8,14.2l0.1,12.6l-1.8,-0.1c-1.4,-0.1 -2.1,0 -3.2,0.5c-2,1 -3.9,2.9 -5.1,5.1l-1,2l0,-12.8l0,-12.8h-33.6h-33.6v51.3v51.3h33.6h33.6l0.1,-34.4c0.1,-33 0.1,-34.4 0.6,-32.9c0.3,0.8 1.8,4 3.4,7c5.5,10.6 5.4,9.9 5.4,47.2c0,27.6 -0.1,30 -1.7,33.1c-1.1,2.2 -2.7,3.7 -5.1,4.7l-1.7,0.7L314,141.8l-36.2,0.1L275.9,141L275.9,141zM318.3,135.9c2.9,-1.3 4.5,-3.7 4.4,-6.6c0,-4.1 -3.1,-7.2 -7.1,-7.2c-2.1,0 -3.6,0.6 -5.2,2.2c-2.2,2.2 -2.8,5.4 -1.3,8.3c0.7,1.4 2.5,3 4,3.5C314.6,136.6 317,136.6 318.3,135.9z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M39.4,157.4l4.3,-4.3l7.1,-2.4c3.9,-1.3 8.7,-3 10.7,-3.7l3.7,-1.3l-3.5,-0.2c-8.2,-0.4 -13,-4 -14.3,-10.9c-0.8,-4.1 -1.1,-17.3 -0.8,-33c0.2,-8.1 0.2,-15.4 0,-16.3c-0.1,-0.9 -0.5,-2.4 -0.9,-3.4c-1.2,-3.5 -0.3,-11.9 1.9,-17.6c0.3,-0.9 1.9,-4.2 3.5,-7.4c4.2,-8.2 4.5,-8.9 4.9,-10.5c0.5,-1.8 0.2,-5.4 -0.5,-6.8c-0.7,-1.3 -2.2,-2.9 -3.2,-3.5c-1.3,-0.7 -2.6,0.1 -4.7,2.9c-3.4,4.5 -14,19.4 -15.7,22.2c-3.7,6 -6,11.2 -8,18.8c-0.7,2.5 -1.9,7 -2.7,10.1c-0.8,3.1 -2.7,10.2 -4.1,15.8l-2.6,10.2l-4.6,5.2c-2.6,2.9 -5.8,6.5 -7.2,8s-2.5,3 -2.5,3.2c0,0.3 34.5,29.3 34.9,29.3C35,161.7 37.1,159.7 39.4,157.4zM133.3,141c1.3,-0.6 2.2,-1.4 2.9,-2.3c2.1,-2.7 2,2.4 1.9,-68.5l-0.1,-64l-0.7,-1.2c-1,-1.9 -2,-2.9 -3.7,-3.9l-1.6,-0.9l-37.8,-0.1c-42.5,-0.1 -39.4,-0.2 -42.1,2.2c-0.9,0.8 -1.8,2 -2.2,2.9c-0.7,1.6 -0.7,1.6 -0.8,14.2L49,32l1.8,-0.1c1.4,-0.1 2.1,0 3.2,0.5c2,1 3.9,2.9 5.1,5.1l1,2l0,-12.8l0,-12.8h33.6h33.6v51.3v51.3L93.8,116.5L60.2,116.5l-0.1,-34.4c-0.1,-33 -0.1,-34.4 -0.6,-32.9c-0.3,0.8 -1.8,4 -3.4,7c-5.5,10.6 -5.4,9.9 -5.4,47.2c0,27.6 0.1,30 1.7,33.1c1.1,2.2 2.7,3.7 5.1,4.7l1.7,0.7l36.2,0.1l36.2,0.1L133.3,141L133.3,141zM90.9,135.9c-2.9,-1.3 -4.5,-3.7 -4.4,-6.6c0,-4.1 3.1,-7.2 7.1,-7.2c2.1,0 3.6,0.6 5.2,2.2c2.2,2.2 2.8,5.4 1.3,8.3c-0.7,1.4 -2.5,3 -4,3.5C94.6,136.6 92.3,136.6 90.9,135.9z"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M80.5,63h2.3v2.3h2.3v2.3L73.6,67.6v-2.3h2.3v-4.6h4.6L80.5,63L80.5,63zM110.5,83.8h2.3v-2.3h-2.3L110.5,83.8zM82.8,63h2.3v-2.3h-2.3L82.8,63zM115.1,83.8h2.3v-2.3h-2.3L115.1,83.8zM87.4,86.1L92,86.1L92,83.8h-4.6L87.4,86.1zM108.2,86.1L108.2,83.8h-2.3v2.3L108.2,86.1zM99,86.1h2.3v-4.6L99,81.5L99,86.1zM80.5,56.1v2.3h6.9v-2.3L80.5,56.1zM78.2,58.4v-2.3h-4.6v4.6h2.3v-2.3L78.2,58.4zM85.1,53.8L69,53.8v-16.1h16.1L85.1,53.8zM82.8,40L71.3,40v11.5h11.5L82.8,40zM73.6,81.5h6.9v-6.9h-6.9L73.6,81.5zM96.6,79.1v2.3L99,81.4v-2.3L96.6,79.1zM80.5,42.3h-6.9v6.9h6.9L80.5,42.3zM117.4,37.7L117.4,53.8L101.3,53.8v-16.1L117.4,37.7zM115.1,40L103.6,40v11.5h11.5L115.1,40zM69,69.9h16.1v16.1L69,86L69,69.9zM71.3,83.8h11.5v-11.5L71.3,72.3L71.3,83.8zM71.3,56.1L69,56.1v11.5h2.3L71.3,56.1zM101.3,67.6v2.3h2.3v-2.3L101.3,67.6zM94.3,76.9v-2.3L92,74.6v2.3h-4.6v4.6L92,81.5v2.3h2.3v-4.6h2.3v-2.3L94.3,76.9zM87.4,46.9L92,46.9v-2.3h-4.6L87.4,46.9zM105.9,65.3h4.6v2.3h2.3v-6.9h-2.3v-4.6h-2.3v6.9h-6.9v2.3h2.3v2.3h2.3L105.9,65.3zM108.2,72.2h-2.3v-2.3h-2.3v4.6h-6.9v2.3h4.6v4.6h2.3v2.3h2.3v-4.6h9.2v-2.3h-6.9L108.2,72.2zM108.2,72.2h2.3v-4.6h-2.3L108.2,72.2zM89.7,72.2v-2.3L92,69.9v-2.3h2.3v-2.3h2.3v-4.6h6.9v-4.6h-2.3v2.3L99,58.4v-9.2h-2.3v-4.6L99,44.6v-6.9h-2.3v4.6h-2.3v-4.6h-6.9v4.6h2.3v-2.3L92,40v4.6h2.3v6.9h2.3v2.3h-2.3v4.6L92,58.4L92,53.8h-2.3v-2.3h-2.3v4.6h2.3v2.3h-2.3v6.9h2.3v-4.6L92,60.7v4.6h-2.3v2.3h-2.3v6.9L92,74.5v-2.3L89.7,72.2zM115.1,74.5v-2.3h-4.6v2.3L115.1,74.5zM112.8,42.3h-6.9v6.9h6.9L112.8,42.3zM94.3,72.2L99,72.2v-2.3h-2.3v-2.3h-2.3L94.4,72.2zM99,67.6v-2.3h-2.3v2.3L99,67.6zM112.8,58.4h4.6v-2.3h-4.6L112.8,58.4zM115.1,76.9h2.3v-2.3h-2.3L115.1,76.9zM115.1,63h2.3v-2.3h-2.3L115.1,63zM94.3,51.5L92,51.5v2.3h2.3L94.3,51.5zM94.3,51.5"/>
|
||||
<!-- <path-->
|
||||
<!-- android:fillColor="#FF000000"-->
|
||||
<!-- android:pathData="M303.5,63h2.3v2.3h2.3v2.3h-11.5v-2.3h2.3v-4.6h4.6L303.5,63L303.5,63zM333.5,83.8h2.3v-2.3h-2.3L333.5,83.8zM305.8,63h2.3v-2.3h-2.3L305.8,63zM338.1,83.8h2.3v-2.3h-2.3L338.1,83.8zM310.4,86.1h4.6L315,83.8h-4.6L310.4,86.1zM331.2,86.1L331.2,83.8h-2.3v2.3L331.2,86.1zM322,86.1h2.3v-4.6L322,81.5L322,86.1zM303.5,56.1v2.3h6.9v-2.3L303.5,56.1zM301.2,58.4v-2.3h-4.6v4.6h2.3v-2.3L301.2,58.4zM308.1,53.8L292,53.8v-16.1h16.1L308.1,53.8zM305.8,40h-11.5v11.5h11.5L305.8,40zM296.6,81.5h6.9v-6.9h-6.9L296.6,81.5zM319.6,79.1v2.3h2.3v-2.3L319.6,79.1zM303.5,42.3h-6.9v6.9h6.9L303.5,42.3zM340.4,37.7L340.4,53.8h-16.1v-16.1L340.4,37.7zM338.1,40h-11.5v11.5h11.5L338.1,40zM292,69.9h16.1v16.1L292,86L292,69.9zM294.3,83.8h11.5v-11.5h-11.5L294.3,83.8zM294.3,56.1L292,56.1v11.5h2.3L294.3,56.1zM324.3,67.6v2.3h2.3v-2.3L324.3,67.6zM317.3,76.9v-2.3L315,74.6v2.3h-4.6v4.6h4.6v2.3h2.3v-4.6h2.3v-2.3L317.3,76.9zM310.4,46.9h4.6v-2.3h-4.6L310.4,46.9zM328.9,65.3h4.6v2.3h2.3v-6.9h-2.3v-4.6h-2.3v6.9h-6.9v2.3h2.3v2.3h2.3L328.9,65.3zM331.2,72.2h-2.3v-2.3h-2.3v4.6h-6.9v2.3h4.6v4.6h2.3v2.3h2.3v-4.6h9.2v-2.3h-6.9L331.2,72.2zM331.2,72.2h2.3v-4.6h-2.3L331.2,72.2zM312.7,72.2v-2.3h2.3v-2.3h2.3v-2.3h2.3v-4.6h6.9v-4.6h-2.3v2.3L322,58.4v-9.2h-2.3v-4.6h2.3v-6.9h-2.3v4.6h-2.3v-4.6h-6.9v4.6h2.3v-2.3h2.3v4.6h2.3v6.9h2.3v2.3h-2.3v4.6L315,58.4L315,53.8h-2.3v-2.3h-2.3v4.6h2.3v2.3h-2.3v6.9h2.3v-4.6h2.3v4.6h-2.3v2.3h-2.3v6.9h4.6v-2.3L312.7,72.2zM338.1,74.5v-2.3h-4.6v2.3L338.1,74.5zM335.8,42.3h-6.9v6.9h6.9L335.8,42.3zM317.3,72.2h4.6v-2.3h-2.3v-2.3h-2.3L317.3,72.2zM322,67.6v-2.3h-2.3v2.3L322,67.6zM335.8,58.4h4.6v-2.3h-4.6L335.8,58.4zM338.1,76.9h2.3v-2.3h-2.3L338.1,76.9zM338.1,63h2.3v-2.3h-2.3L338.1,63zM317.3,51.5L315,51.5v2.3h2.3L317.3,51.5zM317.3,51.5"/>-->
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M 207.61295,88.782357 h -6.42588 v -12.85177 h 6.42588 m 0,25.703533 h -6.42588 v -6.425883 h 6.42588 m -38.5553,16.064713 h 70.68472 L 204.40001,50.227047 Z"
|
||||
android:strokeWidth="3.21294165"/>
|
||||
</vector>
|
||||
@@ -31,7 +31,7 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView1"
|
||||
app:srcCompat="@drawable/qr_code_error" />
|
||||
app:srcCompat="@drawable/qr_code_social_backup_error" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView2"
|
||||
|
||||
@@ -31,7 +31,20 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView"
|
||||
app:srcCompat="@drawable/qr_code_intro" />
|
||||
app:srcCompat="@drawable/qr_code_social_backup" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewExplain"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/custodian_recovery_explainer_extra"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView"
|
||||
tools:layout_editor_absoluteX="16dp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
@@ -42,6 +55,6 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/textViewExplain" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -31,7 +31,21 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView"
|
||||
app:srcCompat="@drawable/qr_code_intro" />
|
||||
app:srcCompat="@drawable/qr_code_social_backup" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewExplain"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/recovery_explainer_extra"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView"
|
||||
tools:layout_editor_absoluteX="16dp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/beginButton"
|
||||
@@ -42,6 +56,6 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/textViewExplain" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:keepScreenOn="true">
|
||||
|
||||
<!-- <org.briarproject.briar.android.contact.add.nearby.CameraView-->
|
||||
<!-- android:id="@+id/camera_view"-->
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="match_parent" />-->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/camera_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:baselineAligned="false"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/status_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/margin_medium"
|
||||
android:visibility="visible"
|
||||
tools:visibility="visible">
|
||||
|
||||
<!-- <ProgressBar-->
|
||||
<!-- style="?android:attr/progressBarStyleLarge"-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="wrap_content" />-->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/connect_status"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:paddingTop="@dimen/margin_large"
|
||||
android:textSize="20sp"
|
||||
android:text="@string/waiting_for_contact_to_scan" />
|
||||
</LinearLayout>
|
||||
|
||||
<org.briarproject.briar.android.view.QrCodeView
|
||||
android:id="@+id/qr_code_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@android:color/white"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/linearLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="@dimen/margin_large"
|
||||
android:paddingTop="@dimen/margin_medium"
|
||||
android:paddingRight="@dimen/margin_large"
|
||||
android:paddingBottom="@dimen/margin_medium">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/recovery_account_recovered"
|
||||
android:textSize="30sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginLeft="32dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginRight="32dp"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView1"
|
||||
app:srcCompat="@drawable/ic_baseline_done_outline_24" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/continue_button"
|
||||
android:textSize="24sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView2" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/recovery_account_recovered_explain"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView2" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -676,19 +676,23 @@
|
||||
<!-- recovery from the secret owner's POV -->
|
||||
|
||||
<string name="recovery_explainer">You need to meet your trusted contacts in-person to receive pieces</string>
|
||||
<string name="recovery_explainer_extra">Your trusted contact must scan a QR code to initiate the transfer.\n\nYou must both be connected to the same wifi network</string>
|
||||
<string name="recovery_begin">Begin</string>
|
||||
<string name="recovery_failed_to_receive">Failed to receive backup piece</string>
|
||||
<string name="recovery_helpful_suggestions">Please check that bluetooth is swtiched on and that no-one but your trusted contact is able to scan the QR code</string>
|
||||
<string name="recovery_helpful_suggestions">Please check that you are both connected to the same wifi network and try again</string>
|
||||
<string name="recovery_retry">Retry</string>
|
||||
<string name="recovery_recovered_shards">Recovered backup pieces:</string>
|
||||
<string name="recovery_scan_qr_code">Show QR code</string>
|
||||
<string name="recovery_recovering_account">Recovering account…</string>
|
||||
<string name="recovery_shard_received">Account backup piece received</string>
|
||||
<string name="recovery_account_recovered">Account recovered</string>
|
||||
<string name="recovery_account_recovered_explain">You must now set a new password for your account</string>
|
||||
<string name="recovery_sending_ack">Sending acknowledgement</string>
|
||||
|
||||
<!-- recovery from the custodian's POV -->
|
||||
|
||||
<string name="custodian_recovery_explainer">You need to meet in-person to transfer backup piece</string>
|
||||
<string name="custodian_recovery_explainer_extra">Make sure you are both connected to the same wifi network, and scan the QR code on your contact\'s device to begin transferring the backup piece</string>
|
||||
<string name="custodian_recovery_failed_to_send">Failed to send backup piece</string>
|
||||
<string name="custodian_scan_code">Scan code</string>
|
||||
<string name="custodian_shard_sent">Account backup piece transmitted</string>
|
||||
@@ -716,7 +720,7 @@
|
||||
|
||||
<!-- activity names -->
|
||||
<string name="activity_name_distributed_backup">Social Backup</string>
|
||||
<string name="activity_name_old_distributed_backup">Old Social Backup</string>
|
||||
<string name="activity_name_restore_account">Restore Account</string>
|
||||
|
||||
<!-- conversation -->
|
||||
<string name="social_backup_shard_received">You have received a social backup shard.</string>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package org.briarproject.briar.socialbackup;
|
||||
package org.briarproject.briar.api.socialbackup;
|
||||
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
import org.briarproject.briar.api.socialbackup.Shard;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -13,14 +12,14 @@ import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class ContactData {
|
||||
public class ContactData {
|
||||
|
||||
private final Contact contact;
|
||||
private final Map<TransportId, TransportProperties> properties;
|
||||
@Nullable
|
||||
private final Shard shard;
|
||||
|
||||
ContactData(Contact contact,
|
||||
public ContactData(Contact contact,
|
||||
Map<TransportId, TransportProperties> properties,
|
||||
@Nullable Shard shard) {
|
||||
this.contact = contact;
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.briar.socialbackup;
|
||||
package org.briarproject.briar.api.socialbackup;
|
||||
|
||||
import org.briarproject.bramble.api.identity.Identity;
|
||||
|
||||
@@ -9,7 +9,7 @@ public class SocialBackup {
|
||||
private List<ContactData> contacts;
|
||||
private int version;
|
||||
|
||||
SocialBackup (Identity identity, List<ContactData> contacts, int version) {
|
||||
public SocialBackup (Identity identity, List<ContactData> contacts, int version) {
|
||||
this.identity = identity;
|
||||
this.contacts = contacts;
|
||||
this.version = version;
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.briarproject.briar.api.socialbackup.recovery;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.briar.api.socialbackup.ReturnShardPayload;
|
||||
import org.briarproject.briar.api.socialbackup.SocialBackup;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
public interface RestoreAccount {
|
||||
|
||||
|
||||
int getNumberOfShards();
|
||||
|
||||
boolean addReturnShardPayload(ReturnShardPayload toAdd);
|
||||
|
||||
boolean canRecover();
|
||||
|
||||
int recover() throws FormatException, GeneralSecurityException;
|
||||
|
||||
SocialBackup getSocialBackup();
|
||||
|
||||
void addContactsToDb() throws DbException;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.api.socialbackup.BackupPayload;
|
||||
import org.briarproject.briar.api.socialbackup.SocialBackup;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
import org.briarproject.briar.api.socialbackup.BackupPayload;
|
||||
import org.briarproject.briar.api.socialbackup.MessageParser;
|
||||
import org.briarproject.briar.api.socialbackup.Shard;
|
||||
import org.briarproject.briar.api.socialbackup.SocialBackup;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.SecureRandom;
|
||||
@@ -54,7 +55,7 @@ public class BackupPayloadDecoderImpl implements BackupPayloadDecoder {
|
||||
this.messageParser = messageParser;
|
||||
}
|
||||
|
||||
public SocialBackup decodeBackupPayload(
|
||||
public org.briarproject.briar.api.socialbackup.SocialBackup decodeBackupPayload(
|
||||
SecretKey secret,
|
||||
BackupPayload backupPayload)
|
||||
throws FormatException, GeneralSecurityException {
|
||||
@@ -103,7 +104,7 @@ public class BackupPayloadDecoderImpl implements BackupPayloadDecoder {
|
||||
handShakePrivateKey, created);
|
||||
LOG.info("New identity created");
|
||||
|
||||
List<ContactData> contactDataList = new ArrayList();
|
||||
List<org.briarproject.briar.api.socialbackup.ContactData> contactDataList = new ArrayList();
|
||||
|
||||
for (int i = 0; i < bdfContactData.size(); i++) {
|
||||
BdfList bdfData = bdfContactData.getList(i);
|
||||
@@ -139,8 +140,8 @@ public class BackupPayloadDecoderImpl implements BackupPayloadDecoder {
|
||||
Contact contact =
|
||||
new Contact(contactId, author, author.getId(), alias,
|
||||
contactHandshakePublicKey, false);
|
||||
ContactData contactData =
|
||||
new ContactData(contact, properties, shard);
|
||||
org.briarproject.briar.api.socialbackup.ContactData contactData =
|
||||
new org.briarproject.briar.api.socialbackup.ContactData(contact, properties, shard);
|
||||
contactDataList.add(contactData);
|
||||
LOG.info("Contact added");
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@ package org.briarproject.briar.socialbackup;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.identity.Identity;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.api.socialbackup.BackupPayload;
|
||||
import org.briarproject.briar.api.socialbackup.ContactData;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@NotNullByDefault
|
||||
interface BackupPayloadEncoder {
|
||||
|
||||
org.briarproject.briar.api.socialbackup.BackupPayload encodeBackupPayload(SecretKey secret, Identity identity,
|
||||
BackupPayload encodeBackupPayload(SecretKey secret, Identity identity,
|
||||
List<ContactData> contactData, int version);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ class BackupPayloadEncoderImpl implements BackupPayloadEncoder {
|
||||
|
||||
@Override
|
||||
public org.briarproject.briar.api.socialbackup.BackupPayload encodeBackupPayload(SecretKey secret,
|
||||
Identity identity, List<ContactData> contactData, int version) {
|
||||
Identity identity, List<org.briarproject.briar.api.socialbackup.ContactData> contactData, int version) {
|
||||
// Encode the local identity
|
||||
BdfList bdfIdentity = new BdfList();
|
||||
LocalAuthor localAuthor = identity.getLocalAuthor();
|
||||
@@ -56,7 +56,7 @@ class BackupPayloadEncoderImpl implements BackupPayloadEncoder {
|
||||
bdfIdentity.add(identity.getHandshakePrivateKey().getEncoded());
|
||||
// Encode the contact data
|
||||
BdfList bdfContactData = new BdfList();
|
||||
for (ContactData cd : contactData) {
|
||||
for (org.briarproject.briar.api.socialbackup.ContactData cd : contactData) {
|
||||
BdfList bdfData = new BdfList();
|
||||
Contact contact = cd.getContact();
|
||||
bdfData.add(clientHelper.toList(contact.getAuthor()));
|
||||
|
||||
@@ -52,6 +52,7 @@ import org.briarproject.briar.api.socialbackup.ShardMessageHeader;
|
||||
import org.briarproject.briar.api.socialbackup.ShardReceivedEvent;
|
||||
import org.briarproject.briar.api.socialbackup.SocialBackupManager;
|
||||
import org.briarproject.briar.client.ConversationClientImpl;
|
||||
import org.briarproject.briar.api.socialbackup.ContactData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -278,7 +279,7 @@ class SocialBackupManagerImpl extends ConversationClientImpl
|
||||
}
|
||||
// Create the encrypted backup payload
|
||||
SecretKey secret = crypto.generateSecretKey();
|
||||
List<ContactData> contactData = loadContactData(txn);
|
||||
List<org.briarproject.briar.api.socialbackup.ContactData> contactData = loadContactData(txn);
|
||||
BackupPayload payload =
|
||||
createBackupPayload(txn, secret, contactData, 0);
|
||||
// Create the shards
|
||||
@@ -415,17 +416,17 @@ class SocialBackupManagerImpl extends ConversationClientImpl
|
||||
}
|
||||
|
||||
private BackupPayload createBackupPayload(Transaction txn,
|
||||
SecretKey secret, List<ContactData> contactData, int version)
|
||||
SecretKey secret, List<org.briarproject.briar.api.socialbackup.ContactData> contactData, int version)
|
||||
throws DbException {
|
||||
Identity identity = identityManager.getIdentity(txn);
|
||||
return backupPayloadEncoder.encodeBackupPayload(secret, identity,
|
||||
contactData, version);
|
||||
}
|
||||
|
||||
private List<ContactData> loadContactData(Transaction txn)
|
||||
private List<org.briarproject.briar.api.socialbackup.ContactData> loadContactData(Transaction txn)
|
||||
throws DbException {
|
||||
Collection<Contact> contacts = contactManager.getContacts(txn);
|
||||
List<ContactData> contactData = new ArrayList<>();
|
||||
List<org.briarproject.briar.api.socialbackup.ContactData> contactData = new ArrayList<>();
|
||||
for (Contact c : contacts) {
|
||||
// Skip contacts that are in the process of being removed
|
||||
Group contactGroup = getContactGroup(c);
|
||||
@@ -433,7 +434,7 @@ class SocialBackupManagerImpl extends ConversationClientImpl
|
||||
Map<TransportId, TransportProperties> props =
|
||||
getTransportProperties(txn, c.getId());
|
||||
Shard shard = getRemoteShard(txn, contactGroup.getId());
|
||||
contactData.add(new ContactData(c, props, shard));
|
||||
contactData.add(new org.briarproject.briar.api.socialbackup.ContactData(c, props, shard));
|
||||
}
|
||||
return contactData;
|
||||
}
|
||||
@@ -513,7 +514,7 @@ class SocialBackupManagerImpl extends ConversationClientImpl
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
private void updateBackup(Transaction txn, List<ContactData> contactData)
|
||||
private void updateBackup(Transaction txn, List<org.briarproject.briar.api.socialbackup.ContactData> contactData)
|
||||
throws DbException {
|
||||
BackupMetadata backupMetadata = requireNonNull(getBackupMetadata(txn));
|
||||
int newVersion = backupMetadata.getVersion() + 1;
|
||||
|
||||
@@ -11,8 +11,10 @@ import org.briarproject.briar.api.conversation.ConversationManager;
|
||||
import org.briarproject.briar.api.socialbackup.SocialBackupExchangeManager;
|
||||
import org.briarproject.briar.api.socialbackup.SocialBackupManager;
|
||||
import org.briarproject.briar.api.socialbackup.recovery.CustodianTask;
|
||||
import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount;
|
||||
import org.briarproject.briar.api.socialbackup.recovery.SecretOwnerTask;
|
||||
import org.briarproject.briar.socialbackup.recovery.CustodianTaskImpl;
|
||||
import org.briarproject.briar.socialbackup.recovery.RestoreAccountImpl;
|
||||
import org.briarproject.briar.socialbackup.recovery.SecretOwnerTaskImpl;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -119,4 +121,10 @@ public class SocialBackupModule {
|
||||
CustodianTask custodianTask(CustodianTaskImpl custodianTask) {
|
||||
return custodianTask;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
RestoreAccount restoreAccount(RestoreAccountImpl restoreAccount) {
|
||||
return restoreAccount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
package org.briarproject.briar.socialbackup.recovery;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.identity.AuthorId;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.briar.api.socialbackup.BackupPayload;
|
||||
import org.briarproject.briar.api.socialbackup.ContactData;
|
||||
import org.briarproject.briar.api.socialbackup.DarkCrystal;
|
||||
import org.briarproject.briar.api.socialbackup.ReturnShardPayload;
|
||||
import org.briarproject.briar.api.socialbackup.Shard;
|
||||
import org.briarproject.briar.api.socialbackup.SocialBackup;
|
||||
import org.briarproject.briar.api.socialbackup.recovery.RestoreAccount;
|
||||
import org.briarproject.briar.socialbackup.BackupPayloadDecoder;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
|
||||
public class RestoreAccountImpl implements RestoreAccount {
|
||||
private final ArrayList<ReturnShardPayload> recoveredShards = new ArrayList<>();
|
||||
private final DarkCrystal darkCrystal;
|
||||
private final Executor ioExecutor;
|
||||
private final DatabaseComponent db;
|
||||
private final ContactManager contactManager;
|
||||
private final LifecycleManager lifecycleManager;
|
||||
private SecretKey secretKey;
|
||||
private final BackupPayloadDecoder backupPayloadDecoder;
|
||||
private SocialBackup socialBackup;
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(RestoreAccountImpl.class.getName());
|
||||
|
||||
@Inject
|
||||
RestoreAccountImpl(DarkCrystal darkCrystal,
|
||||
BackupPayloadDecoder backupPayloadDecoder, DatabaseComponent db,
|
||||
@IoExecutor Executor ioExecutor,
|
||||
ContactManager contactManager,
|
||||
LifecycleManager lifecycleManager) {
|
||||
this.darkCrystal = darkCrystal;
|
||||
this.backupPayloadDecoder = backupPayloadDecoder;
|
||||
this.db = db;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.lifecycleManager = lifecycleManager;
|
||||
this.contactManager = contactManager;
|
||||
}
|
||||
|
||||
public int getNumberOfShards() {
|
||||
return recoveredShards.size();
|
||||
}
|
||||
|
||||
// TODO figure out how to actually use a hash set for these objects
|
||||
public boolean addReturnShardPayload(ReturnShardPayload toAdd) {
|
||||
boolean found = false;
|
||||
for (ReturnShardPayload returnShardPayload : recoveredShards) {
|
||||
if (toAdd.equals(returnShardPayload)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) recoveredShards.add(toAdd);
|
||||
return !found;
|
||||
}
|
||||
|
||||
public boolean canRecover() {
|
||||
ArrayList<Shard> shards = new ArrayList<>();
|
||||
for (ReturnShardPayload returnShardPayload : recoveredShards) {
|
||||
// TODO check shards all have same secret id
|
||||
shards.add(returnShardPayload.getShard());
|
||||
}
|
||||
try {
|
||||
secretKey = darkCrystal.combineShards(shards);
|
||||
} catch (GeneralSecurityException e) {
|
||||
// TODO handle error message
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int recover() throws FormatException, GeneralSecurityException {
|
||||
if (secretKey == null) throw new GeneralSecurityException();
|
||||
// Find backup with highest version number
|
||||
int highestVersion = -1;
|
||||
for (ReturnShardPayload returnShardPayload : recoveredShards) {
|
||||
BackupPayload backupPayload = returnShardPayload.getBackupPayload();
|
||||
SocialBackup s = backupPayloadDecoder
|
||||
.decodeBackupPayload(secretKey, backupPayload);
|
||||
if (s.getVersion() > highestVersion) {
|
||||
socialBackup = s;
|
||||
highestVersion = s.getVersion();
|
||||
}
|
||||
}
|
||||
return highestVersion;
|
||||
}
|
||||
|
||||
public SocialBackup getSocialBackup() {
|
||||
return socialBackup;
|
||||
}
|
||||
|
||||
public void addContactsToDb() throws DbException {
|
||||
if (socialBackup == null) throw new DbException();
|
||||
AuthorId localAuthorId = socialBackup.getIdentity().getId();
|
||||
|
||||
ioExecutor.execute(() -> {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
} catch (InterruptedException e) {
|
||||
LOG.warning("Interrupted when waiting for database");
|
||||
}
|
||||
try {
|
||||
db.transaction(false, txn -> {
|
||||
for (ContactData contactData : socialBackup.getContacts()) {
|
||||
Contact c = contactData.getContact();
|
||||
LOG.info("Adding contact " + c.getAuthor().getName() + " " + c.getAlias());
|
||||
contactManager.addContact(txn, c.getAuthor(), localAuthorId,
|
||||
c.getHandshakePublicKey(), c.isVerified());
|
||||
}
|
||||
});
|
||||
} catch (DbException e) {
|
||||
LOG.warning("Error adding contacts to database");
|
||||
LOG.warning(e.getMessage());
|
||||
}
|
||||
LOG.info("Added all contacts");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.test.TestDatabaseConfigModule;
|
||||
import org.briarproject.briar.api.socialbackup.BackupPayload;
|
||||
import org.briarproject.briar.api.socialbackup.MessageEncoder;
|
||||
import org.briarproject.briar.api.socialbackup.ReturnShardPayload;
|
||||
import org.briarproject.briar.api.socialbackup.Shard;
|
||||
import org.briarproject.briar.api.socialbackup.recovery.CustodianTask;
|
||||
@@ -15,9 +16,9 @@ import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static junit.framework.TestCase.fail;
|
||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||
@@ -32,6 +33,8 @@ public class ReturnShardIntegrationTest extends BrambleTestCase {
|
||||
|
||||
private ReturnShardIntegrationTestComponent owner, custodian;
|
||||
|
||||
private ReturnShardPayload remotePayload;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
assertTrue(testDir.mkdirs());
|
||||
@@ -50,16 +53,20 @@ public class ReturnShardIntegrationTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnShard() {
|
||||
public void testReturnShard() throws Exception {
|
||||
SecretOwnerTask secretOwnerTask = owner.getSecretOwnerTask();
|
||||
CustodianTask custodianTask = custodian.getCustodianTask();
|
||||
byte[] payload = "its nice to be important but its more important to be nice".getBytes();
|
||||
MessageEncoder messageEncoder = owner.getMessageEncoder();
|
||||
|
||||
Shard shard = new Shard("secretid".getBytes(), "shard".getBytes());
|
||||
CountDownLatch secretOwnerFinished = new CountDownLatch(1);
|
||||
CountDownLatch custodianFinished = new CountDownLatch(1);
|
||||
|
||||
Shard shard = new Shard("secret id".getBytes(), "shard".getBytes());
|
||||
BackupPayload backupPayload = new BackupPayload("backup payload".getBytes());
|
||||
ReturnShardPayload returnShardPayload = new ReturnShardPayload(shard, backupPayload);
|
||||
|
||||
// payloadBytes = clientHelper
|
||||
byte[] payloadBytes = messageEncoder.encodeReturnShardPayload(returnShardPayload);
|
||||
|
||||
|
||||
SecretOwnerTask.Observer ownerObserver =
|
||||
state -> {
|
||||
@@ -67,30 +74,21 @@ public class ReturnShardIntegrationTest extends BrambleTestCase {
|
||||
SecretOwnerTask.State.Listening listening =
|
||||
(SecretOwnerTask.State.Listening) state;
|
||||
byte[] qrPayload = listening.getLocalPayload();
|
||||
System.out.println(qrPayload.length);
|
||||
transferQrCode(custodianTask, qrPayload);
|
||||
} else if (state instanceof SecretOwnerTask.State.Success) {
|
||||
ReturnShardPayload remotePayload = ((SecretOwnerTask.State.Success) state).getRemotePayload();
|
||||
assertTrue(remotePayload.equals(payload));
|
||||
System.out.println("Success");
|
||||
remotePayload = ((SecretOwnerTask.State.Success) state).getRemotePayload();
|
||||
secretOwnerFinished.countDown();
|
||||
} else if (state instanceof SecretOwnerTask.State.Failure) {
|
||||
System.out.println("Owner state: failure");
|
||||
fail();
|
||||
} else {
|
||||
System.out.println(
|
||||
"owner: " + state.getClass().getSimpleName());
|
||||
}
|
||||
};
|
||||
|
||||
CustodianTask.Observer custodianObserver =
|
||||
state -> {
|
||||
if (state instanceof CustodianTask.State.Success) {
|
||||
assertEquals(1, 1);
|
||||
custodianFinished.countDown();
|
||||
} else if (state instanceof CustodianTask.State.Failure) {
|
||||
fail();
|
||||
} else {
|
||||
System.out.println(
|
||||
"custodian: " + state.getClass().getSimpleName());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -105,19 +103,14 @@ public class ReturnShardIntegrationTest extends BrambleTestCase {
|
||||
|
||||
custodian.getIoExecutor().execute(() -> {
|
||||
try {
|
||||
custodianTask.start(custodianObserver, payload);
|
||||
custodianTask.start(custodianObserver, payloadBytes);
|
||||
} catch (Exception e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
|
||||
// TODO how to get the test to wait for the io to finish
|
||||
try {
|
||||
// Thread.sleep(1000);
|
||||
tearDown();
|
||||
} catch (Exception e) {
|
||||
fail();
|
||||
}
|
||||
assertTrue(secretOwnerFinished.await(15000, MILLISECONDS));
|
||||
assertTrue(custodianFinished.await(15000, MILLISECONDS));
|
||||
assertTrue(remotePayload.equals(returnShardPayload));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.briar.socialbackup.recovery;
|
||||
|
||||
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons;
|
||||
import org.briarproject.bramble.BrambleCoreModule;
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.connection.ConnectionManager;
|
||||
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
@@ -10,6 +11,7 @@ import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
|
||||
import org.briarproject.briar.api.socialbackup.MessageEncoder;
|
||||
import org.briarproject.briar.api.socialbackup.recovery.CustodianTask;
|
||||
import org.briarproject.briar.api.socialbackup.recovery.SecretOwnerTask;
|
||||
import org.briarproject.briar.socialbackup.SocialBackupModule;
|
||||
@@ -44,6 +46,8 @@ interface ReturnShardIntegrationTestComponent
|
||||
|
||||
LifecycleManager getLifecycleManager();
|
||||
|
||||
MessageEncoder getMessageEncoder();
|
||||
|
||||
SecretOwnerTask getSecretOwnerTask();
|
||||
|
||||
CustodianTask getCustodianTask();
|
||||
|
||||
Reference in New Issue
Block a user