diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java index 81367fefa..99988094d 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java @@ -1,6 +1,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.SecretKey; import org.briarproject.bramble.api.db.DbException; @@ -76,9 +77,11 @@ public interface ContactManager { throws DbException, FormatException; /** - * Returns a list of {@link PendingContact}s. + * Returns a list of {@link PendingContact PendingContacts} and their + * {@link PendingContactState states}. */ - Collection getPendingContacts() throws DbException; + Collection> getPendingContacts() + throws DbException; /** * Removes a {@link PendingContact}. diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/PendingContact.java b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/PendingContact.java index c0093c68c..6db81412c 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/PendingContact.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/PendingContact.java @@ -12,15 +12,13 @@ public class PendingContact { private final PendingContactId id; private final PublicKey publicKey; private final String alias; - private final PendingContactState state; private final long timestamp; public PendingContact(PendingContactId id, PublicKey publicKey, - String alias, PendingContactState state, long timestamp) { + String alias, long timestamp) { this.id = id; this.publicKey = publicKey; this.alias = alias; - this.state = state; this.timestamp = timestamp; } @@ -36,10 +34,6 @@ public class PendingContact { return alias; } - public PendingContactState getState() { - return state; - } - public long getTimestamp() { return timestamp; } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/PendingContactState.java b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/PendingContactState.java index 5fd5a9919..2d8e8812d 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/PendingContactState.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/PendingContactState.java @@ -1,30 +1,9 @@ package org.briarproject.bramble.api.contact; -import org.briarproject.bramble.api.nullsafety.NotNullByDefault; - -import javax.annotation.concurrent.Immutable; - -@Immutable -@NotNullByDefault public enum PendingContactState { - WAITING_FOR_CONNECTION(0), - CONNECTED(1), - ADDING_CONTACT(2), - FAILED(3); - - private final int value; - - PendingContactState(int value) { - this.value = value; - } - - public int getValue() { - return value; - } - - public static PendingContactState fromValue(int value) { - for (PendingContactState s : values()) if (s.value == value) return s; - throw new IllegalArgumentException(); - } + WAITING_FOR_CONNECTION, + CONNECTED, + ADDING_CONTACT, + FAILED } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/event/PendingContactAddedEvent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/event/PendingContactAddedEvent.java new file mode 100644 index 000000000..f57e6a24e --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/event/PendingContactAddedEvent.java @@ -0,0 +1,25 @@ +package org.briarproject.bramble.api.contact.event; + +import org.briarproject.bramble.api.contact.PendingContact; +import org.briarproject.bramble.api.event.Event; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import javax.annotation.concurrent.Immutable; + +/** + * An event that is broadcast when a pending contact is added. + */ +@Immutable +@NotNullByDefault +public class PendingContactAddedEvent extends Event { + + private final PendingContact pendingContact; + + public PendingContactAddedEvent(PendingContact pendingContact) { + this.pendingContact = pendingContact; + } + + public PendingContact getPendingContact() { + return pendingContact; + } +} diff --git a/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java b/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java index 3a1d3f80a..e39e9aa62 100644 --- a/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java +++ b/bramble-api/src/test/java/org/briarproject/bramble/test/TestUtils.java @@ -5,7 +5,6 @@ import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContactId; -import org.briarproject.bramble.api.contact.PendingContactState; import org.briarproject.bramble.api.crypto.AgreementPrivateKey; import org.briarproject.bramble.api.crypto.AgreementPublicKey; import org.briarproject.bramble.api.crypto.PrivateKey; @@ -181,10 +180,7 @@ public class TestUtils { PendingContactId id = new PendingContactId(getRandomId()); PublicKey publicKey = getAgreementPublicKey(); String alias = getRandomString(nameLength); - int stateIndex = - random.nextInt(PendingContactState.values().length - 1); - PendingContactState state = PendingContactState.values()[stateIndex]; - return new PendingContact(id, publicKey, alias, state, timestamp); + return new PendingContact(id, publicKey, alias, timestamp); } public static ContactId getContactId() { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java index 985793843..d6e2d0782 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/contact/ContactManagerImpl.java @@ -1,11 +1,13 @@ package org.briarproject.bramble.contact; import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContactId; +import org.briarproject.bramble.api.contact.PendingContactState; import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DbException; @@ -19,6 +21,7 @@ import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.transport.KeyManager; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -28,6 +31,7 @@ import javax.annotation.concurrent.ThreadSafe; import javax.inject.Inject; import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES; +import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN; @@ -111,8 +115,16 @@ class ContactManagerImpl implements ContactManager { } @Override - public Collection getPendingContacts() throws DbException { - return db.transactionWithResult(true, db::getPendingContacts); + public Collection> getPendingContacts() + throws DbException { + Collection pendingContacts = + db.transactionWithResult(true, db::getPendingContacts); + List> pairs = + new ArrayList<>(pendingContacts.size()); + for (PendingContact p : pendingContacts) { + pairs.add(new Pair<>(p, WAITING_FOR_CONNECTION)); // TODO + } + return pairs; } @Override diff --git a/bramble-core/src/main/java/org/briarproject/bramble/contact/PendingContactFactoryImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/contact/PendingContactFactoryImpl.java index 5a008b136..ae9d1c0cb 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/contact/PendingContactFactoryImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/contact/PendingContactFactoryImpl.java @@ -20,7 +20,6 @@ import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.FORMAT import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.ID_LABEL; import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.LINK_REGEX; import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES; -import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION; class PendingContactFactoryImpl implements PendingContactFactory { @@ -39,8 +38,7 @@ class PendingContactFactoryImpl implements PendingContactFactory { PublicKey publicKey = parseHandshakeLink(link); PendingContactId id = getPendingContactId(publicKey); long timestamp = clock.currentTimeMillis(); - return new PendingContact(id, publicKey, alias, WAITING_FOR_CONNECTION, - timestamp); + return new PendingContact(id, publicKey, alias, timestamp); } private PublicKey parseHandshakeLink(String link) throws FormatException { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java index d93aed223..7f7ad07c4 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java @@ -4,7 +4,6 @@ import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContactId; -import org.briarproject.bramble.api.contact.PendingContactState; import org.briarproject.bramble.api.crypto.PrivateKey; import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.SecretKey; @@ -674,12 +673,6 @@ interface Database { void setMessageState(T txn, MessageId m, MessageState state) throws DbException; - /** - * Sets the state of the given pending contact. - */ - void setPendingContactState(T txn, PendingContactId p, - PendingContactState state) throws DbException; - /** * Sets the reordering window for the given transport keys in the given * time period. diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java index 37939e6d0..c4aeb15c5 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java @@ -7,8 +7,8 @@ import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.event.ContactAddedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent; +import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent; import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent; -import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent; import org.briarproject.bramble.api.crypto.PrivateKey; import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.SecretKey; @@ -295,8 +295,7 @@ class DatabaseComponentImpl implements DatabaseComponent { if (db.containsPendingContact(txn, p.getId())) throw new PendingContactExistsException(); db.addPendingContact(txn, p); - transaction.attach(new PendingContactStateChangedEvent(p.getId(), - p.getState())); + transaction.attach(new PendingContactAddedEvent(p)); } @Override diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java index 77d7fba11..0a154d859 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java @@ -4,7 +4,6 @@ import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContactId; -import org.briarproject.bramble.api.contact.PendingContactState; import org.briarproject.bramble.api.crypto.AgreementPrivateKey; import org.briarproject.bramble.api.crypto.AgreementPublicKey; import org.briarproject.bramble.api.crypto.PrivateKey; @@ -98,7 +97,7 @@ import static org.briarproject.bramble.util.LogUtils.now; abstract class JdbcDatabase implements Database { // Package access for testing - static final int CODE_SCHEMA_VERSION = 44; + static final int CODE_SCHEMA_VERSION = 45; // Time period offsets for incoming transport keys private static final int OFFSET_PREV = -1; @@ -264,7 +263,6 @@ abstract class JdbcDatabase implements Database { + " (pendingContactId _HASH NOT NULL," + " publicKey _BINARY NOT NULL," + " alias _STRING NOT NULL," - + " state INT NOT NULL," + " timestamp BIGINT NOT NULL," + " PRIMARY KEY (pendingContactId))"; @@ -457,7 +455,8 @@ abstract class JdbcDatabase implements Database { new Migration40_41(dbTypes), new Migration41_42(dbTypes), new Migration42_43(dbTypes), - new Migration43_44(dbTypes) + new Migration43_44(dbTypes), + new Migration44_45() ); } @@ -933,14 +932,13 @@ abstract class JdbcDatabase implements Database { PreparedStatement ps = null; try { String sql = "INSERT INTO pendingContacts (pendingContactId," - + " publicKey, alias, state, timestamp)" - + " VALUES (?, ?, ?, ?, ?)"; + + " publicKey, alias, timestamp)" + + " VALUES (?, ?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, p.getId().getBytes()); ps.setBytes(2, p.getPublicKey().getEncoded()); ps.setString(3, p.getAlias()); - ps.setInt(4, p.getState().getValue()); - ps.setLong(5, p.getTimestamp()); + ps.setLong(4, p.getTimestamp()); int affected = ps.executeUpdate(); if (affected != 1) throw new DbStateException(); ps.close(); @@ -2213,8 +2211,7 @@ abstract class JdbcDatabase implements Database { Statement s = null; ResultSet rs = null; try { - String sql = "SELECT pendingContactId, publicKey, alias, state," - + " timestamp" + String sql = "SELECT pendingContactId, publicKey, alias, timestamp" + " FROM pendingContacts"; s = txn.createStatement(); rs = s.executeQuery(sql); @@ -2223,11 +2220,9 @@ abstract class JdbcDatabase implements Database { PendingContactId id = new PendingContactId(rs.getBytes(1)); PublicKey publicKey = new AgreementPublicKey(rs.getBytes(2)); String alias = rs.getString(3); - PendingContactState state = - PendingContactState.fromValue(rs.getInt(4)); - long timestamp = rs.getLong(5); + long timestamp = rs.getLong(4); pendingContacts.add(new PendingContact(id, publicKey, alias, - state, timestamp)); + timestamp)); } rs.close(); s.close(); @@ -3077,25 +3072,6 @@ abstract class JdbcDatabase implements Database { } } - @Override - public void setPendingContactState(Connection txn, PendingContactId p, - PendingContactState state) throws DbException { - PreparedStatement ps = null; - try { - String sql = "UPDATE pendingContacts SET state = ?" - + " WHERE pendingContactId = ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, state.getValue()); - ps.setBytes(2, p.getBytes()); - int affected = ps.executeUpdate(); - if (affected < 0 || affected > 1) throw new DbStateException(); - ps.close(); - } catch (SQLException e) { - tryToClose(ps, LOG, WARNING); - throw new DbException(e); - } - } - @Override public void setReorderingWindow(Connection txn, KeySetId k, TransportId t, long timePeriod, long base, byte[] bitmap) diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Migration44_45.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Migration44_45.java new file mode 100644 index 000000000..62ce1cb0f --- /dev/null +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Migration44_45.java @@ -0,0 +1,39 @@ +package org.briarproject.bramble.db; + +import org.briarproject.bramble.api.db.DbException; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.logging.Logger; + +import static java.util.logging.Level.WARNING; +import static java.util.logging.Logger.getLogger; +import static org.briarproject.bramble.db.JdbcUtils.tryToClose; + +class Migration44_45 implements Migration { + + private static final Logger LOG = getLogger(Migration44_45.class.getName()); + + @Override + public int getStartVersion() { + return 44; + } + + @Override + public int getEndVersion() { + return 45; + } + + @Override + public void migrate(Connection txn) throws DbException { + Statement s = null; + try { + s = txn.createStatement(); + s.execute("ALTER TABLE pendingContacts DROP COLUMN state"); + } catch (SQLException e) { + tryToClose(s, LOG, WARNING); + throw new DbException(e); + } + } +} \ No newline at end of file diff --git a/bramble-core/src/test/java/org/briarproject/bramble/contact/PendingContactFactoryImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/contact/PendingContactFactoryImplTest.java index 70fb1f67a..39ee19324 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/contact/PendingContactFactoryImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/contact/PendingContactFactoryImplTest.java @@ -19,7 +19,6 @@ import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32 import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.FORMAT_VERSION; import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.ID_LABEL; import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES; -import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey; import static org.briarproject.bramble.test.TestUtils.getRandomId; @@ -108,7 +107,6 @@ public class PendingContactFactoryImplTest extends BrambleMockTestCase { assertArrayEquals(publicKey.getEncoded(), p.getPublicKey().getEncoded()); assertEquals(alias, p.getAlias()); - assertEquals(WAITING_FOR_CONNECTION, p.getState()); assertEquals(timestamp, p.getTimestamp()); } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java index 3cd9969f5..2fc0ab5b1 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java @@ -56,7 +56,6 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static java.util.concurrent.TimeUnit.SECONDS; -import static org.briarproject.bramble.api.contact.PendingContactState.FAILED; import static org.briarproject.bramble.api.db.Metadata.REMOVE; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; @@ -2211,16 +2210,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { PendingContact retrieved = pendingContacts.iterator().next(); assertEquals(pendingContact.getId(), retrieved.getId()); assertEquals(pendingContact.getAlias(), retrieved.getAlias()); - assertEquals(pendingContact.getState(), retrieved.getState()); - assertEquals(pendingContact.getTimestamp(), retrieved.getTimestamp()); - - db.setPendingContactState(txn, pendingContact.getId(), FAILED); - pendingContacts = db.getPendingContacts(txn); - assertEquals(1, pendingContacts.size()); - retrieved = pendingContacts.iterator().next(); - assertEquals(pendingContact.getId(), retrieved.getId()); - assertEquals(pendingContact.getAlias(), retrieved.getAlias()); - assertEquals(FAILED, retrieved.getState()); assertEquals(pendingContact.getTimestamp(), retrieved.getTimestamp()); db.removePendingContact(txn, pendingContact.getId()); @@ -2232,8 +2221,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase { @Test public void testSetHandshakeKeyPair() throws Exception { - Identity withoutKeys = - new Identity(localAuthor, null, null, identity.getTimeCreated()); + Identity withoutKeys = new Identity(localAuthor, null, null, + identity.getTimeCreated()); assertFalse(withoutKeys.hasHandshakeKeyPair()); PublicKey publicKey = getAgreementPublicKey(); PrivateKey privateKey = getAgreementPrivateKey(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java index f1eaa533c..07ed8d8c4 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java @@ -18,10 +18,9 @@ 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.event.ContactAddedEvent; -import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; +import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent; import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent; -import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.NoSuchContactException; import org.briarproject.bramble.api.event.Event; @@ -65,7 +64,6 @@ import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAn import static android.support.v4.view.ViewCompat.getTransitionName; import static java.util.Objects.requireNonNull; import static java.util.logging.Level.WARNING; -import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION; import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.now; @@ -293,15 +291,8 @@ public class ContactListFragment extends BaseFragment implements EventListener, (ConversationMessageReceivedEvent) e; ConversationMessageHeader h = p.getMessageHeader(); updateItem(p.getContactId(), h); - } else if (e instanceof PendingContactStateChangedEvent) { - PendingContactStateChangedEvent pe = - (PendingContactStateChangedEvent) e; - // only re-check pending contacts for initial state - if (pe.getPendingContactState() == WAITING_FOR_CONNECTION) { - checkForPendingContacts(); - } - } else if (e instanceof PendingContactRemovedEvent || - e instanceof ContactAddedRemotelyEvent) { + } else if (e instanceof PendingContactAddedEvent || + e instanceof PendingContactRemovedEvent) { checkForPendingContacts(); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/AddContactViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/AddContactViewModel.java index 6ed6087e3..2c032992f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/AddContactViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/AddContactViewModel.java @@ -4,7 +4,6 @@ import android.app.Application; import android.arch.lifecycle.AndroidViewModel; import android.arch.lifecycle.LiveData; import android.arch.lifecycle.MutableLiveData; -import android.support.annotation.NonNull; import android.support.annotation.Nullable; import org.briarproject.bramble.api.FormatException; @@ -45,7 +44,7 @@ public class AddContactViewModel extends AndroidViewModel { private String remoteHandshakeLink; @Inject - public AddContactViewModel(@NonNull Application application, + AddContactViewModel(Application application, ContactManager contactManager, @DatabaseExecutor Executor dbExecutor) { super(application); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactItem.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactItem.java new file mode 100644 index 000000000..a56e94930 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactItem.java @@ -0,0 +1,29 @@ +package org.briarproject.briar.android.contact.add.remote; + +import org.briarproject.bramble.api.contact.PendingContact; +import org.briarproject.bramble.api.contact.PendingContactState; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +class PendingContactItem { + + private final PendingContact pendingContact; + private final PendingContactState state; + + PendingContactItem(PendingContact pendingContact, + PendingContactState state) { + this.pendingContact = pendingContact; + this.state = state; + } + + PendingContact getPendingContact() { + return pendingContact; + } + + PendingContactState getState() { + return state; + } +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactListActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactListActivity.java index c731a70f9..9340a5f1d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactListActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactListActivity.java @@ -53,7 +53,8 @@ public class PendingContactListActivity extends BriarActivity viewModel.getPendingContacts() .observe(this, this::onPendingContactsChanged); - adapter = new PendingContactListAdapter(this, this, PendingContact.class); + adapter = new PendingContactListAdapter(this, this, + PendingContactItem.class); list = findViewById(R.id.list); list.setEmptyText(R.string.no_pending_contacts); list.setLayoutManager(new LinearLayoutManager(this)); @@ -75,13 +76,11 @@ public class PendingContactListActivity extends BriarActivity @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - onBackPressed(); - return true; - default: - return super.onOptionsItemSelected(item); + if (item.getItemId() == android.R.id.home) { + onBackPressed(); + return true; } + return super.onOptionsItemSelected(item); } @Override @@ -89,8 +88,9 @@ public class PendingContactListActivity extends BriarActivity viewModel.removePendingContact(pendingContact.getId()); } - private void onPendingContactsChanged(Collection contacts) { - if (contacts.isEmpty()) { + private void onPendingContactsChanged( + Collection items) { + if (items.isEmpty()) { if (adapter.isEmpty()) { list.showData(); // hides progress bar, shows empty text } else { @@ -98,7 +98,7 @@ public class PendingContactListActivity extends BriarActivity supportFinishAfterTransition(); } } else { - adapter.setItems(contacts); + adapter.setItems(items); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactListAdapter.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactListAdapter.java index f2a25d6c7..1aa31a0a8 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactListAdapter.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactListAdapter.java @@ -12,12 +12,12 @@ import org.briarproject.briar.android.util.BriarAdapter; @NotNullByDefault class PendingContactListAdapter extends - BriarAdapter { + BriarAdapter { private final PendingContactListener listener; PendingContactListAdapter(Context ctx, PendingContactListener listener, - Class c) { + Class c) { super(ctx, c); this.listener = listener; } @@ -37,23 +37,29 @@ class PendingContactListAdapter extends } @Override - public int compare(PendingContact item1, PendingContact item2) { - return (int) (item1.getTimestamp() - item2.getTimestamp()); + public int compare(PendingContactItem item1, PendingContactItem item2) { + long timestamp1 = item1.getPendingContact().getTimestamp(); + long timestamp2 = item2.getPendingContact().getTimestamp(); + return Long.compare(timestamp1, timestamp2); } @Override - public boolean areContentsTheSame(PendingContact item1, - PendingContact item2) { - return item1.getId().equals(item2.getId()) && - item1.getAlias().equals(item2.getAlias()) && - item1.getTimestamp() == item2.getTimestamp() && + public boolean areContentsTheSame(PendingContactItem item1, + PendingContactItem item2) { + PendingContact p1 = item1.getPendingContact(); + PendingContact p2 = item2.getPendingContact(); + return p1.getId().equals(p2.getId()) && + p1.getAlias().equals(p2.getAlias()) && + p1.getTimestamp() == p2.getTimestamp() && item1.getState() == item2.getState(); } @Override - public boolean areItemsTheSame(PendingContact item1, - PendingContact item2) { - return item1.getId().equals(item2.getId()); + public boolean areItemsTheSame(PendingContactItem item1, + PendingContactItem item2) { + PendingContact p1 = item1.getPendingContact(); + PendingContact p2 = item2.getPendingContact(); + return p1.getId().equals(p2.getId()); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactListViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactListViewModel.java index c0772517f..86b4d3fdb 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactListViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactListViewModel.java @@ -5,10 +5,12 @@ import android.arch.lifecycle.AndroidViewModel; import android.arch.lifecycle.LiveData; import android.arch.lifecycle.MutableLiveData; +import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContactId; -import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent; +import org.briarproject.bramble.api.contact.PendingContactState; +import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent; import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent; import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent; import org.briarproject.bramble.api.db.DatabaseExecutor; @@ -18,7 +20,9 @@ import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -40,11 +44,11 @@ public class PendingContactListViewModel extends AndroidViewModel private final ContactManager contactManager; private final EventBus eventBus; - private final MutableLiveData> pendingContacts = - new MutableLiveData<>(); + private final MutableLiveData> + pendingContacts = new MutableLiveData<>(); @Inject - public PendingContactListViewModel(Application application, + PendingContactListViewModel(Application application, @DatabaseExecutor Executor dbExecutor, ContactManager contactManager, EventBus eventBus) { super(application); @@ -63,7 +67,7 @@ public class PendingContactListViewModel extends AndroidViewModel @Override public void eventOccurred(Event e) { - if (e instanceof ContactAddedRemotelyEvent || + if (e instanceof PendingContactAddedEvent || e instanceof PendingContactStateChangedEvent || e instanceof PendingContactRemovedEvent) { loadPendingContacts(); @@ -73,14 +77,21 @@ public class PendingContactListViewModel extends AndroidViewModel private void loadPendingContacts() { dbExecutor.execute(() -> { try { - pendingContacts.postValue(contactManager.getPendingContacts()); + Collection> pairs = + contactManager.getPendingContacts(); + List items = new ArrayList<>(pairs.size()); + for (Pair p : pairs) { + items.add(new PendingContactItem(p.getFirst(), + p.getSecond())); + } + pendingContacts.setValue(items); } catch (DbException e) { logException(LOG, WARNING, e); } }); } - LiveData> getPendingContacts() { + LiveData> getPendingContacts() { return pendingContacts; } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactViewHolder.java index ac3b2a917..15b702627 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactViewHolder.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/PendingContactViewHolder.java @@ -35,13 +35,14 @@ class PendingContactViewHolder extends ViewHolder { this.listener = listener; } - public void bind(PendingContact item) { - avatar.setText(item.getAlias()); - avatar.setBackgroundBytes(item.getId().getBytes()); - name.setText(item.getAlias()); - time.setText(formatDate(time.getContext(), item.getTimestamp())); + public void bind(PendingContactItem item) { + PendingContact p = item.getPendingContact(); + avatar.setText(p.getAlias()); + avatar.setBackgroundBytes(p.getId().getBytes()); + name.setText(p.getAlias()); + time.setText(formatDate(time.getContext(), p.getTimestamp())); removeButton.setOnClickListener(v -> { - listener.onFailedPendingContactRemoved(item); + listener.onFailedPendingContactRemoved(p); removeButton.setEnabled(false); }); diff --git a/briar-headless/README.md b/briar-headless/README.md index 02a7d40d2..8779db912 100644 --- a/briar-headless/README.md +++ b/briar-headless/README.md @@ -107,11 +107,27 @@ Until it is completed, a pending contact is returned as JSON: { "pendingContactId": "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U=", "alias": "ztatsaajzeegraqcizbbfftofdekclatyht", - "state": "adding_contact", "timestamp": 1557838312175 } ``` +It is possible to get a list of all pending contacts: + +`GET /v1/contacts/add/pending` + +This will return a JSON array of pending contacts and their states: + +```json +{ + "pendingContact": { + "pendingContactId": "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U=", + "alias": "ztatsaajzeegraqcizbbfftofdekclatyht", + "timestamp": 1557838312175 + }, + "state": "adding_contact" +} +``` + The state can be one of these values: * `waiting_for_connection` @@ -119,21 +135,16 @@ The state can be one of these values: * `adding_contact` * `failed` -If you want to get informed about state changes, +If you want to be informed about state changes, you can use the Websocket API (below) to listen for events. The following events are relevant here: + * `PendingContactAddedEvent` * `PendingContactStateChangedEvent` * `PendingContactRemovedEvent` * `ContactAddedRemotelyEvent` (when the pending contact becomes an actual contact) -It is possible to get a list of all pending contacts: - -`GET /v1/contacts/add/pending` - -This will return a JSON array of pending contacts formatted as shown above. - To remove a pending contact and abort the process of adding it: `DELETE /v1/contacts/add/pending` @@ -302,6 +313,22 @@ it will send a JSON object representing the new contact to connected websocket c } ``` +### A pending contact was added + +```json +{ + "data": { + "pendingContact": { + "pendingContactId": "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U=", + "alias": "ztatsaajzeegraqcizbbfftofdekclatyht", + "timestamp": 1557838312175 + } + }, + "name": "PendingContactAddedEvent", + "type": "event" +} + +``` ### A pending contact changed its state ```json diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/contact/ContactControllerImpl.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/contact/ContactControllerImpl.kt index 657649260..d1b1a757b 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/contact/ContactControllerImpl.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/contact/ContactControllerImpl.kt @@ -8,6 +8,7 @@ import org.briarproject.bramble.api.contact.ContactManager import org.briarproject.bramble.api.contact.HandshakeLinkConstants.LINK_REGEX import org.briarproject.bramble.api.contact.PendingContactId import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent +import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent import org.briarproject.bramble.api.db.NoSuchContactException @@ -28,6 +29,7 @@ import javax.inject.Singleton internal const val EVENT_CONTACT_ADDED_REMOTELY = "ContactAddedRemotelyEvent" internal const val EVENT_PENDING_CONTACT_STATE_CHANGED = "PendingContactStateChangedEvent" +internal const val EVENT_PENDING_CONTACT_ADDED = "PendingContactAddedEvent" internal const val EVENT_PENDING_CONTACT_REMOVED = "PendingContactRemovedEvent" @Immutable @@ -47,6 +49,9 @@ constructor( is PendingContactStateChangedEvent -> { webSocket.sendEvent(EVENT_PENDING_CONTACT_STATE_CHANGED, e.output()) } + is PendingContactAddedEvent -> { + webSocket.sendEvent(EVENT_PENDING_CONTACT_ADDED, e.output()) + } is PendingContactRemovedEvent -> { webSocket.sendEvent(EVENT_PENDING_CONTACT_REMOVED, e.output()) } @@ -78,8 +83,8 @@ constructor( } override fun listPendingContacts(ctx: Context): Context { - val pendingContacts = contactManager.pendingContacts.map { pendingContact -> - pendingContact.output() + val pendingContacts = contactManager.pendingContacts.map { pair -> + JsonDict("pendingContact" to pair.first.output(), "state" to pair.second.output()) } return ctx.json(pendingContacts) } diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/contact/OutputPendingContact.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/contact/OutputPendingContact.kt index 1988bb342..853e0eb1f 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/contact/OutputPendingContact.kt +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/contact/OutputPendingContact.kt @@ -3,6 +3,7 @@ package org.briarproject.briar.headless.contact import org.briarproject.bramble.api.contact.PendingContact import org.briarproject.bramble.api.contact.PendingContactState import org.briarproject.bramble.api.contact.PendingContactState.* +import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent import org.briarproject.briar.headless.json.JsonDict @@ -10,7 +11,6 @@ import org.briarproject.briar.headless.json.JsonDict internal fun PendingContact.output() = JsonDict( "pendingContactId" to id.bytes, "alias" to alias, - "state" to state.output(), "timestamp" to timestamp ) @@ -22,6 +22,10 @@ internal fun PendingContactState.output() = when(this) { else -> throw AssertionError() } +internal fun PendingContactAddedEvent.output() = JsonDict( + "pendingContact" to pendingContact.output() +) + internal fun PendingContactStateChangedEvent.output() = JsonDict( "pendingContactId" to id.bytes, "state" to pendingContactState.output() diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerIntegrationTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerIntegrationTest.kt index bd63c1193..bea7b950c 100644 --- a/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerIntegrationTest.kt +++ b/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerIntegrationTest.kt @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test class ContactControllerIntegrationTest: IntegrationTest() { @Test - fun `list of contacts need authentication token`() { + fun `returning list of contacts needs authentication token`() { val response = getWithWrongToken("$url/contacts") assertEquals(401, response.statusCode) } @@ -72,11 +72,10 @@ class ContactControllerIntegrationTest: IntegrationTest() { assertEquals(200, response.statusCode) assertEquals(1, response.jsonArray.length()) val jsonObject = response.jsonArray.getJSONObject(0) - assertEquals(alias, jsonObject.getString("alias")) - assertEquals("waiting_for_connection", jsonObject.getString("state")) + assertEquals(alias, jsonObject.getJSONObject("pendingContact").getString("alias")) // remove pending contact again - val idString = jsonObject.getString("pendingContactId") + val idString = jsonObject.getJSONObject("pendingContact").getString("pendingContactId") val deleteJson = """{"pendingContactId": "$idString"}""" response = delete("$url/contacts/add/pending", deleteJson) assertEquals(200, response.statusCode) @@ -94,7 +93,7 @@ class ContactControllerIntegrationTest: IntegrationTest() { } @Test - fun `adding pending contacts needs authentication token`() { + fun `adding a pending contact needs authentication token`() { val response = postWithWrongToken("$url/contacts/add/pending") assertEquals(401, response.statusCode) } @@ -106,7 +105,7 @@ class ContactControllerIntegrationTest: IntegrationTest() { } @Test - fun `deleting contact need authentication token`() { + fun `deleting a contact needs authentication token`() { val response = deleteWithWrongToken("$url/contacts/1") assertEquals(401, response.statusCode) } diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerTest.kt index 05128f28a..506089903 100644 --- a/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerTest.kt +++ b/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerTest.kt @@ -7,11 +7,14 @@ import io.mockk.Runs import io.mockk.every import io.mockk.just import io.mockk.runs +import org.briarproject.bramble.api.Pair import org.briarproject.bramble.api.contact.Contact import org.briarproject.bramble.api.contact.ContactId import org.briarproject.bramble.api.contact.PendingContactId import org.briarproject.bramble.api.contact.PendingContactState.FAILED +import org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent +import org.briarproject.bramble.api.contact.event.PendingContactAddedEvent import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent import org.briarproject.bramble.api.db.NoSuchContactException @@ -124,8 +127,19 @@ internal class ContactControllerTest : ControllerTest() { @Test fun testListPendingContacts() { - every { contactManager.pendingContacts } returns listOf(pendingContact) - every { ctx.json(listOf(pendingContact.output())) } returns ctx + every { contactManager.pendingContacts } returns listOf( + Pair(pendingContact, WAITING_FOR_CONNECTION) + ) + every { + ctx.json( + listOf( + JsonDict( + "pendingContact" to pendingContact.output(), + "state" to WAITING_FOR_CONNECTION.output() + ) + ) + ) + } returns ctx controller.listPendingContacts(ctx) } @@ -225,6 +239,20 @@ internal class ContactControllerTest : ControllerTest() { controller.eventOccurred(event) } + @Test + fun testPendingContactAddedEvent() { + val event = PendingContactAddedEvent(pendingContact) + + every { + webSocketController.sendEvent( + EVENT_PENDING_CONTACT_ADDED, + event.output() + ) + } just runs + + controller.eventOccurred(event) + } + @Test fun testPendingContactRemovedEvent() { val event = PendingContactRemovedEvent(pendingContact.id) @@ -284,13 +312,27 @@ internal class ContactControllerTest : ControllerTest() { { "pendingContactId": ${toJson(pendingContact.id.bytes)}, "alias": "${pendingContact.alias}", - "state": "${pendingContact.state.name.toLowerCase()}", "timestamp": ${pendingContact.timestamp} } """ assertJsonEquals(json, pendingContact.output()) } + @Test + fun testOutputPendingContactAddedEvent() { + val event = PendingContactAddedEvent(pendingContact) + val json = """ + { + "pendingContact": { + "pendingContactId": ${toJson(pendingContact.id.bytes)}, + "alias": "${pendingContact.alias}", + "timestamp": ${pendingContact.timestamp} + } + } + """ + assertJsonEquals(json, event.output()) + } + @Test fun testOutputPendingContactStateChangedEvent() { val event = PendingContactStateChangedEvent(pendingContact.id, FAILED)