Compare commits

..

11 Commits

Author SHA1 Message Date
akwizgran
68738a5a03 Refactor layouts for alias and link dialogs. 2018-10-31 15:28:58 +00:00
Torsten Grote
1a025d0f40 [android] Show existing alias in alias edit text view
This commit also uses LiveData Transformations to expose contact related information
2018-10-30 08:36:10 -03:00
Torsten Grote
a3593ea8ca [android] Show contact alias inside private groups and their memberlist 2018-10-29 19:41:01 -03:00
Torsten Grote
34eaedbd63 Show alias for introduction notices in private conversation 2018-10-29 19:23:22 -03:00
Torsten Grote
44e1ce32ce [android] Show alias for creator of private group in list of private groups 2018-10-29 18:38:20 -03:00
Torsten Grote
7af4b3d3ca [android] Show Author alias in AuthorView 2018-10-29 18:18:05 -03:00
Torsten Grote
1423ca7a15 [android] Add UI for changing and displaying contact alias
Note that this commit only shows the alias where the Contact is
available. In cases, where we only have the Author, only its name is
shown.

This also introduces the first ViewModel to share state between the
ConversationActivity and the AliasDialogFragment.
2018-10-29 18:18:05 -03:00
Torsten Grote
baf64e1129 [bramble] Add transactionless method for retrieving AuthorInfo to ContactManager 2018-10-29 18:16:34 -03:00
Torsten Grote
88adfabe09 Refactor Author.Status into dedicated AuthorInfo class and add alias 2018-10-29 17:23:45 -03:00
Torsten Grote
969150bff0 [bramble] Factor out database type placeholder replacement
to make it available in database schema migrations
2018-10-29 12:50:08 -03:00
Torsten Grote
8fc622f85d [bramble] Add support for contact aliases
Foundation for #41
2018-10-29 12:50:08 -03:00
111 changed files with 1330 additions and 555 deletions

View File

@@ -9,8 +9,8 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 26
versionCode 10104
versionName "1.1.4"
versionCode 10103
versionName "1.1.3"
consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

View File

@@ -4,8 +4,12 @@ import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.util.StringUtils.toUtf8;
@Immutable
@NotNullByDefault
public class Contact {
@@ -13,13 +17,21 @@ public class Contact {
private final ContactId id;
private final Author author;
private final AuthorId localAuthorId;
@Nullable
private final String alias;
private final boolean verified, active;
public Contact(ContactId id, Author author, AuthorId localAuthorId,
boolean verified, boolean active) {
@Nullable String alias, boolean verified, boolean active) {
if (alias != null) {
int aliasLength = toUtf8(alias).length;
if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH)
throw new IllegalArgumentException();
}
this.id = id;
this.author = author;
this.localAuthorId = localAuthorId;
this.alias = alias;
this.verified = verified;
this.active = active;
}
@@ -36,6 +48,11 @@ public class Contact {
return localAuthorId;
}
@Nullable
public String getAlias() {
return alias;
}
public boolean isVerified() {
return verified;
}

View File

@@ -5,11 +5,14 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collection;
import javax.annotation.Nullable;
@NotNullByDefault
public interface ContactManager {
@@ -93,6 +96,12 @@ public interface ContactManager {
void setContactActive(Transaction txn, ContactId c, boolean active)
throws DbException;
/**
* Sets an alias name for the contact or unsets it if alias is null.
*/
void setContactAlias(ContactId c, @Nullable String alias)
throws DbException;
/**
* Return true if a contact with this name and public key already exists
*/
@@ -105,6 +114,16 @@ public interface ContactManager {
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
throws DbException;
/**
* Returns the {@link AuthorInfo} for the given author.
*/
AuthorInfo getAuthorInfo(AuthorId a) throws DbException;
/**
* Returns the {@link AuthorInfo} for the given author.
*/
AuthorInfo getAuthorInfo(Transaction txn, AuthorId a) throws DbException;
interface ContactHook {
void addingContact(Transaction txn, Contact c) throws DbException;

View File

@@ -515,6 +515,12 @@ public interface DatabaseComponent {
void setContactActive(Transaction txn, ContactId c, boolean active)
throws DbException;
/**
* Sets an alias name for the contact or unsets it if alias is null.
*/
void setContactAlias(Transaction txn, ContactId c, @Nullable String alias)
throws DbException;
/**
* Sets the given group's visibility to the given contact.
*/

View File

@@ -16,10 +16,6 @@ import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_K
@NotNullByDefault
public class Author implements Nameable {
public enum Status {
NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES
}
/**
* The current version of the author structure.
*/

View File

@@ -0,0 +1,42 @@
package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class AuthorInfo {
public enum Status {
NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES;
public boolean isContact() {
return this == UNVERIFIED || this == VERIFIED;
}
}
private final Status status;
@Nullable
private final String alias;
public AuthorInfo(Status status, @Nullable String alias) {
this.status = status;
this.alias = alias;
}
public AuthorInfo(Status status) {
this(status, null);
}
public Status getStatus() {
return status;
}
@Nullable
public String getAlias() {
return alias;
}
}

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
@@ -37,14 +36,4 @@ public interface IdentityManager {
*/
LocalAuthor getLocalAuthor(Transaction txn) throws DbException;
/**
* Returns the {@link Status} of the given author.
*/
Status getAuthorStatus(AuthorId a) throws DbException;
/**
* Returns the {@link Status} of the given author.
*/
Status getAuthorStatus(Transaction txn, AuthorId a) throws DbException;
}

View File

@@ -10,6 +10,9 @@ import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.KeyManager;
@@ -18,21 +21,32 @@ import java.util.Collection;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
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;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
import static org.briarproject.bramble.util.StringUtils.toUtf8;
@ThreadSafe
@NotNullByDefault
class ContactManagerImpl implements ContactManager {
private final DatabaseComponent db;
private final KeyManager keyManager;
private final IdentityManager identityManager;
private final List<ContactHook> hooks;
@Inject
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager) {
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager,
IdentityManager identityManager) {
this.db = db;
this.keyManager = keyManager;
this.identityManager = identityManager;
hooks = new CopyOnWriteArrayList<>();
}
@@ -148,6 +162,17 @@ class ContactManagerImpl implements ContactManager {
db.setContactActive(txn, c, active);
}
@Override
public void setContactAlias(ContactId c, @Nullable String alias)
throws DbException {
if (alias != null) {
int aliasLength = toUtf8(alias).length;
if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH)
throw new IllegalArgumentException();
}
db.transaction(false, txn -> db.setContactAlias(txn, c, alias));
}
@Override
public boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException {
@@ -176,4 +201,23 @@ class ContactManagerImpl implements ContactManager {
db.removeContact(txn, c);
}
@Override
public AuthorInfo getAuthorInfo(AuthorId a) throws DbException {
return db.transactionWithResult(true, txn -> getAuthorInfo(txn, a));
}
@Override
public AuthorInfo getAuthorInfo(Transaction txn, AuthorId authorId)
throws DbException {
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
if (localAuthor.getId().equals(authorId))
return new AuthorInfo(OURSELVES);
Collection<Contact> contacts = db.getContactsByAuthorId(txn, authorId);
if (contacts.isEmpty()) return new AuthorInfo(UNKNOWN);
if (contacts.size() > 1) throw new AssertionError();
Contact c = contacts.iterator().next();
if (c.isVerified()) return new AuthorInfo(VERIFIED, c.getAlias());
else return new AuthorInfo(UNVERIFIED, c.getAlias());
}
}

View File

@@ -616,6 +616,12 @@ interface Database<T> {
void setContactActive(T txn, ContactId c, boolean active)
throws DbException;
/**
* Sets an alias name for a contact.
*/
void setContactAlias(T txn, ContactId c, @Nullable String alias)
throws DbException;
/**
* Sets the given group's visibility to the given contact to either
* {@link Visibility VISIBLE} or {@link Visibility SHARED}.

View File

@@ -859,6 +859,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
transaction.attach(new ContactStatusChangedEvent(c, active));
}
@Override
public void setContactAlias(Transaction transaction, ContactId c,
String alias) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
db.setContactAlias(txn, c, alias);
}
@Override
public void setGroupVisibility(Transaction transaction, ContactId c,
GroupId g, Visibility v) throws DbException {

View File

@@ -0,0 +1,34 @@
package org.briarproject.bramble.db;
class DatabaseTypes {
private final String hashType, secretType, binaryType;
private final String counterType, stringType;
public DatabaseTypes(String hashType, String secretType, String binaryType,
String counterType, String stringType) {
this.hashType = hashType;
this.secretType = secretType;
this.binaryType = binaryType;
this.counterType = counterType;
this.stringType = stringType;
}
/**
* Replaces database type placeholders in a statement with the actual types.
* These placeholders are currently supported:
* <li> _HASH
* <li> _SECRET
* <li> _BINARY
* <li> _COUNTER
* <li> _STRING
*/
String replaceTypes(String s) {
s = s.replaceAll("_HASH", hashType);
s = s.replaceAll("_SECRET", secretType);
s = s.replaceAll("_BINARY", binaryType);
s = s.replaceAll("_COUNTER", counterType);
s = s.replaceAll("_STRING", stringType);
return s;
}
}

View File

@@ -30,6 +30,8 @@ class H2Database extends JdbcDatabase {
private static final String BINARY_TYPE = "BINARY";
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
private static final String STRING_TYPE = "VARCHAR";
private static final DatabaseTypes dbTypes = new DatabaseTypes(HASH_TYPE,
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE);
private final DatabaseConfig config;
private final String url;
@@ -40,8 +42,7 @@ class H2Database extends JdbcDatabase {
@Inject
H2Database(DatabaseConfig config, MessageFactory messageFactory,
Clock clock) {
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
messageFactory, clock);
super(dbTypes, messageFactory, clock);
this.config = config;
File dir = config.getDatabaseDirectory();
String path = new File(dir, "db").getAbsolutePath();

View File

@@ -30,6 +30,8 @@ class HyperSqlDatabase extends JdbcDatabase {
private static final String COUNTER_TYPE =
"INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY(START WITH 1)";
private static final String STRING_TYPE = "VARCHAR";
private static final DatabaseTypes dbTypes = new DatabaseTypes(HASH_TYPE,
SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE);
private final DatabaseConfig config;
private final String url;
@@ -40,8 +42,7 @@ class HyperSqlDatabase extends JdbcDatabase {
@Inject
HyperSqlDatabase(DatabaseConfig config, MessageFactory messageFactory,
Clock clock) {
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
messageFactory, clock);
super(dbTypes, messageFactory, clock);
this.config = config;
File dir = config.getDatabaseDirectory();
String path = new File(dir, "db").getAbsolutePath();
@@ -78,7 +79,7 @@ class HyperSqlDatabase extends JdbcDatabase {
}
@Override
public long getFreeSpace() throws DbException {
public long getFreeSpace() {
File dir = config.getDatabaseDirectory();
long maxSize = config.getMaxSize();
long free = dir.getFreeSpace();

View File

@@ -56,6 +56,7 @@ import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.sql.Types.INTEGER;
import static java.sql.Types.VARCHAR;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
@@ -83,7 +84,7 @@ import static org.briarproject.bramble.util.LogUtils.now;
abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing
static final int CODE_SCHEMA_VERSION = 40;
static final int CODE_SCHEMA_VERSION = 41;
// Rotation period offsets for incoming transport keys
private static final int OFFSET_PREV = -1;
@@ -113,6 +114,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " authorId _HASH NOT NULL,"
+ " formatVersion INT NOT NULL,"
+ " name _STRING NOT NULL,"
+ " alias _STRING," // Null if no alias exists
+ " publicKey _BINARY NOT NULL,"
+ " localAuthorId _HASH NOT NULL,"
+ " verified BOOLEAN NOT NULL,"
@@ -310,10 +312,9 @@ abstract class JdbcDatabase implements Database<Connection> {
Logger.getLogger(JdbcDatabase.class.getName());
// Different database libraries use different names for certain types
private final String hashType, secretType, binaryType;
private final String counterType, stringType;
private final MessageFactory messageFactory;
private final Clock clock;
private final DatabaseTypes dbTypes;
// Locking: connectionsLock
private final LinkedList<Connection> connections = new LinkedList<>();
@@ -328,14 +329,9 @@ abstract class JdbcDatabase implements Database<Connection> {
private final Lock connectionsLock = new ReentrantLock();
private final Condition connectionsChanged = connectionsLock.newCondition();
JdbcDatabase(String hashType, String secretType, String binaryType,
String counterType, String stringType,
MessageFactory messageFactory, Clock clock) {
this.hashType = hashType;
this.secretType = secretType;
this.binaryType = binaryType;
this.counterType = counterType;
this.stringType = stringType;
JdbcDatabase(DatabaseTypes databaseTypes, MessageFactory messageFactory,
Clock clock) {
this.dbTypes = databaseTypes;
this.messageFactory = messageFactory;
this.clock = clock;
}
@@ -427,7 +423,11 @@ abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing
List<Migration<Connection>> getMigrations() {
return Arrays.asList(new Migration38_39(), new Migration39_40());
return Arrays.asList(
new Migration38_39(),
new Migration39_40(),
new Migration40_41(dbTypes)
);
}
private boolean isCompactionDue(Settings s) {
@@ -486,20 +486,20 @@ abstract class JdbcDatabase implements Database<Connection> {
Statement s = null;
try {
s = txn.createStatement();
s.executeUpdate(insertTypeNames(CREATE_SETTINGS));
s.executeUpdate(insertTypeNames(CREATE_LOCAL_AUTHORS));
s.executeUpdate(insertTypeNames(CREATE_CONTACTS));
s.executeUpdate(insertTypeNames(CREATE_GROUPS));
s.executeUpdate(insertTypeNames(CREATE_GROUP_METADATA));
s.executeUpdate(insertTypeNames(CREATE_GROUP_VISIBILITIES));
s.executeUpdate(insertTypeNames(CREATE_MESSAGES));
s.executeUpdate(insertTypeNames(CREATE_MESSAGE_METADATA));
s.executeUpdate(insertTypeNames(CREATE_MESSAGE_DEPENDENCIES));
s.executeUpdate(insertTypeNames(CREATE_OFFERS));
s.executeUpdate(insertTypeNames(CREATE_STATUSES));
s.executeUpdate(insertTypeNames(CREATE_TRANSPORTS));
s.executeUpdate(insertTypeNames(CREATE_OUTGOING_KEYS));
s.executeUpdate(insertTypeNames(CREATE_INCOMING_KEYS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_SETTINGS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_LOCAL_AUTHORS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_CONTACTS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_GROUPS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_GROUP_METADATA));
s.executeUpdate(dbTypes.replaceTypes(CREATE_GROUP_VISIBILITIES));
s.executeUpdate(dbTypes.replaceTypes(CREATE_MESSAGES));
s.executeUpdate(dbTypes.replaceTypes(CREATE_MESSAGE_METADATA));
s.executeUpdate(dbTypes.replaceTypes(CREATE_MESSAGE_DEPENDENCIES));
s.executeUpdate(dbTypes.replaceTypes(CREATE_OFFERS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_STATUSES));
s.executeUpdate(dbTypes.replaceTypes(CREATE_TRANSPORTS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_OUTGOING_KEYS));
s.executeUpdate(dbTypes.replaceTypes(CREATE_INCOMING_KEYS));
s.close();
} catch (SQLException e) {
tryToClose(s);
@@ -524,15 +524,6 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
private String insertTypeNames(String s) {
s = s.replaceAll("_HASH", hashType);
s = s.replaceAll("_SECRET", secretType);
s = s.replaceAll("_BINARY", binaryType);
s = s.replaceAll("_COUNTER", counterType);
s = s.replaceAll("_STRING", stringType);
return s;
}
@Override
public Connection startTransaction() throws DbException {
Connection txn;
@@ -1258,8 +1249,8 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT authorId, formatVersion, name, publicKey,"
+ " localAuthorId, verified, active"
String sql = "SELECT authorId, formatVersion, name, alias,"
+ " publicKey, localAuthorId, verified, active"
+ " FROM contacts"
+ " WHERE contactId = ?";
ps = txn.prepareStatement(sql);
@@ -1269,15 +1260,17 @@ abstract class JdbcDatabase implements Database<Connection> {
AuthorId authorId = new AuthorId(rs.getBytes(1));
int formatVersion = rs.getInt(2);
String name = rs.getString(3);
byte[] publicKey = rs.getBytes(4);
AuthorId localAuthorId = new AuthorId(rs.getBytes(5));
boolean verified = rs.getBoolean(6);
boolean active = rs.getBoolean(7);
String alias = rs.getString(4);
byte[] publicKey = rs.getBytes(5);
AuthorId localAuthorId = new AuthorId(rs.getBytes(6));
boolean verified = rs.getBoolean(7);
boolean active = rs.getBoolean(8);
rs.close();
ps.close();
Author author =
new Author(authorId, formatVersion, name, publicKey);
return new Contact(c, author, localAuthorId, verified, active);
return new Contact(c, author, localAuthorId, alias, verified,
active);
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
@@ -1292,7 +1285,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ResultSet rs = null;
try {
String sql = "SELECT contactId, authorId, formatVersion, name,"
+ " publicKey, localAuthorId, verified, active"
+ " alias, publicKey, localAuthorId, verified, active"
+ " FROM contacts";
ps = txn.prepareStatement(sql);
rs = ps.executeQuery();
@@ -1302,14 +1295,15 @@ abstract class JdbcDatabase implements Database<Connection> {
AuthorId authorId = new AuthorId(rs.getBytes(2));
int formatVersion = rs.getInt(3);
String name = rs.getString(4);
byte[] publicKey = rs.getBytes(5);
String alias = rs.getString(5);
byte[] publicKey = rs.getBytes(6);
Author author =
new Author(authorId, formatVersion, name, publicKey);
AuthorId localAuthorId = new AuthorId(rs.getBytes(6));
boolean verified = rs.getBoolean(7);
boolean active = rs.getBoolean(8);
AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
boolean verified = rs.getBoolean(8);
boolean active = rs.getBoolean(9);
contacts.add(new Contact(contactId, author, localAuthorId,
verified, active));
alias, verified, active));
}
rs.close();
ps.close();
@@ -1350,8 +1344,8 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT contactId, formatVersion, name, publicKey,"
+ " localAuthorId, verified, active"
String sql = "SELECT contactId, formatVersion, name, alias,"
+ " publicKey, localAuthorId, verified, active"
+ " FROM contacts"
+ " WHERE authorId = ?";
ps = txn.prepareStatement(sql);
@@ -1362,14 +1356,15 @@ abstract class JdbcDatabase implements Database<Connection> {
ContactId c = new ContactId(rs.getInt(1));
int formatVersion = rs.getInt(2);
String name = rs.getString(3);
byte[] publicKey = rs.getBytes(4);
AuthorId localAuthorId = new AuthorId(rs.getBytes(5));
boolean verified = rs.getBoolean(6);
boolean active = rs.getBoolean(7);
String alias = rs.getString(4);
byte[] publicKey = rs.getBytes(5);
AuthorId localAuthorId = new AuthorId(rs.getBytes(6));
boolean verified = rs.getBoolean(7);
boolean active = rs.getBoolean(8);
Author author =
new Author(remote, formatVersion, name, publicKey);
contacts.add(new Contact(c, author, localAuthorId, verified,
active));
contacts.add(new Contact(c, author, localAuthorId, alias,
verified, active));
}
rs.close();
ps.close();
@@ -2794,6 +2789,25 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
@Override
public void setContactAlias(Connection txn, ContactId c,
@Nullable String alias) throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE contacts SET alias = ? WHERE contactId = ?";
ps = txn.prepareStatement(sql);
if (alias == null) ps.setNull(1, VARCHAR);
else ps.setString(1, alias);
ps.setInt(2, c.getInt());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
@Override
public void setGroupVisibility(Connection txn, ContactId c, GroupId g,
boolean shared) throws DbException {

View File

@@ -0,0 +1,56 @@
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 javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
class Migration40_41 implements Migration<Connection> {
private static final Logger LOG = getLogger(Migration40_41.class.getName());
private final DatabaseTypes dbTypes;
public Migration40_41(DatabaseTypes databaseTypes) {
this.dbTypes = databaseTypes;
}
@Override
public int getStartVersion() {
return 40;
}
@Override
public int getEndVersion() {
return 41;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.execute("ALTER TABLE contacts"
+ dbTypes.replaceTypes(" ADD alias VARCHAR"));
} catch (SQLException e) {
tryToClose(s);
throw new DbException(e);
}
}
private void tryToClose(@Nullable Statement s) {
try {
if (s != null) s.close();
} catch (SQLException e) {
logException(LOG, WARNING, e);
}
}
}

View File

@@ -1,29 +1,21 @@
package org.briarproject.bramble.identity;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collection;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.Author.Status.UNKNOWN;
import static org.briarproject.bramble.api.identity.Author.Status.UNVERIFIED;
import static org.briarproject.bramble.api.identity.Author.Status.VERIFIED;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.now;
@@ -118,26 +110,4 @@ class IdentityManagerImpl implements IdentityManager {
return db.getLocalAuthors(txn).iterator().next();
}
@Override
public Status getAuthorStatus(AuthorId authorId) throws DbException {
Transaction txn = db.startTransaction(true);
try {
return getAuthorStatus(txn, authorId);
} finally {
db.endTransaction(txn);
}
}
@Override
public Status getAuthorStatus(Transaction txn, AuthorId authorId)
throws DbException {
if (getLocalAuthor(txn).getId().equals(authorId)) return OURSELVES;
Collection<Contact> contacts = db.getContactsByAuthorId(txn, authorId);
if (contacts.isEmpty()) return UNKNOWN;
for (Contact c : contacts) {
if (c.isVerified()) return VERIFIED;
}
return UNVERIFIED;
}
}

View File

@@ -5,12 +5,17 @@ import org.briarproject.bramble.api.contact.ContactId;
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.db.NoSuchContactException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
@@ -20,10 +25,20 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Random;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
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;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class ContactManagerImplTest extends BrambleMockTestCase {
@@ -31,16 +46,20 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
private final Mockery context = new Mockery();
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final KeyManager keyManager = context.mock(KeyManager.class);
private final IdentityManager identityManager =
context.mock(IdentityManager.class);
private final ContactManager contactManager;
private final ContactId contactId = new ContactId(42);
private final Author remote = getAuthor();
private final AuthorId local = new AuthorId(getRandomId());
private final LocalAuthor localAuthor = getLocalAuthor();
private final String alias = getRandomString(MAX_AUTHOR_NAME_LENGTH);
private final boolean verified = false, active = true;
private final Contact contact =
new Contact(contactId, remote, local, verified, active);
new Contact(contactId, remote, local, alias, verified, active);
public ContactManagerImplTest() {
contactManager = new ContactManagerImpl(db, keyManager);
contactManager = new ContactManagerImpl(db, keyManager, identityManager);
}
@Test
@@ -105,7 +124,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(Collections.emptyList()));
will(returnValue(emptyList()));
oneOf(db).endTransaction(txn);
}});
@@ -131,7 +150,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
public void testActiveContacts() throws Exception {
Collection<Contact> activeContacts = Collections.singletonList(contact);
Collection<Contact> contacts = new ArrayList<>(activeContacts);
contacts.add(new Contact(new ContactId(3), remote, local, true, false));
contacts.add(new Contact(new ContactId(3), remote, local, alias, true,
false));
Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
@@ -171,6 +191,23 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
contactManager.setContactActive(txn, contactId, active);
}
@Test
public void testSetContactAlias() throws Exception {
Transaction txn = new Transaction(null, false);
context.checking(new DbExpectations() {{
oneOf(db).transaction(with(equal(false)), withDbRunnable(txn));
oneOf(db).setContactAlias(txn, contactId, alias);
}});
contactManager.setContactAlias(contactId, alias);
}
@Test(expected = IllegalArgumentException.class)
public void testSetContactAliasTooLong() throws Exception {
contactManager.setContactAlias(contactId,
getRandomString(MAX_AUTHOR_NAME_LENGTH + 1));
}
@Test
public void testContactExists() throws Exception {
Transaction txn = new Transaction(null, true);
@@ -186,4 +223,79 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
assertTrue(contactManager.contactExists(remote.getId(), local));
}
@Test
public void testGetAuthorStatus() throws Exception {
Transaction txn = new Transaction(null, true);
Collection<Contact> contacts = singletonList(
new Contact(new ContactId(1), remote, localAuthor.getId(),
alias, false, true));
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(equal(true)),
withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(contacts));
}});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(UNVERIFIED, authorInfo.getStatus());
assertEquals(alias, contact.getAlias());
}
@Test
public void testGetAuthorStatusTransaction() throws DbException {
Transaction txn = new Transaction(null, true);
// check unknown author
context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(emptyList()));
}});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(UNKNOWN, authorInfo.getStatus());
assertNull(authorInfo.getAlias());
// check unverified contact
Collection<Contact> contacts = singletonList(
new Contact(new ContactId(1), remote, localAuthor.getId(),
alias, false, true));
checkAuthorStatusContext(txn, remote.getId(), contacts);
authorInfo = contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(UNVERIFIED, authorInfo.getStatus());
assertEquals(alias, contact.getAlias());
// check verified contact
contacts = singletonList(new Contact(new ContactId(1), remote,
localAuthor.getId(), alias, true, true));
checkAuthorStatusContext(txn, remote.getId(), contacts);
authorInfo = contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(VERIFIED, authorInfo.getStatus());
assertEquals(alias, contact.getAlias());
// check ourselves
context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
never(db).getContactsByAuthorId(txn, remote.getId());
}});
authorInfo = contactManager.getAuthorInfo(txn, localAuthor.getId());
assertEquals(OURSELVES, authorInfo.getStatus());
assertNull(authorInfo.getAlias());
}
private void checkAuthorStatusContext(Transaction txn, AuthorId authorId,
Collection<Contact> contacts) throws DbException {
context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContactsByAuthorId(txn, authorId);
will(returnValue(contacts));
}});
}
}

View File

@@ -77,6 +77,7 @@ import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -99,6 +100,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
private final Group group;
private final Author author;
private final LocalAuthor localAuthor;
private final String alias;
private final Message message, message1;
private final MessageId messageId, messageId1;
private final Metadata metadata;
@@ -115,6 +117,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
groupId = group.getId();
author = getAuthor();
localAuthor = getLocalAuthor();
alias = getRandomString(5);
message = getMessage(groupId);
message1 = getMessage(groupId);
messageId = message.getId();
@@ -124,7 +127,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
transportId = getTransportId();
maxLatency = Integer.MAX_VALUE;
contactId = new ContactId(234);
contact = new Contact(contactId, author, localAuthor.getId(),
contact = new Contact(contactId, author, localAuthor.getId(), alias,
true, true);
keySetId = new KeySetId(345);
}
@@ -288,11 +291,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception {
context.checking(new Expectations() {{
// Check whether the contact is in the DB (which it's not)
exactly(16).of(database).startTransaction();
exactly(17).of(database).startTransaction();
will(returnValue(txn));
exactly(16).of(database).containsContact(txn, contactId);
exactly(17).of(database).containsContact(txn, contactId);
will(returnValue(false));
exactly(16).of(database).abortTransaction(txn);
exactly(17).of(database).abortTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
@@ -450,6 +453,16 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
db.endTransaction(transaction);
}
transaction = db.startTransaction(false);
try {
db.setContactAlias(transaction, contactId, alias);
fail();
} catch (NoSuchContactException expected) {
// Expected
} finally {
db.endTransaction(transaction);
}
transaction = db.startTransaction(false);
try {
db.setGroupVisibility(transaction, contactId, groupId, SHARED);

View File

@@ -53,6 +53,7 @@ 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.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;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
@@ -74,6 +75,7 @@ import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -1713,6 +1715,39 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close();
}
@Test
public void testSetContactAlias() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
true, true));
// The contact should have no alias
Contact contact = db.getContact(txn, contactId);
assertNull(contact.getAlias());
// Set a contact alias
String alias = getRandomString(MAX_AUTHOR_NAME_LENGTH);
db.setContactAlias(txn, contactId, alias);
// The contact should have an alias
contact = db.getContact(txn, contactId);
assertEquals(alias, contact.getAlias());
// Set the contact alias null
db.setContactAlias(txn, contactId, null);
// The contact should have no alias
contact = db.getContact(txn, contactId);
assertNull(contact.getAlias());
db.commitTransaction(txn);
db.close();
}
@Test
public void testSetMessageState() throws Exception {
Database<Connection> db = open(false);

View File

@@ -1,7 +1,5 @@
package org.briarproject.bramble.identity;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PrivateKey;
@@ -9,9 +7,7 @@ import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.test.BrambleMockTestCase;
@@ -19,15 +15,9 @@ import org.jmock.Expectations;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.Author.Status.UNKNOWN;
import static org.briarproject.bramble.api.identity.Author.Status.UNVERIFIED;
import static org.briarproject.bramble.api.identity.Author.Status.VERIFIED;
import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.junit.Assert.assertEquals;
@@ -107,60 +97,4 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
assertEquals(localAuthor, identityManager.getLocalAuthor());
}
@Test
public void testGetAuthorStatus() throws DbException {
Author author = getAuthor();
AuthorId authorId = author.getId();
Collection<Contact> contacts = new ArrayList<>();
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).getLocalAuthors(txn);
will(returnValue(localAuthors));
oneOf(db).getContactsByAuthorId(txn, authorId);
will(returnValue(contacts));
oneOf(db).endTransaction(txn);
}});
assertEquals(UNKNOWN, identityManager.getAuthorStatus(authorId));
// add one unverified contact
Contact contact = new Contact(new ContactId(1), author,
localAuthor.getId(), false, true);
contacts.add(contact);
checkAuthorStatusContext(authorId, contacts);
assertEquals(UNVERIFIED, identityManager.getAuthorStatus(authorId));
// add one verified contact
Contact contact2 = new Contact(new ContactId(1), author,
localAuthor.getId(), true, true);
contacts.add(contact2);
checkAuthorStatusContext(authorId, contacts);
assertEquals(VERIFIED, identityManager.getAuthorStatus(authorId));
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
will(returnValue(txn));
never(db).getLocalAuthors(txn);
never(db).getContactsByAuthorId(txn, authorId);
oneOf(db).endTransaction(txn);
}});
assertEquals(OURSELVES,
identityManager.getAuthorStatus(localAuthor.getId()));
}
private void checkAuthorStatusContext(AuthorId authorId,
Collection<Contact> contacts) throws DbException {
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
will(returnValue(txn));
never(db).getLocalAuthors(txn);
oneOf(db).getContactsByAuthorId(txn, authorId);
will(returnValue(contacts));
oneOf(db).endTransaction(txn);
}});
}
}

View File

@@ -39,6 +39,7 @@ import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -612,7 +613,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
private Contact getContact(boolean active) {
ContactId c = new ContactId(nextContactId++);
return new Contact(c, getAuthor(), localAuthor.getId(),
true, active);
getRandomString(5), true, active);
}
private void expectGetLocalProperties(Transaction txn) throws Exception {

View File

@@ -0,0 +1,24 @@
package org.briarproject.bramble.test;
import org.briarproject.bramble.api.db.DbCallable;
import org.briarproject.bramble.api.db.DbRunnable;
import org.briarproject.bramble.api.db.Transaction;
import org.jmock.Expectations;
public class DbExpectations extends Expectations {
protected <E extends Exception> DbRunnable<E> withDbRunnable(
Transaction txn) {
addParameterMatcher(any(DbRunnable.class));
currentBuilder().setAction(new RunTransactionAction(txn));
return null;
}
protected <R, E extends Exception> DbCallable<R, E> withDbCallable(
Transaction txn) {
addParameterMatcher(any(DbCallable.class));
currentBuilder().setAction(new RunTransactionWithResultAction(txn));
return null;
}
}

View File

@@ -0,0 +1,30 @@
package org.briarproject.bramble.test;
import org.briarproject.bramble.api.db.DbRunnable;
import org.briarproject.bramble.api.db.Transaction;
import org.hamcrest.Description;
import org.jmock.api.Action;
import org.jmock.api.Invocation;
public class RunTransactionAction implements Action {
private final Transaction txn;
@SuppressWarnings("WeakerAccess")
public RunTransactionAction(Transaction txn) {
this.txn = txn;
}
@Override
public Object invoke(Invocation invocation) throws Throwable {
DbRunnable task = (DbRunnable) invocation.getParameter(1);
task.run(txn);
return null;
}
@Override
public void describeTo(Description description) {
description.appendText("runs a task inside a database transaction");
}
}

View File

@@ -0,0 +1,27 @@
package org.briarproject.bramble.test;
import org.briarproject.bramble.api.db.DbCallable;
import org.briarproject.bramble.api.db.Transaction;
import org.hamcrest.Description;
import org.jmock.api.Action;
import org.jmock.api.Invocation;
public class RunTransactionWithResultAction implements Action {
private final Transaction txn;
public RunTransactionWithResultAction(Transaction txn) {
this.txn = txn;
}
@Override
public Object invoke(Invocation invocation) throws Throwable {
DbCallable task = (DbCallable) invocation.getParameter(1);
return task.call(txn);
}
@Override
public void describeTo(Description description) {
description.appendText("runs a task inside a database transaction");
}
}

View File

@@ -33,6 +33,7 @@ import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals;
public class KeyManagerImplTest extends BrambleMockTestCase {
@@ -66,10 +67,10 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
Author remoteAuthor = getAuthor();
AuthorId localAuthorId = new AuthorId(getRandomId());
Collection<Contact> contacts = new ArrayList<>();
contacts.add(new Contact(contactId, remoteAuthor, localAuthorId, true,
true));
contacts.add(new Contact(contactId, remoteAuthor, localAuthorId,
getRandomString(5), true, true));
contacts.add(new Contact(inactiveContactId, remoteAuthor, localAuthorId,
true, false));
getRandomString(5), true, false));
SimplexPluginFactory pluginFactory =
context.mock(SimplexPluginFactory.class);
Collection<SimplexPluginFactory> factories =

View File

@@ -38,6 +38,7 @@ import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
@@ -56,7 +57,8 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Contact contact = new Contact(new ContactId(123),
getAuthor(), getLocalAuthor().getId(), true, true);
getAuthor(), getLocalAuthor().getId(), getRandomString(5), true,
true);
private final ClientId clientId = getClientId();
private final long now = System.currentTimeMillis();
private final Transaction txn = new Transaction(null, false);

View File

@@ -22,8 +22,8 @@ android {
defaultConfig {
minSdkVersion 15
targetSdkVersion 26
versionCode 10104
versionName "1.1.4"
versionCode 10103
versionName "1.1.3"
applicationId "org.briarproject.briar.android"
buildConfigField "String", "GitHash",
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
@@ -105,6 +105,7 @@ dependencies {
implementation "com.android.support:cardview-v7:$supportVersion"
implementation "com.android.support:support-annotations:$supportVersion"
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation "android.arch.lifecycle:extensions:1.1.1"
implementation('ch.acra:acra:4.11') {
exclude module: 'support-v4'

View File

@@ -26,6 +26,7 @@ import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
import org.briarproject.briar.BriarCoreEagerSingletons;
import org.briarproject.briar.BriarCoreModule;
import org.briarproject.briar.android.contact.ConversationViewModel;
import org.briarproject.briar.android.login.SignInReminderReceiver;
import org.briarproject.briar.android.reporting.BriarReportSender;
import org.briarproject.briar.android.view.TextInputView;
@@ -164,6 +165,8 @@ public interface AndroidComponent
void inject(TextInputView textInputView);
void inject(ConversationViewModel conversationViewModel);
// Eager singleton load
void inject(AppModule.EagerSingletons init);
}

View File

@@ -3,7 +3,7 @@ package org.briarproject.briar.android.blog;
import android.support.annotation.NonNull;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.blog.BlogPostHeader;
@@ -15,6 +15,7 @@ import javax.annotation.concurrent.NotThreadSafe;
public class BlogPostItem implements Comparable<BlogPostItem> {
private final BlogPostHeader header;
@Nullable
protected String text;
private boolean read;
@@ -40,10 +41,11 @@ public class BlogPostItem implements Comparable<BlogPostItem> {
return header.getAuthor();
}
Status getAuthorStatus() {
return header.getAuthorStatus();
AuthorInfo getAuthorInfo() {
return header.getAuthorInfo();
}
@Nullable
public String getText() {
return text;
}

View File

@@ -14,7 +14,6 @@ import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R;
import org.briarproject.briar.android.view.AuthorView;
@@ -98,9 +97,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
// author and date
BlogPostHeader post = item.getPostHeader();
Author a = post.getAuthor();
author.setAuthor(a);
author.setAuthorStatus(post.getAuthorStatus());
author.setAuthor(post.getAuthor(), post.getAuthorInfo());
author.setDate(post.getTimestamp());
author.setPersona(
item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL);
@@ -143,8 +140,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
private void onBindComment(BlogCommentItem item) {
// reblogger
reblogger.setAuthor(item.getAuthor());
reblogger.setAuthorStatus(item.getAuthorStatus());
reblogger.setAuthor(item.getAuthor(), item.getAuthorInfo());
reblogger.setDate(item.getTimestamp());
if (!fullText) {
reblogger.setAuthorClickable(v -> listener.onAuthorClick(item));
@@ -165,8 +161,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
AuthorView author = v.findViewById(R.id.authorView);
TextView text = v.findViewById(R.id.textView);
author.setAuthor(c.getAuthor());
author.setAuthorStatus(c.getAuthorStatus());
author.setAuthor(c.getAuthor(), c.getAuthorInfo());
author.setDate(c.getTimestamp());
// TODO make author clickable #624

View File

@@ -0,0 +1,75 @@
package org.briarproject.briar.android.contact;
import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatDialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.briar.R;
public class AliasDialogFragment extends AppCompatDialogFragment {
final static String TAG = AliasDialogFragment.class.getName();
private ConversationViewModel viewModel;
private ContactId contactId;
private EditText aliasEditText;
public static AliasDialogFragment newInstance(ContactId id) {
AliasDialogFragment f = new AliasDialogFragment();
Bundle args = new Bundle();
args.putInt("contactId", id.getInt());
f.setArguments(args);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() == null) throw new IllegalArgumentException();
int contactIdInt = getArguments().getInt("contactId", -1);
if (contactIdInt == -1) throw new IllegalArgumentException();
contactId = new ContactId(contactIdInt);
setStyle(STYLE_NO_TITLE, R.style.BriarDialogTheme);
viewModel =
ViewModelProviders.of(getActivity()).get(ConversationViewModel.class);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_alias_dialog, container,
false);
aliasEditText = v.findViewById(R.id.aliasEditText);
Contact contact = viewModel.getContact().getValue();
String alias = contact == null ? null : contact.getAlias();
aliasEditText.setText(alias);
if (alias != null) aliasEditText.setSelection(alias.length());
Button setButton = v.findViewById(R.id.setButton);
setButton.setOnClickListener(v1 -> {
viewModel.setContactAlias(contactId,
aliasEditText.getText().toString());
getDialog().dismiss();
});
Button cancelButton = v.findViewById(R.id.cancelButton);
cancelButton.setOnClickListener(v1 -> getDialog().cancel());
return v;
}
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android.contact;
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.View;
import org.briarproject.bramble.api.contact.ContactId;
@@ -9,6 +10,7 @@ import org.briarproject.briar.android.util.BriarAdapter;
import javax.annotation.Nullable;
import static android.support.v7.util.SortedList.INVALID_POSITION;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
public abstract class BaseContactListAdapter<I extends ContactItem, VH extends ContactItemViewHolder<I>>
extends BriarAdapter<I, VH> {
@@ -23,15 +25,15 @@ public abstract class BaseContactListAdapter<I extends ContactItem, VH extends C
}
@Override
public void onBindViewHolder(VH ui, int position) {
public void onBindViewHolder(@NonNull VH ui, int position) {
I item = items.get(position);
ui.bind(item, listener);
}
@Override
public int compare(I c1, I c2) {
return c1.getContact().getAuthor().getName()
.compareTo(c2.getContact().getAuthor().getName());
return getContactDisplayName(c1.getContact())
.compareTo(getContactDisplayName(c2.getContact()));
}
@Override

View File

@@ -16,6 +16,8 @@ import javax.annotation.Nullable;
import im.delight.android.identicons.IdenticonDrawable;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
@UiThread
@NotNullByDefault
public class ContactItemViewHolder<I extends ContactItem>
@@ -41,8 +43,7 @@ public class ContactItemViewHolder<I extends ContactItem>
Author author = item.getContact().getAuthor();
avatar.setImageDrawable(
new IdenticonDrawable(author.getId().getBytes()));
String contactName = author.getName();
name.setText(contactName);
name.setText(getContactDisplayName(item.getContact()));
if (bulb != null) {
// online/offline

View File

@@ -1,7 +1,8 @@
package org.briarproject.briar.android.contact;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
@@ -23,7 +24,6 @@ import android.widget.TextView;
import android.widget.Toast;
import org.briarproject.bramble.api.FormatException;
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.ContactRemovedEvent;
@@ -34,7 +34,6 @@ import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
@@ -131,8 +130,8 @@ public class ConversationActivity extends BriarActivity
Executor cryptoExecutor;
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>();
private final MutableLiveData<String> contactName = new MutableLiveData<>();
private ConversationViewModel viewModel;
private ConversationVisitor visitor;
private ConversationAdapter adapter;
private Toolbar toolbar;
@@ -166,8 +165,6 @@ public class ConversationActivity extends BriarActivity
private volatile ContactId contactId;
@Nullable
private volatile AuthorId contactAuthorId;
@Nullable
private volatile GroupId messagingGroupId;
@SuppressWarnings("ConstantConditions")
@@ -176,6 +173,9 @@ public class ConversationActivity extends BriarActivity
setSceneTransitionAnimation();
super.onCreate(state);
viewModel =
ViewModelProviders.of(this).get(ConversationViewModel.class);
Intent i = getIntent();
int id = i.getIntExtra(CONTACT_ID, -1);
if (id == -1) throw new IllegalStateException();
@@ -185,16 +185,29 @@ public class ConversationActivity extends BriarActivity
// Custom Toolbar
toolbar = setUpCustomToolbar(true);
if (toolbar != null) {
toolbarAvatar = toolbar.findViewById(R.id.contactAvatar);
toolbarStatus = toolbar.findViewById(R.id.contactStatus);
toolbarTitle = toolbar.findViewById(R.id.contactName);
}
toolbarAvatar = toolbar.findViewById(R.id.contactAvatar);
toolbarStatus = toolbar.findViewById(R.id.contactStatus);
toolbarTitle = toolbar.findViewById(R.id.contactName);
viewModel.getContactAuthorId().observe(this, authorId -> {
toolbarAvatar.setImageDrawable(
new IdenticonDrawable(authorId.getBytes()));
// we only need this once
viewModel.getContactAuthorId().removeObservers(this);
});
viewModel.getContactDisplayName().observe(this, contactName -> {
toolbarTitle.setText(contactName);
});
viewModel.isContactDeleted().observe(this, deleted -> {
if (deleted != null && deleted) finish();
});
viewModel.loadContact(contactId);
setTransitionName(toolbarAvatar, getAvatarTransitionName(contactId));
setTransitionName(toolbarStatus, getBulbTransitionName(contactId));
visitor = new ConversationVisitor(this, this, contactName);
visitor = new ConversationVisitor(this, this,
viewModel.getContactDisplayName());
adapter = new ConversationAdapter(this, this);
list = findViewById(R.id.conversationView);
list.setLayoutManager(new LinearLayoutManager(this));
@@ -229,7 +242,19 @@ public class ConversationActivity extends BriarActivity
notificationManager.blockContactNotification(contactId);
notificationManager.clearContactNotification(contactId);
displayContactOnlineStatus();
loadContactDetailsAndMessages();
LiveData<String> contactName = viewModel.getContactDisplayName();
if (contactName.getValue() == null) {
// wait for contact name to be initialized
contactName.observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String cName) {
if (cName != null) {
loadMessages();
contactName.removeObserver(this);
}
}
});
} else loadMessages();
list.startPeriodicUpdate();
}
@@ -266,6 +291,10 @@ public class ConversationActivity extends BriarActivity
intent.putExtra(CONTACT_ID, contactId.getInt());
startActivityForResult(intent, REQUEST_INTRODUCTION);
return true;
case R.id.action_set_alias:
AliasDialogFragment.newInstance(contactId).show(
getSupportFragmentManager(), AliasDialogFragment.TAG);
return true;
case R.id.action_social_remove_person:
askToRemoveContact();
return true;
@@ -274,36 +303,6 @@ public class ConversationActivity extends BriarActivity
}
}
private void loadContactDetailsAndMessages() {
runOnDbThread(() -> {
try {
long start = now();
if (contactAuthorId == null) {
Contact contact = contactManager.getContact(contactId);
contactName.postValue(contact.getAuthor().getName());
contactAuthorId = contact.getAuthor().getId();
}
logDuration(LOG, "Loading contact", start);
loadMessages();
displayContactDetails();
} catch (NoSuchContactException e) {
finishOnUiThread();
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
// contactAuthorId and contactName are expected to be set
private void displayContactDetails() {
runOnUiThreadUnlessDestroyed(() -> {
//noinspection ConstantConditions
toolbarAvatar.setImageDrawable(
new IdenticonDrawable(contactAuthorId.getBytes()));
toolbarTitle.setText(contactName.getValue());
});
}
private void displayContactOnlineStatus() {
runOnUiThreadUnlessDestroyed(() -> {
if (connectionRegistry.isConnected(contactId)) {
@@ -453,15 +452,17 @@ public class ConversationActivity extends BriarActivity
private void onNewPrivateMessage(PrivateMessageHeader h) {
runOnUiThreadUnlessDestroyed(() -> {
if (h instanceof PrivateRequest || h instanceof PrivateResponse) {
String cName = contactName.getValue();
String cName = viewModel.getContactDisplayName().getValue();
if (cName == null) {
// Wait for the contact name to be loaded
contactName.observe(this, new Observer<String>() {
viewModel.getContactDisplayName()
.observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String cName) {
if (cName != null) {
addConversationItem(h.accept(visitor));
contactName.removeObserver(this);
viewModel.getContactDisplayName()
.removeObserver(this);
}
}
});

View File

@@ -0,0 +1,100 @@
package org.briarproject.briar.android.contact;
import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.Transformations;
import android.support.annotation.NonNull;
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.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.briar.android.AndroidComponent;
import org.briarproject.briar.android.BriarApplication;
import org.briarproject.briar.android.util.UiUtils;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
public class ConversationViewModel extends AndroidViewModel {
private static Logger LOG =
Logger.getLogger(ConversationViewModel.class.getName());
@Inject
@DatabaseExecutor
Executor dbExecutor;
@Inject
ContactManager contactManager;
private final MutableLiveData<Contact> contact = new MutableLiveData<>();
private final LiveData<AuthorId> contactAuthorId =
Transformations.map(contact, c -> c.getAuthor().getId());
private final LiveData<String> contactName =
Transformations.map(contact, UiUtils::getContactDisplayName);
private final MutableLiveData<Boolean> contactDeleted =
new MutableLiveData<>();
public ConversationViewModel(@NonNull Application application) {
super(application);
AndroidComponent component =
((BriarApplication) application).getApplicationComponent();
component.inject(this);
contactDeleted.setValue(false);
}
void loadContact(ContactId contactId) {
dbExecutor.execute(() -> {
try {
long start = now();
contact.postValue(contactManager.getContact(contactId));
logDuration(LOG, "Loading contact", start);
} catch (NoSuchContactException e) {
contactDeleted.postValue(true);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
void setContactAlias(ContactId contactId, String alias) {
dbExecutor.execute(() -> {
try {
contactManager.setContactAlias(contactId,
alias.isEmpty() ? null : alias);
loadContact(contactId);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
LiveData<Contact> getContact() {
return contact;
}
LiveData<AuthorId> getContactAuthorId() {
return contactAuthorId;
}
LiveData<String> getContactDisplayName() {
return contactName;
}
LiveData<Boolean> isContactDeleted() {
return contactDeleted;
}
}

View File

@@ -24,6 +24,7 @@ import static org.briarproject.briar.android.contact.ConversationRequestItem.Req
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.FORUM;
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.GROUP;
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.INTRODUCTION;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
@UiThread
@NotNullByDefault
@@ -188,33 +189,36 @@ class ConversationVisitor implements PrivateMessageVisitor<ConversationItem> {
@Override
public ConversationItem visitIntroductionRequest(IntroductionRequest r) {
String name = getContactDisplayName(r.getNameable(), r.getAlias());
if (r.isLocal()) {
String text = ctx.getString(R.string.introduction_request_sent,
contactName.getValue(), r.getName());
contactName.getValue(), name);
return new ConversationNoticeOutItem(text, r);
} else {
String text = ctx.getString(R.string.introduction_request_received,
contactName.getValue(), r.getName());
contactName.getValue(), name);
return new ConversationRequestItem(text, INTRODUCTION, r);
}
}
@Override
public ConversationItem visitIntroductionResponse(IntroductionResponse r) {
String introducedAuthor =
getContactDisplayName(r.getIntroducedAuthor(),
r.getIntroducedAuthorInfo().getAlias());
if (r.isLocal()) {
String text;
if (r.wasAccepted()) {
String introducee = r.getIntroducedAuthor().getName();
text = ctx.getString(
R.string.introduction_response_accepted_sent,
introducee)
introducedAuthor)
+ "\n\n" + ctx.getString(
R.string.introduction_response_accepted_sent_info,
introducee);
introducedAuthor);
} else {
text = ctx.getString(
R.string.introduction_response_declined_sent,
r.getIntroducedAuthor().getName());
introducedAuthor);
}
return new ConversationNoticeOutItem(text, r);
} else {
@@ -223,17 +227,17 @@ class ConversationVisitor implements PrivateMessageVisitor<ConversationItem> {
text = ctx.getString(
R.string.introduction_response_accepted_received,
contactName.getValue(),
r.getIntroducedAuthor().getName());
introducedAuthor);
} else if (r.isIntroducer()) {
text = ctx.getString(
R.string.introduction_response_declined_received,
contactName.getValue(),
r.getIntroducedAuthor().getName());
introducedAuthor);
} else {
text = ctx.getString(
R.string.introduction_response_declined_received_by_introducee,
contactName.getValue(),
r.getIntroducedAuthor().getName());
introducedAuthor);
}
return new ConversationNoticeInItem(text, r);
}

View File

@@ -1,7 +1,7 @@
package org.briarproject.briar.android.forum;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.android.threaded.ThreadItem;
import org.briarproject.briar.api.forum.ForumPostHeader;
@@ -14,11 +14,11 @@ class ForumItem extends ThreadItem {
ForumItem(ForumPostHeader h, String text) {
super(h.getId(), h.getParentId(), text, h.getTimestamp(), h.getAuthor(),
h.getAuthorStatus(), h.isRead());
h.getAuthorInfo(), h.isRead());
}
ForumItem(MessageId messageId, @Nullable MessageId parentId, String text,
long timestamp, Author author, Status status) {
long timestamp, Author author, AuthorInfo status) {
super(messageId, parentId, text, timestamp, author, status, true);
}

View File

@@ -38,6 +38,7 @@ import static android.view.View.VISIBLE;
import static android.widget.Toast.LENGTH_SHORT;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_TEXT_LENGTH;
public class IntroductionMessageFragment extends BaseFragment
@@ -148,8 +149,8 @@ public class IntroductionMessageFragment extends BaseFragment
c2.getAuthor().getId().getBytes()));
// set contact names
ui.contactName1.setText(c1.getAuthor().getName());
ui.contactName2.setText(c2.getAuthor().getName());
ui.contactName1.setText(getContactDisplayName(c1));
ui.contactName2.setText(getContactDisplayName(c2));
// hide progress bar
ui.progressBar.setVisibility(GONE);

View File

@@ -4,7 +4,7 @@ import android.support.annotation.LayoutRes;
import android.support.annotation.UiThread;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R;
@@ -22,14 +22,14 @@ class GroupMessageItem extends ThreadItem {
private GroupMessageItem(MessageId messageId, GroupId groupId,
@Nullable MessageId parentId, String text, long timestamp,
Author author, Status status, boolean isRead) {
Author author, AuthorInfo status, boolean isRead) {
super(messageId, parentId, text, timestamp, author, status, isRead);
this.groupId = groupId;
}
GroupMessageItem(GroupMessageHeader h, String text) {
this(h.getId(), h.getGroupId(), h.getParentId(), text, h.getTimestamp(),
h.getAuthor(), h.getAuthorStatus(), h.isRead());
h.getAuthor(), h.getAuthorInfo(), h.isRead());
}
public GroupId getGroupId() {

View File

@@ -9,7 +9,8 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.threaded.BaseThreadItemViewHolder;
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
@UiThread
@NotNullByDefault
@@ -36,24 +37,27 @@ class JoinMessageItemViewHolder
if (item.isInitial()) {
textView.setText(R.string.groups_member_created_you);
} else {
textView.setText(
getContext().getString(R.string.groups_member_joined,
item.getAuthor().getName()));
String name = getContactDisplayName(item.getAuthor(),
item.getAuthorInfo().getAlias());
textView.setText(getContext()
.getString(R.string.groups_member_joined, name));
}
}
private void bind(JoinMessageItem item) {
Context ctx = getContext();
String name = getContactDisplayName(item.getAuthor(),
item.getAuthorInfo().getAlias());
if (item.isInitial()) {
textView.setText(ctx.getString(R.string.groups_member_created,
item.getAuthor().getName()));
textView.setText(
ctx.getString(R.string.groups_member_created, name));
} else {
if (item.getStatus() == OURSELVES) {
if (item.getAuthorInfo().getStatus() == OURSELVES) {
textView.setText(R.string.groups_member_joined_you);
} else {
textView.setText(ctx.getString(R.string.groups_member_joined,
item.getAuthor().getName()));
textView.setText(
ctx.getString(R.string.groups_member_joined, name));
}
}
}

View File

@@ -9,6 +9,8 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationItem;
import javax.annotation.Nullable;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
class GroupInvitationViewHolder
extends InvitationViewHolder<GroupInvitationItem> {
@@ -24,7 +26,7 @@ class GroupInvitationViewHolder
sharedBy.setText(
sharedBy.getContext().getString(R.string.groups_created_by,
item.getCreator().getAuthor().getName()));
getContactDisplayName(item.getCreator())));
}
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android.privategroup.list;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
@@ -12,12 +13,15 @@ import org.briarproject.briar.api.privategroup.PrivateGroup;
class GroupItem {
private final PrivateGroup privateGroup;
private final AuthorInfo authorInfo;
private int messageCount, unreadCount;
private long timestamp;
private boolean dissolved;
GroupItem(PrivateGroup privateGroup, GroupCount count, boolean dissolved) {
GroupItem(PrivateGroup privateGroup, AuthorInfo authorInfo,
GroupCount count, boolean dissolved) {
this.privateGroup = privateGroup;
this.authorInfo = authorInfo;
this.messageCount = count.getMsgCount();
this.unreadCount = count.getUnreadCount();
this.timestamp = count.getLatestMsgTime();
@@ -46,6 +50,10 @@ class GroupItem {
return privateGroup.getCreator();
}
AuthorInfo getCreatorInfo() {
return authorInfo;
}
String getName() {
return privateGroup.getName();
}

View File

@@ -2,12 +2,15 @@ package org.briarproject.briar.android.privategroup.list;
import android.support.annotation.CallSuper;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
@@ -30,7 +33,9 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -52,6 +57,7 @@ class GroupListControllerImpl extends DbControllerImpl
private final PrivateGroupManager groupManager;
private final GroupInvitationManager groupInvitationManager;
private final ContactManager contactManager;
private final AndroidNotificationManager notificationManager;
private final EventBus eventBus;
@@ -61,10 +67,12 @@ class GroupListControllerImpl extends DbControllerImpl
GroupListControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, PrivateGroupManager groupManager,
GroupInvitationManager groupInvitationManager,
ContactManager contactManager,
AndroidNotificationManager notificationManager, EventBus eventBus) {
super(dbExecutor, lifecycleManager);
this.groupManager = groupManager;
this.groupInvitationManager = groupInvitationManager;
this.contactManager = contactManager;
this.notificationManager = notificationManager;
this.eventBus = eventBus;
}
@@ -153,12 +161,22 @@ class GroupListControllerImpl extends DbControllerImpl
Collection<PrivateGroup> groups =
groupManager.getPrivateGroups();
List<GroupItem> items = new ArrayList<>(groups.size());
Map<AuthorId, AuthorInfo> authorInfos = new HashMap<>();
for (PrivateGroup g : groups) {
try {
GroupId id = g.getId();
AuthorId authorId = g.getCreator().getId();
AuthorInfo authorInfo;
if (authorInfos.containsKey(authorId)) {
authorInfo = authorInfos.get(authorId);
} else {
authorInfo = contactManager.getAuthorInfo(authorId);
authorInfos.put(authorId, authorInfo);
}
GroupCount count = groupManager.getGroupCount(id);
boolean dissolved = groupManager.isDissolved(id);
items.add(new GroupItem(g, count, dissolved));
items.add(
new GroupItem(g, authorInfo, count, dissolved));
} catch (NoSuchGroupException e) {
// Continue
}

View File

@@ -20,6 +20,7 @@ import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_NAME;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -60,8 +61,9 @@ class GroupViewHolder extends RecyclerView.ViewHolder {
name.setText(group.getName());
// Creator
creator.setText(ctx.getString(R.string.groups_created_by,
group.getCreator().getName()));
String creatorName = getContactDisplayName(group.getCreator(),
group.getCreatorInfo().getAlias());
creator.setText(ctx.getString(R.string.groups_created_by, creatorName));
if (!group.isDissolved()) {
// full visibility

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android.privategroup.memberlist;
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -8,6 +9,8 @@ import android.view.ViewGroup;
import org.briarproject.briar.R;
import org.briarproject.briar.android.util.BriarAdapter;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
class MemberListAdapter extends
BriarAdapter<MemberListItem, MemberListItemHolder> {
@@ -15,8 +18,9 @@ class MemberListAdapter extends
super(context, MemberListItem.class);
}
@NonNull
@Override
public MemberListItemHolder onCreateViewHolder(ViewGroup viewGroup,
public MemberListItemHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
int i) {
View v = LayoutInflater.from(ctx).inflate(
R.layout.list_item_group_member, viewGroup, false);
@@ -24,13 +28,18 @@ class MemberListAdapter extends
}
@Override
public void onBindViewHolder(MemberListItemHolder ui, int position) {
public void onBindViewHolder(@NonNull MemberListItemHolder ui,
int position) {
ui.bind(items.get(position));
}
@Override
public int compare(MemberListItem m1, MemberListItem m2) {
return m1.getMember().getName().compareTo(m2.getMember().getName());
String n1 = getContactDisplayName(m1.getMember(),
m1.getAuthorInfo().getAlias());
String n2 = getContactDisplayName(m2.getMember(),
m2.getAuthorInfo().getAlias());
return n1.compareTo(n2);
}
@Override

View File

@@ -2,7 +2,8 @@ package org.briarproject.briar.android.privategroup.memberlist;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.AuthorInfo.Status;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.privategroup.GroupMember;
@@ -25,8 +26,12 @@ class MemberListItem {
return groupMember.getAuthor();
}
AuthorInfo getAuthorInfo() {
return groupMember.getAuthorInfo();
}
Status getStatus() {
return groupMember.getStatus();
return groupMember.getAuthorInfo().getStatus();
}
boolean isCreator() {

View File

@@ -10,7 +10,10 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.view.AuthorView;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
@UiThread
@NotNullByDefault
@@ -29,33 +32,33 @@ class MemberListItemHolder extends RecyclerView.ViewHolder {
protected void bind(MemberListItem item) {
// member name, avatar and status
author.setAuthor(item.getMember());
author.setAuthorStatus(item.getStatus());
author.setAuthor(item.getMember(), item.getAuthorInfo());
// online status of visible contacts
if (item.getContactId() != null) {
bulb.setVisibility(View.VISIBLE);
bulb.setVisibility(VISIBLE);
if (item.isOnline()) {
bulb.setImageResource(R.drawable.contact_connected);
} else {
bulb.setImageResource(R.drawable.contact_disconnected);
}
} else {
bulb.setVisibility(View.GONE);
bulb.setVisibility(GONE);
}
// text shown for creator
if (item.isCreator()) {
creator.setVisibility(View.VISIBLE);
creator.setVisibility(VISIBLE);
if (item.getStatus() == OURSELVES) {
creator.setText(R.string.groups_member_created_you);
} else {
String name = getContactDisplayName(item.getMember(),
item.getAuthorInfo().getAlias());
creator.setText(creator.getContext()
.getString(R.string.groups_member_created,
item.getMember().getName()));
.getString(R.string.groups_member_created, name));
}
} else {
creator.setVisibility(View.GONE);
creator.setVisibility(GONE);
}
}

View File

@@ -14,6 +14,7 @@ import javax.annotation.Nullable;
import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityIcon;
import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityString;
import static org.briarproject.briar.android.util.UiUtils.GREY_OUT;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
@UiThread
@NotNullByDefault
@@ -36,7 +37,7 @@ class RevealableContactViewHolder
icon.setImageResource(getVisibilityIcon(item.getVisibility()));
info.setText(
getVisibilityString(info.getContext(), item.getVisibility(),
item.getContact().getAuthor().getName()));
getContactDisplayName(item.getContact())));
}
@Override

View File

@@ -13,6 +13,8 @@ import java.util.Collection;
import javax.annotation.Nullable;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
class SharingInvitationViewHolder
extends InvitationViewHolder<SharingInvitationItem> {
@@ -28,7 +30,7 @@ class SharingInvitationViewHolder
Collection<String> names = new ArrayList<>();
for (Contact c : item.getNewSharers())
names.add(c.getAuthor().getName());
names.add(getContactDisplayName(c));
sharedBy.setText(
sharedBy.getContext().getString(R.string.shared_by_format,
StringUtils.join(names, ", ")));

View File

@@ -43,9 +43,8 @@ public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
public void bind(I item, ThreadItemListener<I> listener) {
textView.setText(StringUtils.trim(item.getText()));
author.setAuthor(item.getAuthor());
author.setAuthor(item.getAuthor(), item.getAuthorInfo());
author.setDate(item.getTimestamp());
author.setAuthorStatus(item.getStatus());
if (item.isHighlighted()) {
layout.setActivated(true);

View File

@@ -1,7 +1,7 @@
package org.briarproject.briar.android.threaded;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.MessageTree.MessageNode;
@@ -21,19 +21,19 @@ public abstract class ThreadItem implements MessageNode {
private final String text;
private final long timestamp;
private final Author author;
private final Status status;
private final AuthorInfo authorInfo;
private int level = UNDEFINED;
private boolean isRead, highlighted;
public ThreadItem(MessageId messageId, @Nullable MessageId parentId,
String text, long timestamp, Author author, Status status,
String text, long timestamp, Author author, AuthorInfo authorInfo,
boolean isRead) {
this.messageId = messageId;
this.parentId = parentId;
this.text = text;
this.timestamp = timestamp;
this.author = author;
this.status = status;
this.authorInfo = authorInfo;
this.isRead = isRead;
this.highlighted = false;
}
@@ -66,8 +66,8 @@ public abstract class ThreadItem implements MessageNode {
return author;
}
public Status getStatus() {
return status;
public AuthorInfo getAuthorInfo() {
return authorInfo;
}
@Override

View File

@@ -33,7 +33,9 @@ import android.view.View;
import android.widget.TextView;
import org.acra.ACRA;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.system.AndroidExecutor;
@@ -74,6 +76,17 @@ public class UiUtils {
public static final int TEASER_LENGTH = 320;
public static final float GREY_OUT = 0.5f;
public static String getContactDisplayName(Author author,
@Nullable String alias) {
String name = author.getName();
if (alias == null) return name;
else return String.format("%s (%s)", alias, name);
}
public static String getContactDisplayName(Contact c) {
return getContactDisplayName(c.getAuthor(), c.getAlias());
}
public static void setError(TextInputLayout til, @Nullable String error,
boolean set) {
if (set) {
@@ -127,7 +140,9 @@ public class UiUtils {
return builder;
}
public static Spanned getSpanned(String s) {
public static Spanned getSpanned(@Nullable String s) {
// TODO move to HtmlCompat
// https://commonsware.com/blog/2018/05/29/at-last-htmlcompat.html
return Html.fromHtml(s);
}

View File

@@ -12,7 +12,7 @@ import android.widget.ImageView;
import android.widget.TextView;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.briar.R;
import org.briarproject.briar.android.util.UiUtils;
@@ -24,8 +24,9 @@ import im.delight.android.identicons.IdenticonDrawable;
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
import static android.graphics.Typeface.BOLD;
import static android.util.TypedValue.COMPLEX_UNIT_PX;
import static org.briarproject.bramble.api.identity.Author.Status.NONE;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.NONE;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
import static org.briarproject.briar.android.util.UiUtils.resolveAttribute;
@UiThread
@@ -70,24 +71,20 @@ public class AuthorView extends ConstraintLayout {
this(context, null);
}
public void setAuthor(Author author) {
authorName.setText(author.getName());
public void setAuthor(Author author, AuthorInfo authorInfo) {
authorName
.setText(getContactDisplayName(author, authorInfo.getAlias()));
IdenticonDrawable d = new IdenticonDrawable(author.getId().getBytes());
avatar.setImageDrawable(d);
invalidate();
requestLayout();
}
public void setAuthorStatus(Status status) {
if (status != NONE) {
trustIndicator.setTrustLevel(status);
if (authorInfo.getStatus() != NONE) {
trustIndicator.setTrustLevel(authorInfo.getStatus());
trustIndicator.setVisibility(VISIBLE);
} else {
trustIndicator.setVisibility(GONE);
}
if (status == OURSELVES) {
if (authorInfo.getStatus() == OURSELVES) {
authorName.setTypeface(authorNameTypeface, BOLD);
} else {
authorName.setTypeface(authorNameTypeface, NORMAL);
@@ -123,7 +120,7 @@ public class AuthorView extends ConstraintLayout {
*
* Attention: RSS_FEED and RSS_FEED_REBLOGGED change the avatar
* and override the one set by
* {@link AuthorView#setAuthor(Author)}.
* {@link AuthorView#setAuthor(Author, AuthorInfo)}.
*/
public void setPersona(int persona) {
switch (persona) {

View File

@@ -3,14 +3,14 @@ package org.briarproject.briar.android.view;
import android.content.Context;
import android.support.annotation.UiThread;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.widget.ImageView;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorInfo.Status;
import org.briarproject.briar.R;
@UiThread
public class TrustIndicatorView extends ImageView {
public class TrustIndicatorView extends AppCompatImageView {
public TrustIndicatorView(Context context) {
super(context);

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/titleView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/set_contact_alias"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_large"
android:textStyle="bold"/>
<EditText
android:id="@+id/aliasEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="@string/set_contact_alias_hint"
android:inputType="textPersonName"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_medium"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_xxlarge"
android:layout_marginStart="@dimen/margin_xxlarge"
android:gravity="end"
android:orientation="horizontal">
<Button
android:id="@+id/cancelButton"
style="@style/BriarButtonFlat.Negative"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel"/>
<Button
android:id="@+id/setButton"
style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/set_alias"/>
</LinearLayout>
</LinearLayout>

View File

@@ -1,75 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/link_warning_title"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_large"
android:textStyle="bold"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_large"
android:text="@string/link_warning_intro"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_medium"/>
<TextView
android:id="@+id/urlView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_large"
android:textColor="?android:attr/textColorPrimary"
android:textIsSelectable="true"
android:textSize="@dimen/text_size_medium"
android:typeface="monospace"
tools:text="http://very.bad.site.com"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_large"
android:text="@string/link_warning_text"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_medium"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/margin_large">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/link_warning_title"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_large"
android:textStyle="bold"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_large"
android:text="@string/link_warning_intro"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_medium"/>
<TextView
android:id="@+id/urlView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_large"
android:textIsSelectable="true"
android:typeface="monospace"
tools:text="http://very.bad.site.com"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_large"
android:text="@string/link_warning_text"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_medium"/>
</LinearLayout>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="@dimen/button_size">
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_xxlarge"
android:layout_marginStart="@dimen/margin_xxlarge"
android:gravity="end"
android:orientation="horizontal">
<Button
android:id="@+id/cancelButton"
style="@style/BriarButtonFlat.Positive"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel"
app:layout_constraintEnd_toStartOf="@+id/openButton"
app:layout_constraintStart_toStartOf="parent"/>
android:text="@string/cancel"/>
<Button
android:id="@+id/openButton"
style="@style/BriarButtonFlat.Negative"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/link_warning_open_link"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/cancelButton"/>
android:text="@string/link_warning_open_link"/>
</android.support.constraint.ConstraintLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -10,6 +10,12 @@
android:enabled="false"
app:showAsAction="never"/>
<item
android:id="@+id/action_set_alias"
android:title="@string/set_contact_alias"
android:enabled="true"
app:showAsAction="never"/>
<item
android:id="@+id/action_social_remove_person"
android:icon="@drawable/action_delete_white"

View File

@@ -471,6 +471,7 @@
<string name="permission_camera_title">إذن الكاميرا</string>
<string name="permission_camera_request_body">للتمكن من مسح رمز QR، يحتاج Briar (براير) إلى إستعمال الكاميرا.</string>
<string name="permission_camera_denied_body">قد رفضت إعطاء إذن الكاميرا، لكن إضافة جهات إتصال يتطلب إستعمال الكاميرا.\n\nالرجاء منح الإذن.</string>
<string name="permission_camera_denied_toast">لم يتم منح الإذن بإستعمال الكاميرا</string>
<string name="qr_code">رمز QR</string>
<string name="show_qr_code_fullscreen">اظهار رمز QR بوضع ملء الشاشة</string>
<!--App Locking-->

View File

@@ -430,6 +430,7 @@
<string name="permission_camera_title">Permís de la càmera</string>
<string name="permission_camera_request_body">Per escanejar el codi QR, Briar necessita accedir a la càmera.</string>
<string name="permission_camera_denied_body">Heu denegat l\'accés a la càmera però per afegir contactes cal utilitzar la càmera.\n\nRecomanem que permeteu l\'accés a la càmera.</string>
<string name="permission_camera_denied_toast">No s\'ha concedit el permís per accedir a la càmera</string>
<string name="qr_code">Codi QR</string>
<string name="show_qr_code_fullscreen">Mostra el codi QR a pantalla completa</string>
<!--App Locking-->

View File

@@ -403,6 +403,7 @@
<string name="permission_camera_title">Oprávnění pro přístup k fotoaparátu</string>
<string name="permission_camera_request_body">Pro scan QR kódu, Briar vyžaduje přístup k fotoaparátu.</string>
<string name="permission_camera_denied_body">Odmítli jste udělit oprávnění přístupu k fotoaparátu, avšak pro přidání kontaktů je nutné použití fotoaparátu.\n\nZvažte prosím, opětovné udělení přístupu.</string>
<string name="permission_camera_denied_toast">Oprávnění pro přístup k fotoaparátu nebylo uděleno</string>
<string name="qr_code">QR kód</string>
<string name="show_qr_code_fullscreen">Zobrazit QR kód na celou obrazovku</string>
<!--App Locking-->

View File

@@ -430,6 +430,7 @@
<string name="permission_camera_title">Berechtigung Kamera</string>
<string name="permission_camera_request_body">Um den QR-Code zu scannen, benötigt Briar Zugriff auf die Kamera.</string>
<string name="permission_camera_denied_body">Du hast den Zugriff auf die Kamera verweigert, aber das Hinzufügen von Kontakten erfordert die Verwendung der Kamera.\n\nBitte gewähre den Zugriff.</string>
<string name="permission_camera_denied_toast">Berechtigung für Kamera wurde nicht gewährt</string>
<string name="qr_code">QR-Code</string>
<string name="show_qr_code_fullscreen">QR-Code im Vollbildmodus anzeigen</string>
<!--App Locking-->

View File

@@ -48,7 +48,6 @@
<string name="download_briar_button">Descargar Briar 1.0</string>
<string name="startup_open_database">Descifrando la base de datos...</string>
<string name="startup_migrate_database">Actualizando la base de datos...</string>
<string name="startup_compact_database">Compactando base de datos...</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Abre el panel de navegación</string>
<string name="nav_drawer_close_description">Cierra el panel de navegación</string>
@@ -436,6 +435,7 @@
<string name="permission_camera_title">Permiso de cámara</string>
<string name="permission_camera_request_body">Para escanear el código QR, Briar necesita acceso a la cámara.</string>
<string name="permission_camera_denied_body">Has denegado el acceso a la cámara, pero para añadir contactos se requiere el uso de la cámara.\n\nPor favor considera la posibilidad de conceder el acceso.</string>
<string name="permission_camera_denied_toast">No se concedió el permiso de cámara</string>
<string name="qr_code">Código QR</string>
<string name="show_qr_code_fullscreen">Mostrar código QR a pantalla completa</string>
<!--App Locking-->

View File

@@ -48,7 +48,6 @@
<string name="download_briar_button">Deskargatu Briar 1.0</string>
<string name="startup_open_database">Datu-basea deszifratzen...</string>
<string name="startup_migrate_database">Datu-basea eguneratzen...</string>
<string name="startup_compact_database">Datu-basea trinkotzen…</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Ireki nabigazio-tiradera</string>
<string name="nav_drawer_close_description">Itxi nabigazio-tiradera</string>
@@ -436,6 +435,7 @@
<string name="permission_camera_title">Kamera erabiltzeko baimena</string>
<string name="permission_camera_request_body">QR kodea eskaneatzeko Briar-ek kamera atzitu behar du.</string>
<string name="permission_camera_denied_body">Kamera atzitzeko baimena ukatu duzu, baina kontaktuak gehitzeko kamera erabili behar da.\n\nMesedez baimendu kamera atzitzea.</string>
<string name="permission_camera_denied_toast">Ez da kamera erabiltzeko baimenik eman</string>
<string name="qr_code">QR kodea</string>
<string name="show_qr_code_fullscreen">Erakutsi QR kodea pantaila osoan</string>
<!--App Locking-->

View File

@@ -474,6 +474,7 @@
<string name="permission_camera_denied_body">شما دسترسی به دوربین را رد کرده اید، اما افزودن مخاطب نیاز به دوربین دارد.
لطفا اجازه دسترسی را بدهید.</string>
<string name="permission_camera_denied_toast">اجازه دسترسی به دوربین پذیرفته نشد</string>
<string name="qr_code">کد QR</string>
<string name="show_qr_code_fullscreen">نمایش کد QR به صورت فول اسکرین</string>
<!--App Locking-->

View File

@@ -430,6 +430,7 @@
<string name="permission_camera_title">Kameran käyttöoikeus</string>
<string name="permission_camera_request_body">Skannatakseen QR koodin, Briar tarvitsee luvan käyttää kameraa.</string>
<string name="permission_camera_denied_body">Olet kieltänyt käyttämästä kameraa, mutta yhteyshenkilöiden lisääminen vaatii kameran käyttöä.\n\nOle hyvä ja harkitse kameraluvan myöntämistä.</string>
<string name="permission_camera_denied_toast">Kameran käyttöoikeutta ei myönnetty</string>
<string name="qr_code">QR-koodi</string>
<string name="show_qr_code_fullscreen">Näytä QR-koodi koko näytöllä</string>
<!--App Locking-->

View File

@@ -132,7 +132,7 @@
<string name="qr_code_invalid">Le code QR est invalide</string>
<string name="qr_code_too_old">Le code QR que vous avez lu provient dune version plus ancienne de %s.\n\nVeuillez demander à votre contact de passer à la version la plus récente, puis ressayez.</string>
<string name="qr_code_too_new">Le code QR que vous avez lu provient dune version plus récente de %s.\n\nVeuillez passer à la version la plus récente, puis ressayez.</string>
<string name="camera_error">Erreur de lappareil photo</string>
<string name="camera_error">Erreur de la caméra</string>
<string name="connecting_to_device">Connexion à lappareil\u2026</string>
<string name="authenticating_with_device">Autentification avec lappareil\u2026</string>
<string name="connection_error_title">Impossible de se connecter à votre contact</string>
@@ -433,13 +433,10 @@
<string name="screen_filter_body">Une autre appli saffiche par-dessus Briar. Pour protéger votre sécurité, Briar ne répondra pas au toucher si une autre appli saffiche par-dessus.\n\nLes applis suivantes pourraient safficher par-dessus Briar : \n\n%1$s</string>
<string name="screen_filter_allow">Permettre à ces applis de safficher par-dessus</string>
<!--Permission Requests-->
<string name="permission_camera_title">Accès à lappareil photo</string>
<string name="permission_camera_request_body">Afin de lire le code QR, Briar doit accéder à lappareil photo.</string>
<string name="permission_location_title">Autorisation daccès à la position</string>
<string name="permission_location_request_body">Afin de découvrir des périphériques Bluetooth, Briar doit accéder à votre position.\n\nBriar nenregistre pas votre position et ne la partage avec personne.</string>
<string name="permission_camera_location_title">Appareil photo et position</string>
<string name="permission_camera_location_request_body">Afin de lire le code QR, Briar doit accéder à lappareil photo.\n\nAfin de découvrir des périphériques Bluetooth, Briar doit accéder à votre position.\n\nBriar nenregistre pas votre position et ne la partage avec personne.</string>
<string name="permission_camera_title">Accès à la caméra</string>
<string name="permission_camera_request_body">Pour lire le code QR, Briar doit accéder à la caméra.</string>
<string name="permission_camera_denied_body">Vous avez refusé laccès à la caméra, mais lajout de contacts exige lutilisation de celle-ci.\n\nVeuillez envisager dy accorder laccès.</string>
<string name="permission_camera_denied_toast">Laccès à la caméra na pas été accordé</string>
<string name="qr_code">Code QR</string>
<string name="show_qr_code_fullscreen">Afficher le code QR en plein écran</string>
<!--App Locking-->

View File

@@ -436,6 +436,7 @@
<string name="permission_camera_title">Permiso da cámara</string>
<string name="permission_camera_request_body">Para escanear códigos QR, Briar precisa acceso a cámara.</string>
<string name="permission_camera_denied_body">Denegou o permiso de acceso a cámara, pero é necesario para engadir contactos.\n\nPor favor, considere conceder o permiso.</string>
<string name="permission_camera_denied_toast">Non concedeu o acceso a cámara</string>
<string name="qr_code">Código QR</string>
<string name="show_qr_code_fullscreen">Amosar o código QR a pantalla completa</string>
<!--App Locking-->

View File

@@ -53,7 +53,6 @@
<string name="download_briar_button">הורד את Briar 1.0</string>
<string name="startup_open_database">מפענח מסד נתונים...</string>
<string name="startup_migrate_database">משדרג מסד נתונים...</string>
<string name="startup_compact_database">מכווץ מסד נתונים...</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">פתח את מגירת הניווט</string>
<string name="nav_drawer_close_description">סגור את מגירת הניווט</string>
@@ -318,7 +317,6 @@
<string name="blogs_remove_blog_ok">הסר</string>
<string name="blogs_blog_removed">בלוג הוסר</string>
<string name="blogs_reblog_comment_hint">הוסף תגובה (רשותי)</string>
<string name="blogs_reblog_button">פרסם מחדש</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">שתף בלוג</string>
<string name="blogs_sharing_error">הייתה שגיאה בשיתוף בלוג זה.</string>
@@ -463,12 +461,11 @@
<string name="progress_title_logout">מתנתק מן Briar...</string>
<!--Screen Filters & Tapjacking-->
<string name="screen_filter_title">ציפוי מסך התגלה</string>
<string name="screen_filter_body">יישום אחר מציירת מעל Briar. כדי להגן על אבטחתך, Briar לא יגיב לנגיעות כאשר יישום אחר מצייר מעל.\n\nהיישומים הבאים יכולים לצייר מעל:\n\n%1$s</string>
<string name="screen_filter_allow">התר ליישומים אלו לצייר מעל</string>
<!--Permission Requests-->
<string name="permission_camera_title">הרשאת מצלמה</string>
<string name="permission_camera_request_body">כדי לסרוק את קוד ה־QR, היישום Briar צריך גישה אל המצלמה.</string>
<string name="permission_camera_denied_body">דחית גישה אל המצלמה, אבל הוספת אנשי קשר דורשת שימוש במצלמה.\n\nאנא שקול הענקת גישה.</string>
<string name="permission_camera_denied_toast">הרשאת מצלמה לא הוענקה</string>
<string name="qr_code">קוד QR</string>
<string name="show_qr_code_fullscreen">הראה קוד QR במסך מלא</string>
<!--App Locking-->

View File

@@ -430,6 +430,7 @@
<string name="permission_camera_title">कैमरा अनुमति</string>
<string name="permission_camera_request_body">QR कोड को स्कैन करने के लिए, Briar को कैमरे तक पहुंच की आवश्यकता है।</string>
<string name="permission_camera_denied_body">आपने कैमरे तक पहुंच से वंचित किया है, लेकिन संपर्क जोड़ने के लिए कैमरे का उपयोग करने की आवश्यकता है। \ N \ n कृपया पहुंच प्रदान करने पर विचार करें।</string>
<string name="permission_camera_denied_toast">कैमरा अनुमति नहीं दी गई थी</string>
<string name="qr_code">क्यूआर कोड</string>
<string name="show_qr_code_fullscreen">क्यूआर कोड पूर्णस्क्रीन दिखाएं</string>
<!--App Locking-->

View File

@@ -433,6 +433,7 @@ Vigyázat: Ez végleg törli az identitásait, kapcsolatait és üzeneteit</stri
<string name="permission_camera_title">Kamera jogosultságok</string>
<string name="permission_camera_request_body">A QR kód olvasáshoz a Briar-nak szüksége van kamera hozzáférésre.</string>
<string name="permission_camera_denied_body">Megtiltotta hozzáférést a kamerához, de a kapcsolatok hozzáadásához szükséges a kamera.\n\nKérjük gondolja meg a jog megadását.</string>
<string name="permission_camera_denied_toast">A kamera jogosultság nincs megadva</string>
<string name="qr_code">QR kód</string>
<string name="show_qr_code_fullscreen">A QR kód teljes képernyősen</string>
<!--App Locking-->

View File

@@ -48,7 +48,6 @@
<string name="download_briar_button">Scarica Briar 1.0</string>
<string name="startup_open_database">Decrittazione database...</string>
<string name="startup_migrate_database">Aggiornamento database...</string>
<string name="startup_compact_database">Compattazione database…</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Apri la barra di navigazione</string>
<string name="nav_drawer_close_description">Chiudi la barra di navigazione</string>
@@ -436,6 +435,7 @@
<string name="permission_camera_title">Autorizzazione fotocamera</string>
<string name="permission_camera_request_body">Per scansionare il codice QR, Briar deve accedere alla fotocamera.</string>
<string name="permission_camera_denied_body">Hai negato l\'accesso alla fotocamera, ma questa serve per aggiungere i contatti.\n\nConsidera la possibilità di concedere l\'accesso.</string>
<string name="permission_camera_denied_toast">Autorizzazione fotocamera non concessa</string>
<string name="qr_code">Codice QR</string>
<string name="show_qr_code_fullscreen">Mostra codice QR a tutto schermo</string>
<!--App Locking-->

View File

@@ -430,6 +430,7 @@
<string name="permission_camera_title">Cameratoestemming</string>
<string name="permission_camera_request_body">Om de QR-code te scannen moet Briar toegang hebben tot de camera.</string>
<string name="permission_camera_denied_body">Je hebt toegang tot de camera niet vrijgegeven, terwijl het toevoegen van contacten de camera nodig heeft.\n\nOverweeg alsjeblieft toegang vrij te geven.</string>
<string name="permission_camera_denied_toast">Cameratoegang niet vrijgegeven</string>
<string name="qr_code">QR-code</string>
<string name="show_qr_code_fullscreen">Toon QR-code op volledig scherm</string>
<!--App Locking-->

View File

@@ -440,6 +440,7 @@ contactes quavètz partejat aqueste flux quitaràn benlèu de recebre las mes
<string name="permission_camera_title">Permission de la camèra</string>
<string name="permission_camera_request_body">Per numerizar lo còdi QR cal que Briar aja laccès a la camèra.</string>
<string name="permission_camera_denied_body">Avètz regetat laccès a la camèra, mas per ajustar de contacte cal utilizar la camèra.\n\n Mercés de li donar laccès</string>
<string name="permission_camera_denied_toast">La permission a la camèra es pas estada donada</string>
<string name="qr_code">Còdi QR</string>
<string name="show_qr_code_fullscreen">Mostrar lo còdi QR en ecran complèt</string>
<!--App Locking-->

View File

@@ -450,6 +450,7 @@
<string name="permission_camera_title">Dostęp do aparatu</string>
<string name="permission_camera_request_body">Aby zeskanować kod QR, Briar potrzebuje mieć dostęp do aparatu.</string>
<string name="permission_camera_denied_body">Odmówiłeś dostępu do kamery, lecz dodawanie kontaktów tego wymaga.\n\nProszę udzielić dostępu.</string>
<string name="permission_camera_denied_toast">Dostęp do aparatu nie został przyznany</string>
<string name="qr_code">Kod QR</string>
<string name="show_qr_code_fullscreen">Pokaż QR na pełnym ekranie</string>
<!--App Locking-->

View File

@@ -430,6 +430,7 @@
<string name="permission_camera_title">Permissão da câmera</string>
<string name="permission_camera_request_body">O Briar precisa acessar a câmera para pode escanear o código QR.</string>
<string name="permission_camera_denied_body">Você negou acesso à câmera, mas para adicionar contatos você precisa da câmera.\n\nPor favor, pense em liberar o acesso a ela.</string>
<string name="permission_camera_denied_toast">A permissão da câmera não foi concedida</string>
<string name="qr_code">Código QR</string>
<string name="show_qr_code_fullscreen">Mostrar o código QR na tela cheia</string>
<!--App Locking-->

View File

@@ -446,6 +446,7 @@
<string name="permission_camera_title">Permisiune de acces la camera foto</string>
<string name="permission_camera_request_body">Pentru a scana codul QR, Briar are nevoie să acceseze camera foto.</string>
<string name="permission_camera_denied_body">Ați refuzat accesul la camera foto, dar pentru a adăuga contacte este necesară folosirea camerei foto.\n\nVă rugăm să luați în considerare acordarea accesului.</string>
<string name="permission_camera_denied_toast">Permisiunea de acces la camera foto nu a fost acordată</string>
<string name="qr_code">Cod QR</string>
<string name="show_qr_code_fullscreen">Arată codul QR pe tot ecranul</string>
<!--App Locking-->

View File

@@ -452,6 +452,7 @@
<string name="permission_camera_title">Доступ к камере</string>
<string name="permission_camera_request_body">Для сканирования QR-кода Briar необходим доступ к камере.</string>
<string name="permission_camera_denied_body">Вы отказали в доступе к камере. Для добавления контактов необходимо использовать камеру.\n\nРассмотрите возможность предоставления доступа.</string>
<string name="permission_camera_denied_toast">Доступ к камере предоставлен не был</string>
<string name="qr_code">QR-код</string>
<string name="show_qr_code_fullscreen">Показать QR-код во весь экран</string>
<!--App Locking-->

View File

@@ -436,6 +436,7 @@
<string name="permission_camera_title">Leje mbi kamerën</string>
<string name="permission_camera_request_body">Që të skanojë kodin QR, Briar-i lypset të hyjë te kamera.</string>
<string name="permission_camera_denied_body">Keni mohuar hyrjen në kamera, por shtimi i kontakteve lyp përdorimin e kamerës.\n\nJu lutemi, shihni mundësinë e akordimit të hyrjes.</string>
<string name="permission_camera_denied_toast">S\u akorduan leje mbi kamerën</string>
<string name="qr_code">Kod QR</string>
<string name="show_qr_code_fullscreen">Shfaqe kodin QR sa tërë ekrani</string>
<!--App Locking-->

View File

@@ -466,6 +466,7 @@ Slijedeće aplikacije mogu precrtavati preko:
<string name="permission_camera_denied_body">Uskratili ste pristup kameri, ali dodavanje kontakata zahtijeva pristup kameri.
Molim vas da uzmete u obzir odobravanja pristupa.</string>
<string name="permission_camera_denied_toast">Dozvola kameri nije odobrena</string>
<string name="qr_code">QR kod</string>
<string name="show_qr_code_fullscreen">Prikazi QR kod na puni ekran</string>
<!--App Locking-->

View File

@@ -420,6 +420,7 @@
<string name="permission_camera_title">相机权限</string>
<string name="permission_camera_request_body">Briar 需要获得相机权限以扫描二维码。</string>
<string name="permission_camera_denied_body">您已拒绝相机权限,而添加联系人需要使用相机。\n\n请考虑授予相机权限。</string>
<string name="permission_camera_denied_toast">未授予相机权限</string>
<string name="qr_code">二维码</string>
<string name="show_qr_code_fullscreen">全屏显示二维码</string>
<!--App Locking-->

View File

@@ -127,6 +127,9 @@
<string name="date_no_private_messages">No messages.</string>
<string name="no_private_messages">No messages to show</string>
<string name="message_hint">Type message</string>
<string name="set_contact_alias">Set alias name</string>
<string name="set_contact_alias_hint">Contact alias</string>
<string name="set_alias">Set Alias</string>
<string name="delete_contact">Delete contact</string>
<string name="dialog_title_delete_contact">Confirm Contact Deletion</string>
<string name="dialog_message_delete_contact">Are you sure that you want to remove this contact and all messages exchanged with this contact?</string>

View File

@@ -6,6 +6,7 @@ import junit.framework.Assert;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.android.TestBriarApplication;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
@@ -26,7 +27,7 @@ import java.util.Arrays;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.briarproject.bramble.api.identity.Author.Status.UNKNOWN;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
@@ -87,7 +88,8 @@ public class ForumActivityTest {
Author author = getAuthor();
String text = getRandomString(MAX_FORUM_POST_TEXT_LENGTH);
forumItems[i] = new ForumItem(MESSAGE_IDS[i], PARENT_IDS[i],
text, System.currentTimeMillis(), author, UNKNOWN);
text, System.currentTimeMillis(), author,
new AuthorInfo(UNKNOWN));
forumItems[i].setLevel(LEVELS[i]);
}
ThreadItemList<ForumItem> list = new ThreadItemListImpl<>();

View File

@@ -3,6 +3,7 @@ dependencyVerification {
'android.arch.core:common:1.1.1:common-1.1.1.jar:3a616a32f433e9e23f556b38575c31b013613d3ae85206263b7625fe1f4c151a',
'android.arch.core:runtime:1.1.1:runtime-1.1.1.aar:c3215aa5873311b3f88a6f4e4a3c25ad89971bc127de8c3e1291c57f93a05c39',
'android.arch.lifecycle:common:1.1.1:common-1.1.1.jar:8d378e88ebd5189e09eef623414812c868fd90aa519d6160e2311fb8b81cff56',
'android.arch.lifecycle:extensions:1.1.1:extensions-1.1.1.aar:429426b2feec2245ffc5e75b3b5309bedb36159cf06dc71843ae43526ac289b6',
'android.arch.lifecycle:livedata-core:1.1.1:livedata-core-1.1.1.aar:d6fdd8b985d6178d7ea2f16986a24e83f1bee936b74d43167c69e08d3cc12c50',
'android.arch.lifecycle:livedata:1.1.1:livedata-1.1.1.aar:50ab0490c1ff1a7cfb4e554032998b080888946d0dd424f39900efc4a1bcd750',
'android.arch.lifecycle:runtime:1.1.1:runtime-1.1.1.aar:c4e4be66c1b2f0abec593571454e1de14013f7e0f96bf2a9f212931a48cae550',

View File

@@ -1,7 +1,7 @@
package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
@@ -23,10 +23,10 @@ public class BlogCommentHeader extends BlogPostHeader {
public BlogCommentHeader(MessageType type, GroupId groupId,
@Nullable String comment, BlogPostHeader parent, MessageId id,
long timestamp, long timeReceived, Author author,
Status authorStatus, boolean read) {
AuthorInfo authorInfo, boolean read) {
super(type, groupId, id, parent.getId(), timestamp,
timeReceived, author, authorStatus, false, read);
timeReceived, author, authorInfo, false, read);
if (type != COMMENT && type != WRAPPED_COMMENT)
throw new IllegalArgumentException("Incompatible Message Type");

View File

@@ -1,7 +1,7 @@
package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
@@ -21,8 +21,8 @@ public class BlogPostHeader extends PostHeader {
public BlogPostHeader(MessageType type, GroupId groupId, MessageId id,
@Nullable MessageId parentId, long timestamp, long timeReceived,
Author author, Status authorStatus, boolean rssFeed, boolean read) {
super(id, parentId, timestamp, author, authorStatus, read);
Author author, AuthorInfo authorInfo, boolean rssFeed, boolean read) {
super(id, parentId, timestamp, author, authorInfo, read);
this.type = type;
this.groupId = groupId;
this.timeReceived = timeReceived;
@@ -31,9 +31,9 @@ public class BlogPostHeader extends PostHeader {
public BlogPostHeader(MessageType type, GroupId groupId, MessageId id,
long timestamp, long timeReceived, Author author,
Status authorStatus, boolean rssFeed, boolean read) {
AuthorInfo authorInfo, boolean rssFeed, boolean read) {
this(type, groupId, id, null, timestamp, timeReceived, author,
authorStatus, rssFeed, read);
authorInfo, rssFeed, read);
}
public MessageType getType() {

View File

@@ -1,7 +1,8 @@
package org.briarproject.briar.api.client;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.AuthorInfo.Status;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
@@ -17,16 +18,16 @@ public abstract class PostHeader {
private final MessageId parentId;
private final long timestamp;
private final Author author;
private final Status authorStatus;
private final AuthorInfo authorInfo;
private final boolean read;
public PostHeader(MessageId id, @Nullable MessageId parentId,
long timestamp, Author author, Status authorStatus, boolean read) {
long timestamp, Author author, AuthorInfo authorInfo, boolean read) {
this.id = id;
this.parentId = parentId;
this.timestamp = timestamp;
this.author = author;
this.authorStatus = authorStatus;
this.authorInfo = authorInfo;
this.read = read;
}
@@ -39,7 +40,11 @@ public abstract class PostHeader {
}
public Status getAuthorStatus() {
return authorStatus;
return authorInfo.getStatus();
}
public AuthorInfo getAuthorInfo() {
return authorInfo;
}
public long getTimestamp() {

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.api.forum;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.PostHeader;
@@ -13,9 +14,9 @@ import javax.annotation.concurrent.Immutable;
public class ForumPostHeader extends PostHeader {
public ForumPostHeader(MessageId id, @Nullable MessageId parentId,
long timestamp, Author author, Author.Status authorStatus,
long timestamp, Author author, AuthorInfo authorInfo,
boolean read) {
super(id, parentId, timestamp, author, authorStatus, read);
super(id, parentId, timestamp, author, authorInfo, read);
}
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.api.introduction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
@@ -15,19 +16,24 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault
public class IntroductionRequest extends PrivateRequest<Author> {
private final boolean contact;
private final AuthorInfo authorInfo;
public IntroductionRequest(MessageId messageId, GroupId groupId,
long time, boolean local, boolean sent, boolean seen, boolean read,
SessionId sessionId, Author author, @Nullable String text,
boolean answered, boolean contact) {
boolean answered, AuthorInfo authorInfo) {
super(messageId, groupId, time, local, sent, seen, read, sessionId,
author, text, answered);
this.contact = contact;
this.authorInfo = authorInfo;
}
@Nullable
public String getAlias() {
return authorInfo.getAlias();
}
public boolean isContact() {
return contact;
return authorInfo.getStatus().isContact();
}
@Override

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.api.introduction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
@@ -17,14 +18,17 @@ import static org.briarproject.briar.api.introduction.Role.INTRODUCER;
public class IntroductionResponse extends PrivateResponse {
private final Author introducedAuthor;
private final AuthorInfo introducedAuthorInfo;
private final Role ourRole;
public IntroductionResponse(MessageId messageId, GroupId groupId, long time,
boolean local, boolean sent, boolean seen, boolean read,
SessionId sessionId, boolean accepted, Author author, Role role) {
SessionId sessionId, boolean accepted, Author author,
AuthorInfo introducedAuthorInfo, Role role) {
super(messageId, groupId, time, local, sent, seen, read, sessionId,
accepted);
this.introducedAuthor = author;
this.introducedAuthorInfo = introducedAuthorInfo;
this.ourRole = role;
}
@@ -32,6 +36,10 @@ public class IntroductionResponse extends PrivateResponse {
return introducedAuthor;
}
public AuthorInfo getIntroducedAuthorInfo() {
return introducedAuthorInfo;
}
public boolean isIntroducer() {
return ourRole == INTRODUCER;
}

View File

@@ -2,7 +2,7 @@ package org.briarproject.briar.api.privategroup;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
@@ -13,16 +13,16 @@ import javax.annotation.concurrent.Immutable;
public class GroupMember {
private final Author author;
private final Status status;
private final AuthorInfo authorInfo;
private final boolean isCreator;
@Nullable
private final ContactId contactId;
private final Visibility visibility;
public GroupMember(Author author, Status status, boolean isCreator,
public GroupMember(Author author, AuthorInfo status, boolean isCreator,
@Nullable ContactId contactId, Visibility visibility) {
this.author = author;
this.status = status;
this.authorInfo = status;
this.isCreator = isCreator;
this.contactId = contactId;
this.visibility = visibility;
@@ -32,8 +32,8 @@ public class GroupMember {
return author;
}
public Status getStatus() {
return status;
public AuthorInfo getAuthorInfo() {
return authorInfo;
}
public boolean isCreator() {

View File

@@ -1,7 +1,7 @@
package org.briarproject.briar.api.privategroup;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
@@ -18,8 +18,8 @@ public class GroupMessageHeader extends PostHeader {
public GroupMessageHeader(GroupId groupId, MessageId id,
@Nullable MessageId parentId, long timestamp,
Author author, Status authorStatus, boolean read) {
super(id, parentId, timestamp, author, authorStatus, read);
Author author, AuthorInfo authorInfo, boolean read) {
super(id, parentId, timestamp, author, authorInfo, read);
this.groupId = groupId;
}

View File

@@ -14,7 +14,7 @@ public class JoinMessageHeader extends GroupMessageHeader {
public JoinMessageHeader(GroupMessageHeader h, Visibility visibility,
boolean isInitial) {
super(h.getGroupId(), h.getId(), h.getParentId(), h.getTimestamp(),
h.getAuthor(), h.getAuthorStatus(), h.isRead());
h.getAuthor(), h.getAuthorInfo(), h.isRead());
this.visibility = visibility;
this.isInitial = isInitial;
}

View File

@@ -3,6 +3,7 @@ package org.briarproject.briar.blog;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.ContactManager.ContactHook;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
@@ -12,8 +13,8 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -49,6 +50,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.NONE;
import static org.briarproject.briar.api.blog.BlogConstants.KEY_AUTHOR;
import static org.briarproject.briar.api.blog.BlogConstants.KEY_COMMENT;
import static org.briarproject.briar.api.blog.BlogConstants.KEY_ORIGINAL_MSG_ID;
@@ -68,17 +70,19 @@ import static org.briarproject.briar.api.blog.MessageType.WRAPPED_POST;
class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
ContactHook, Client {
private final ContactManager contactManager;
private final IdentityManager identityManager;
private final BlogFactory blogFactory;
private final BlogPostFactory blogPostFactory;
private final List<RemoveBlogHook> removeHooks;
@Inject
BlogManagerImpl(DatabaseComponent db, IdentityManager identityManager,
ClientHelper clientHelper, MetadataParser metadataParser,
BlogFactory blogFactory, BlogPostFactory blogPostFactory) {
BlogManagerImpl(DatabaseComponent db, ContactManager contactManager,
IdentityManager identityManager, ClientHelper clientHelper,
MetadataParser metadataParser, BlogFactory blogFactory,
BlogPostFactory blogPostFactory) {
super(db, clientHelper, metadataParser);
this.contactManager = contactManager;
this.identityManager = identityManager;
this.blogFactory = blogFactory;
this.blogPostFactory = blogPostFactory;
@@ -501,25 +505,24 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
new HashMap<>(metadata1.size() + metadata2.size());
metadata.putAll(metadata1);
metadata.putAll(metadata2);
// get all authors we need to get the status for
// get all authors we need to get the information for
Set<AuthorId> authors = new HashSet<>();
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
BdfList authorList = entry.getValue().getList(KEY_AUTHOR);
Author a = clientHelper.parseAndValidateAuthor(authorList);
authors.add(a.getId());
}
// get statuses for all authors
Map<AuthorId, Status> authorStatuses = new HashMap<>();
// get information for all authors
Map<AuthorId, AuthorInfo> authorInfos = new HashMap<>();
for (AuthorId authorId : authors) {
authorStatuses.put(authorId,
identityManager.getAuthorStatus(txn, authorId));
authorInfos.put(authorId,
contactManager.getAuthorInfo(txn, authorId));
}
// get post headers
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
BdfDictionary meta = entry.getValue();
BlogPostHeader h =
getPostHeaderFromMetadata(txn, g, entry.getKey(), meta,
authorStatuses);
BlogPostHeader h = getPostHeaderFromMetadata(txn, g,
entry.getKey(), meta, authorInfos);
headers.add(h);
}
db.commitTransaction(txn);
@@ -563,7 +566,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
private BlogPostHeader getPostHeaderFromMetadata(Transaction txn,
GroupId groupId, MessageId id, BdfDictionary meta,
Map<AuthorId, Status> authorStatuses)
Map<AuthorId, AuthorInfo> authorInfos)
throws DbException, FormatException {
MessageType type = getMessageType(meta);
@@ -574,13 +577,13 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
BdfList authorList = meta.getList(KEY_AUTHOR);
Author author = clientHelper.parseAndValidateAuthor(authorList);
boolean isFeedPost = meta.getBoolean(KEY_RSS_FEED, false);
Status authorStatus;
AuthorInfo authorInfo;
if (isFeedPost) {
authorStatus = Status.NONE;
} else if (authorStatuses.containsKey(author.getId())) {
authorStatus = authorStatuses.get(author.getId());
authorInfo = new AuthorInfo(NONE);
} else if (authorInfos.containsKey(author.getId())) {
authorInfo = authorInfos.get(author.getId());
} else {
authorStatus = identityManager.getAuthorStatus(txn, author.getId());
authorInfo = contactManager.getAuthorInfo(txn, author.getId());
}
boolean read = meta.getBoolean(KEY_READ, false);
@@ -591,10 +594,10 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
BlogPostHeader parent =
getPostHeaderFromMetadata(txn, groupId, parentId);
return new BlogCommentHeader(type, groupId, comment, parent, id,
timestamp, timeReceived, author, authorStatus, read);
timestamp, timeReceived, author, authorInfo, read);
} else {
return new BlogPostHeader(type, groupId, id, timestamp,
timeReceived, author, authorStatus, isFeedPost, read);
timeReceived, author, authorInfo, isFeedPost, read);
}
}

View File

@@ -2,6 +2,7 @@ package org.briarproject.briar.forum;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataParser;
@@ -9,9 +10,8 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
@@ -45,7 +45,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.briar.api.forum.ForumConstants.KEY_AUTHOR;
import static org.briarproject.briar.api.forum.ForumConstants.KEY_LOCAL;
import static org.briarproject.briar.api.forum.ForumConstants.KEY_PARENT;
@@ -56,19 +56,19 @@ import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ
@NotNullByDefault
class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
private final IdentityManager identityManager;
private final ContactManager contactManager;
private final ForumFactory forumFactory;
private final ForumPostFactory forumPostFactory;
private final MessageTracker messageTracker;
private final List<RemoveForumHook> removeHooks;
@Inject
ForumManagerImpl(DatabaseComponent db, IdentityManager identityManager,
ForumManagerImpl(DatabaseComponent db, ContactManager contactManager,
ClientHelper clientHelper, MetadataParser metadataParser,
ForumFactory forumFactory, ForumPostFactory forumPostFactory,
MessageTracker messageTracker) {
super(db, clientHelper, metadataParser);
this.identityManager = identityManager;
this.contactManager = contactManager;
this.forumFactory = forumFactory;
this.forumPostFactory = forumPostFactory;
this.messageTracker = messageTracker;
@@ -142,8 +142,9 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
throw new AssertionError(e);
}
});
AuthorInfo authorInfo = new AuthorInfo(OURSELVES);
return new ForumPostHeader(p.getMessage().getId(), p.getParent(),
p.getMessage().getTimestamp(), p.getAuthor(), OURSELVES, true);
p.getMessage().getTimestamp(), p.getAuthor(), authorInfo, true);
}
@Override
@@ -196,7 +197,7 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
Collection<ForumPostHeader> headers = new ArrayList<>();
Map<MessageId, BdfDictionary> metadata =
clientHelper.getMessageMetadataAsDictionary(txn, g);
// get all authors we need to get the status for
// get all authors we need to get the info for
Set<AuthorId> authors = new HashSet<>();
for (Entry<MessageId, BdfDictionary> entry :
metadata.entrySet()) {
@@ -204,17 +205,17 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
Author a = clientHelper.parseAndValidateAuthor(authorList);
authors.add(a.getId());
}
// get statuses for all authors
Map<AuthorId, Status> statuses = new HashMap<>();
// get information for all authors
Map<AuthorId, AuthorInfo> authorInfos = new HashMap<>();
for (AuthorId id : authors) {
statuses.put(id, identityManager.getAuthorStatus(txn, id));
authorInfos.put(id, contactManager.getAuthorInfo(txn, id));
}
// Parse the metadata
for (Entry<MessageId, BdfDictionary> entry :
metadata.entrySet()) {
BdfDictionary meta = entry.getValue();
headers.add(getForumPostHeader(txn, entry.getKey(), meta,
statuses));
authorInfos));
}
return headers;
});
@@ -252,7 +253,7 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
}
private ForumPostHeader getForumPostHeader(Transaction txn, MessageId id,
BdfDictionary meta, Map<AuthorId, Status> statuses)
BdfDictionary meta, Map<AuthorId, AuthorInfo> authorInfos)
throws DbException, FormatException {
long timestamp = meta.getLong(KEY_TIMESTAMP);
@@ -261,12 +262,12 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
parentId = new MessageId(meta.getRaw(KEY_PARENT));
BdfList authorList = meta.getList(KEY_AUTHOR);
Author author = clientHelper.parseAndValidateAuthor(authorList);
Status status = statuses.get(author.getId());
if (status == null)
status = identityManager.getAuthorStatus(txn, author.getId());
AuthorInfo authorInfo = authorInfos.get(author.getId());
if (authorInfo == null)
authorInfo = contactManager.getAuthorInfo(txn, author.getId());
boolean read = meta.getBoolean(MSG_KEY_READ);
return new ForumPostHeader(id, parentId, timestamp, author, status,
return new ForumPostHeader(id, parentId, timestamp, author, authorInfo,
read);
}

View File

@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
@@ -149,11 +150,13 @@ abstract class AbstractProtocolEngine<S extends Session>
throws DbException {
AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId();
Contact c = contactManager.getContact(txn, sender, localAuthorId);
AuthorInfo otherAuthorInfo =
contactManager.getAuthorInfo(txn, otherAuthor.getId());
IntroductionResponse response =
new IntroductionResponse(m.getMessageId(), m.getGroupId(),
m.getTimestamp(), false, false, false, false,
s.getSessionId(), m instanceof AcceptMessage,
otherAuthor, s.getRole());
otherAuthor, otherAuthorInfo, s.getRole());
IntroductionResponseReceivedEvent e =
new IntroductionResponseReceivedEvent(response, c.getId());
txn.attach(e);

View File

@@ -12,6 +12,7 @@ import org.briarproject.bramble.api.db.ContactExistsException;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -251,12 +252,12 @@ class IntroduceeProtocolEngine
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
Contact c = contactManager.getContact(txn, s.getIntroducer().getId(),
localAuthor.getId());
boolean contactExists = contactManager
.contactExists(txn, m.getAuthor().getId(), localAuthor.getId());
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, m.getAuthor().getId());
IntroductionRequest request = new IntroductionRequest(m.getMessageId(),
m.getGroupId(), m.getTimestamp(), false, false, false, false,
s.getSessionId(), m.getAuthor(), m.getText(), false,
contactExists);
authorInfo);
IntroductionRequestReceivedEvent e =
new IntroductionRequestReceivedEvent(request, c.getId());
txn.attach(e);

View File

@@ -15,6 +15,8 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -39,6 +41,7 @@ import org.briarproject.briar.introduction.IntroducerSession.Introducee;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -408,6 +411,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
.getMessageMetadataAsDictionary(txn, contactGroupId, query);
List<PrivateMessageHeader> messages =
new ArrayList<>(results.size());
Map<AuthorId, AuthorInfo> authorInfos = new HashMap<>();
for (Entry<MessageId, BdfDictionary> e : results.entrySet()) {
MessageId m = e.getKey();
MessageMetadata meta =
@@ -419,15 +423,17 @@ class IntroductionManagerImpl extends ConversationClientImpl
if (type == REQUEST) {
messages.add(
parseInvitationRequest(txn, contactGroupId, m,
meta, status, ss.bdfSession));
meta, status, ss.bdfSession, authorInfos));
} else if (type == ACCEPT) {
messages.add(
parseInvitationResponse(contactGroupId, m, meta,
status, ss.bdfSession, true));
parseInvitationResponse(txn, contactGroupId, m,
meta, status, ss.bdfSession, authorInfos,
true));
} else if (type == DECLINE) {
messages.add(
parseInvitationResponse(contactGroupId, m, meta,
status, ss.bdfSession, false));
parseInvitationResponse(txn, contactGroupId, m,
meta, status, ss.bdfSession, authorInfos,
false));
}
}
return messages;
@@ -438,7 +444,8 @@ class IntroductionManagerImpl extends ConversationClientImpl
private IntroductionRequest parseInvitationRequest(Transaction txn,
GroupId contactGroupId, MessageId m, MessageMetadata meta,
MessageStatus status, BdfDictionary bdfSession)
MessageStatus status, BdfDictionary bdfSession,
Map<AuthorId, AuthorInfo> authorInfos)
throws DbException, FormatException {
Role role = sessionParser.getRole(bdfSession);
SessionId sessionId;
@@ -462,19 +469,22 @@ class IntroductionManagerImpl extends ConversationClientImpl
BdfList body = clientHelper.toList(msg);
RequestMessage rm = messageParser.parseRequestMessage(msg, body);
String text = rm.getText();
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
boolean contactExists = contactManager
.contactExists(txn, rm.getAuthor().getId(),
localAuthor.getId());
AuthorInfo authorInfo = authorInfos.get(author.getId());
if (authorInfo == null) {
authorInfo = contactManager.getAuthorInfo(txn, author.getId());
authorInfos.put(author.getId(), authorInfo);
}
return new IntroductionRequest(m, contactGroupId, meta.getTimestamp(),
meta.isLocal(), status.isSent(), status.isSeen(), meta.isRead(),
sessionId, author, text, !meta.isAvailableToAnswer(),
contactExists);
authorInfo);
}
private IntroductionResponse parseInvitationResponse(GroupId contactGroupId,
MessageId m, MessageMetadata meta, MessageStatus status,
BdfDictionary bdfSession, boolean accept) throws FormatException {
private IntroductionResponse parseInvitationResponse(Transaction txn,
GroupId contactGroupId, MessageId m, MessageMetadata meta,
MessageStatus status, BdfDictionary bdfSession,
Map<AuthorId, AuthorInfo> authorInfos, boolean accept)
throws FormatException, DbException {
Role role = sessionParser.getRole(bdfSession);
SessionId sessionId;
Author author;
@@ -493,9 +503,14 @@ class IntroductionManagerImpl extends ConversationClientImpl
sessionId = session.getSessionId();
author = session.getRemote().author;
} else throw new AssertionError();
AuthorInfo authorInfo = authorInfos.get(author.getId());
if (authorInfo == null) {
authorInfo = contactManager.getAuthorInfo(txn, author.getId());
authorInfos.put(author.getId(), authorInfo);
}
return new IntroductionResponse(m, contactGroupId, meta.getTimestamp(),
meta.isLocal(), status.isSent(), status.isSeen(), meta.isRead(),
sessionId, accept, author, role);
sessionId, accept, author, authorInfo, role);
}
private void removeSessionWithIntroducer(Transaction txn,

View File

@@ -13,8 +13,9 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.AuthorInfo.Status;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -53,9 +54,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.Author.Status.UNVERIFIED;
import static org.briarproject.bramble.api.identity.Author.Status.VERIFIED;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
import static org.briarproject.briar.api.privategroup.MessageType.JOIN;
import static org.briarproject.briar.api.privategroup.MessageType.POST;
import static org.briarproject.briar.api.privategroup.Visibility.INVISIBLE;
@@ -231,9 +232,10 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
} finally {
db.endTransaction(txn);
}
AuthorInfo authorInfo = new AuthorInfo(OURSELVES);
return new GroupMessageHeader(m.getMessage().getGroupId(),
m.getMessage().getId(), m.getParent(),
m.getMessage().getTimestamp(), m.getMember(), OURSELVES, true);
m.getMessage().getTimestamp(), m.getMember(), authorInfo, true);
}
private void addMessageMetadata(BdfDictionary meta, GroupMessage m) {
@@ -321,15 +323,15 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
try {
Map<MessageId, BdfDictionary> metadata =
clientHelper.getMessageMetadataAsDictionary(txn, g);
// get all authors we need to get the status for
// get all authors we need to get the information for
Set<AuthorId> authors = new HashSet<>();
for (BdfDictionary meta : metadata.values()) {
authors.add(getAuthor(meta).getId());
}
// get statuses for all authors
Map<AuthorId, Status> statuses = new HashMap<>();
// get information for all authors
Map<AuthorId, AuthorInfo> authorInfos = new HashMap<>();
for (AuthorId id : authors) {
statuses.put(id, identityManager.getAuthorStatus(txn, id));
authorInfos.put(id, contactManager.getAuthorInfo(txn, id));
}
// get current visibilities for join messages
Map<Author, Visibility> visibilities = getMembers(txn, g);
@@ -340,10 +342,10 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
Author member = getAuthor(meta);
Visibility v = visibilities.get(member);
headers.add(getJoinMessageHeader(txn, g, entry.getKey(),
meta, statuses, v));
meta, authorInfos, v));
} else {
headers.add(getGroupMessageHeader(txn, g, entry.getKey(),
meta, statuses));
meta, authorInfos));
}
}
db.commitTransaction(txn);
@@ -356,7 +358,8 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
}
private GroupMessageHeader getGroupMessageHeader(Transaction txn, GroupId g,
MessageId id, BdfDictionary meta, Map<AuthorId, Status> statuses)
MessageId id, BdfDictionary meta,
Map<AuthorId, AuthorInfo> authorInfos)
throws DbException, FormatException {
MessageId parentId = null;
@@ -366,24 +369,25 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
long timestamp = meta.getLong(KEY_TIMESTAMP);
Author member = getAuthor(meta);
Status status;
if (statuses.containsKey(member.getId())) {
status = statuses.get(member.getId());
AuthorInfo authorInfo;
if (authorInfos.containsKey(member.getId())) {
authorInfo = authorInfos.get(member.getId());
} else {
status = identityManager.getAuthorStatus(txn, member.getId());
authorInfo = contactManager.getAuthorInfo(txn, member.getId());
}
boolean read = meta.getBoolean(KEY_READ);
return new GroupMessageHeader(g, id, parentId, timestamp, member,
status, read);
authorInfo, read);
}
private JoinMessageHeader getJoinMessageHeader(Transaction txn, GroupId g,
MessageId id, BdfDictionary meta, Map<AuthorId, Status> statuses,
Visibility v) throws DbException, FormatException {
MessageId id, BdfDictionary meta,
Map<AuthorId, AuthorInfo> authorInfos, Visibility v)
throws DbException, FormatException {
GroupMessageHeader header =
getGroupMessageHeader(txn, g, id, meta, statuses);
getGroupMessageHeader(txn, g, id, meta, authorInfos);
boolean creator = meta.getBoolean(KEY_INITIAL_JOIN_MSG);
return new JoinMessageHeader(header, v, creator);
}
@@ -398,7 +402,8 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
PrivateGroup privateGroup = getPrivateGroup(txn, g);
for (Entry<Author, Visibility> m : authors.entrySet()) {
Author a = m.getKey();
Status status = identityManager.getAuthorStatus(txn, a.getId());
AuthorInfo authorInfo = contactManager.getAuthorInfo(txn, a.getId());
Status status = authorInfo.getStatus();
Visibility v = m.getValue();
ContactId c = null;
if (v != INVISIBLE &&
@@ -407,7 +412,7 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
.getId();
}
boolean isCreator = privateGroup.getCreator().equals(a);
members.add(new GroupMember(a, status, isCreator, c, v));
members.add(new GroupMember(a, authorInfo, isCreator, c, v));
}
db.commitTransaction(txn);
return members;

View File

@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
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.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
@@ -12,6 +13,7 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.sync.Group;
@@ -29,9 +31,9 @@ import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import static org.briarproject.bramble.api.identity.Author.Status.NONE;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.Author.Status.VERIFIED;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.NONE;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage;
@@ -64,6 +66,8 @@ public class BlogManagerImplTest extends BriarTestCase {
private final Mockery context = new Mockery();
private final BlogManagerImpl blogManager;
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final ContactManager contactManager =
context.mock(ContactManager.class);
private final IdentityManager identityManager =
context.mock(IdentityManager.class);
private final ClientHelper clientHelper = context.mock(ClientHelper.class);
@@ -72,6 +76,8 @@ public class BlogManagerImplTest extends BriarTestCase {
context.mock(BlogPostFactory.class);
private final LocalAuthor localAuthor1, localAuthor2, rssLocalAuthor;
private final AuthorInfo ourselvesInfo = new AuthorInfo(OURSELVES);
private final AuthorInfo verifiedInfo = new AuthorInfo(VERIFIED);
private final BdfList authorList1, authorList2, rssAuthorList;
private final Blog blog1, blog2, rssBlog;
private final Message message, rssMessage;
@@ -81,7 +87,7 @@ public class BlogManagerImplTest extends BriarTestCase {
public BlogManagerImplTest() {
MetadataParser metadataParser = context.mock(MetadataParser.class);
blogManager = new BlogManagerImpl(db, identityManager, clientHelper,
blogManager = new BlogManagerImpl(db, contactManager, identityManager, clientHelper,
metadataParser, blogFactory, blogPostFactory);
localAuthor1 = getLocalAuthor();
@@ -124,7 +130,7 @@ public class BlogManagerImplTest extends BriarTestCase {
ContactId contactId = new ContactId(0);
Contact contact = new Contact(contactId, blog2.getAuthor(),
blog1.getAuthor().getId(), true, true);
blog1.getAuthor().getId(), getRandomString(5), true, true);
context.checking(new Expectations() {{
oneOf(blogFactory).createBlog(blog2.getAuthor());
@@ -146,7 +152,7 @@ public class BlogManagerImplTest extends BriarTestCase {
ContactId contactId = new ContactId(0);
Contact contact = new Contact(contactId, blog2.getAuthor(),
blog1.getAuthor().getId(), true, true);
blog1.getAuthor().getId(), getRandomString(5), true, true);
context.checking(new Expectations() {{
oneOf(blogFactory).createBlog(blog2.getAuthor());
@@ -175,8 +181,8 @@ public class BlogManagerImplTest extends BriarTestCase {
context.checking(new Expectations() {{
oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1));
oneOf(identityManager).getAuthorStatus(txn, localAuthor1.getId());
will(returnValue(VERIFIED));
oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(verifiedInfo));
}});
blogManager.incomingMessage(txn, message, body, meta);
@@ -281,8 +287,8 @@ public class BlogManagerImplTest extends BriarTestCase {
oneOf(clientHelper).addLocalMessage(txn, message, meta, true);
oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1));
oneOf(identityManager).getAuthorStatus(txn, localAuthor1.getId());
will(returnValue(OURSELVES));
oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(ourselvesInfo));
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
}});
@@ -396,21 +402,21 @@ public class BlogManagerImplTest extends BriarTestCase {
// Create the headers for the comment and its parent
oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1));
oneOf(identityManager).getAuthorStatus(txn, localAuthor1.getId());
will(returnValue(OURSELVES));
oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(ourselvesInfo));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, messageId);
will(returnValue(postMeta));
oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1));
oneOf(identityManager).getAuthorStatus(txn, localAuthor1.getId());
will(returnValue(OURSELVES));
oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(ourselvesInfo));
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
}});
BlogPostHeader postHeader = new BlogPostHeader(POST, blog1.getId(),
messageId, null, timestamp, timeReceived, localAuthor1,
OURSELVES, false, true);
ourselvesInfo, false, true);
blogManager.addLocalComment(localAuthor1, blog1.getId(), comment,
postHeader);
context.assertIsSatisfied();
@@ -504,22 +510,22 @@ public class BlogManagerImplTest extends BriarTestCase {
// Create the headers for the comment and the wrapped post
oneOf(clientHelper).parseAndValidateAuthor(authorList2);
will(returnValue(localAuthor2));
oneOf(identityManager).getAuthorStatus(txn, localAuthor2.getId());
will(returnValue(OURSELVES));
oneOf(contactManager).getAuthorInfo(txn, localAuthor2.getId());
will(returnValue(ourselvesInfo));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
wrappedPostId);
will(returnValue(wrappedPostMeta));
oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1));
oneOf(identityManager).getAuthorStatus(txn, localAuthor1.getId());
will(returnValue(VERIFIED));
oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(verifiedInfo));
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
}});
BlogPostHeader originalPostHeader = new BlogPostHeader(POST,
blog1.getId(), messageId, null, timestamp, timeReceived,
localAuthor1, VERIFIED, false, true);
localAuthor1, verifiedInfo, false, true);
blogManager.addLocalComment(localAuthor2, blog2.getId(), comment,
originalPostHeader);
context.assertIsSatisfied();
@@ -613,8 +619,8 @@ public class BlogManagerImplTest extends BriarTestCase {
// Create the headers for the comment and the wrapped post
oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1));
oneOf(identityManager).getAuthorStatus(txn, localAuthor1.getId());
will(returnValue(OURSELVES));
oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(ourselvesInfo));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
wrappedPostId);
will(returnValue(wrappedPostMeta));
@@ -626,7 +632,7 @@ public class BlogManagerImplTest extends BriarTestCase {
BlogPostHeader originalPostHeader = new BlogPostHeader(POST,
rssBlog.getId(), rssMessageId, null, timestamp, timeReceived,
rssLocalAuthor, NONE, true, true);
rssLocalAuthor, new AuthorInfo(NONE), true, true);
blogManager.addLocalComment(localAuthor1, blog1.getId(), comment,
originalPostHeader);
context.assertIsSatisfied();
@@ -752,15 +758,15 @@ public class BlogManagerImplTest extends BriarTestCase {
// the rewrapped post
oneOf(clientHelper).parseAndValidateAuthor(authorList2);
will(returnValue(localAuthor2));
oneOf(identityManager).getAuthorStatus(txn, localAuthor2.getId());
will(returnValue(OURSELVES));
oneOf(contactManager).getAuthorInfo(txn, localAuthor2.getId());
will(returnValue(ourselvesInfo));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
wrappedCommentId);
will(returnValue(wrappedCommentMeta));
oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1));
oneOf(identityManager).getAuthorStatus(txn, localAuthor1.getId());
will(returnValue(VERIFIED));
oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(verifiedInfo));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
rewrappedPostId);
will(returnValue(rewrappedPostMeta));
@@ -772,10 +778,10 @@ public class BlogManagerImplTest extends BriarTestCase {
BlogPostHeader wrappedPostHeader = new BlogPostHeader(WRAPPED_POST,
blog1.getId(), wrappedPostId, null, timestamp, timeReceived,
rssLocalAuthor, NONE, true, true);
rssLocalAuthor, new AuthorInfo(NONE), true, true);
BlogCommentHeader originalCommentHeader = new BlogCommentHeader(COMMENT,
blog1.getId(), comment, wrappedPostHeader, originalCommentId,
timestamp, timeReceived, localAuthor1, VERIFIED, true);
timestamp, timeReceived, localAuthor1, verifiedInfo, true);
blogManager.addLocalComment(localAuthor2, blog2.getId(), localComment,
originalCommentHeader);

View File

@@ -1,6 +1,5 @@
package org.briarproject.briar.blog;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.test.TestDatabaseModule;
@@ -23,6 +22,7 @@ import java.util.Iterator;
import static junit.framework.Assert.assertNotNull;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.NONE;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.api.blog.MessageType.COMMENT;
@@ -424,7 +424,7 @@ public class BlogManagerIntegrationTest
assertEquals(1, headers.size());
BlogPostHeader header = headers.iterator().next();
assertEquals(POST, header.getType());
assertEquals(Author.Status.NONE, header.getAuthorStatus());
assertEquals(NONE, header.getAuthorStatus());
assertTrue(header.isRssFeed());
}

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