Compare commits

...

32 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
akwizgran
22eed91019 Merge branch 'javalin-access-manager' into 'master'
[headless] Set up access manager before starting server

See merge request briar/briar!966
2018-10-29 15:35:48 +00:00
akwizgran
fcb88ed58c Merge branch '1147-bluetooth-discovery' into 'master'
Support Bluetooth discovery for adding contacts

See merge request briar/briar!954
2018-10-29 14:35:17 +00:00
Torsten Grote
0d940fc7d7 [headless] Set up access manager before starting server
This became necesary to due an upstream change we missed when bumping
the dependency:

ab19ff91b7
2018-10-29 11:20:48 -03:00
akwizgran
53da13794f Merge branch '1422-activity-log' into 'master'
Log when activities start and stop

See merge request briar/briar!959
2018-10-24 16:00:52 +00:00
akwizgran
2ab03f48cc Merge branch '1256-remove-contact' into 'master'
briar-headless: Add endpoint for removing a contact

See merge request briar/briar!962
2018-10-24 15:59:39 +00:00
Torsten Grote
436f45554d [briar-headless] update dependencies 2018-10-24 12:41:29 -03:00
Torsten Grote
51209b5eec briar-headless: Add endpoint for removing a contact 2018-10-24 12:12:33 -03:00
akwizgran
822597b4c6 Merge branch '1373-mirror-icons' into 'master'
Fix RTL icon mirroring in DevReportActivity

Closes #1373

See merge request briar/briar!960
2018-10-24 14:05:06 +00:00
akwizgran
7c01bc59c0 Merge branch '1252-dark-theme-system-default' into 'master'
Remove system default theme option on API < 28

Closes #1252

See merge request briar/briar!961
2018-10-24 13:52:20 +00:00
Torsten Grote
825d342f9b Remove system default theme option on API < 27
Closes #1252
2018-10-24 10:40:43 -03:00
Torsten Grote
34955fecbb Fix RTL icon mirroring in DevReportActivity
For some reason, the toolbar icon has a wrong layout direction,
so the autoMirrored attribute doesn't take any effect.
2018-10-22 16:53:17 -03:00
Torsten Grote
5c28b60a6b Log when activities start and stop
Remove BriarRecyclerView log messages
2018-10-22 14:11:36 -03:00
akwizgran
9c4fb4fd34 Remove unused string. 2018-10-18 17:22:54 +01:00
akwizgran
3d6a336f6d Refactor permissions code, add comments, fix corner cases. 2018-10-18 17:16:49 +01:00
akwizgran
4b7a81177c Static imports. 2018-10-15 14:46:40 +01:00
akwizgran
9515e93857 Cancel discovery after 10 seconds and try to connect. 2018-10-15 11:04:46 +01:00
akwizgran
efe15df940 Remove static import of R's fields. 2018-10-15 11:04:46 +01:00
akwizgran
de611857cf Discover BT devices if no address is provided. 2018-10-15 11:04:46 +01:00
akwizgran
8935ec2c2e Don't wait for state change if BT is already discoverable. 2018-10-15 11:04:45 +01:00
akwizgran
bd00fb1c04 Ask for coarse location permission before adding a contact. 2018-10-15 11:04:45 +01:00
akwizgran
3192015cfd Ask for Bluetooth discoverability before adding a contact. 2018-10-15 11:04:45 +01:00
110 changed files with 1787 additions and 726 deletions

View File

@@ -16,18 +16,27 @@ import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.AndroidUtils; import org.briarproject.bramble.util.AndroidUtils;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_FINISHED;
import static android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_STARTED;
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED; import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED; import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE; import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
@@ -37,8 +46,13 @@ import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERA
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE; import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
import static android.bluetooth.BluetoothAdapter.STATE_OFF; import static android.bluetooth.BluetoothAdapter.STATE_OFF;
import static android.bluetooth.BluetoothAdapter.STATE_ON; import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.bluetooth.BluetoothDevice.ACTION_FOUND;
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -47,8 +61,11 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(AndroidBluetoothPlugin.class.getName()); Logger.getLogger(AndroidBluetoothPlugin.class.getName());
private static final int MAX_DISCOVERY_MS = 10_000;
private final AndroidExecutor androidExecutor; private final AndroidExecutor androidExecutor;
private final Context appContext; private final Context appContext;
private final Clock clock;
private volatile boolean wasEnabledByUs = false; private volatile boolean wasEnabledByUs = false;
private volatile BluetoothStateReceiver receiver = null; private volatile BluetoothStateReceiver receiver = null;
@@ -58,12 +75,13 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter, AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
Executor ioExecutor, AndroidExecutor androidExecutor, Executor ioExecutor, AndroidExecutor androidExecutor,
Context appContext, SecureRandom secureRandom, Backoff backoff, Context appContext, SecureRandom secureRandom, Clock clock,
DuplexPluginCallback callback, int maxLatency) { Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
super(connectionLimiter, ioExecutor, secureRandom, backoff, callback, super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
maxLatency); maxLatency);
this.androidExecutor = androidExecutor; this.androidExecutor = androidExecutor;
this.appContext = appContext; this.appContext = appContext;
this.clock = clock;
} }
@Override @Override
@@ -182,6 +200,74 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
} }
} }
@Override
@Nullable
DuplexTransportConnection discoverAndConnect(String uuid) {
if (adapter == null) return null;
for (String address : discoverDevices()) {
try {
if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + scrubMacAddress(address));
return connectTo(address, uuid);
} catch (IOException e) {
if (LOG.isLoggable(INFO)) {
LOG.info("Could not connect to "
+ scrubMacAddress(address));
}
}
}
LOG.info("Could not connect to any devices");
return null;
}
private Collection<String> discoverDevices() {
List<String> addresses = new ArrayList<>();
BlockingQueue<Intent> intents = new LinkedBlockingQueue<>();
DiscoveryReceiver receiver = new DiscoveryReceiver(intents);
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_DISCOVERY_STARTED);
filter.addAction(ACTION_DISCOVERY_FINISHED);
filter.addAction(ACTION_FOUND);
appContext.registerReceiver(receiver, filter);
try {
if (adapter.startDiscovery()) {
long now = clock.currentTimeMillis();
long end = now + MAX_DISCOVERY_MS;
while (now < end) {
Intent i = intents.poll(end - now, MILLISECONDS);
if (i == null) break;
String action = i.getAction();
if (ACTION_DISCOVERY_STARTED.equals(action)) {
LOG.info("Discovery started");
} else if (ACTION_DISCOVERY_FINISHED.equals(action)) {
LOG.info("Discovery finished");
break;
} else if (ACTION_FOUND.equals(action)) {
BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE);
String address = d.getAddress();
if (LOG.isLoggable(INFO))
LOG.info("Discovered " + scrubMacAddress(address));
if (!addresses.contains(address))
addresses.add(address);
}
now = clock.currentTimeMillis();
}
} else {
LOG.info("Could not start discovery");
}
} catch (InterruptedException e) {
LOG.info("Interrupted while discovering devices");
Thread.currentThread().interrupt();
} finally {
LOG.info("Cancelling discovery");
adapter.cancelDiscovery();
appContext.unregisterReceiver(receiver);
}
// Shuffle the addresses so we don't always try the same one first
Collections.shuffle(addresses);
return addresses;
}
private void tryToClose(@Nullable Closeable c) { private void tryToClose(@Nullable Closeable c) {
try { try {
if (c != null) c.close(); if (c != null) c.close();
@@ -207,4 +293,18 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
} }
} }
} }
private static class DiscoveryReceiver extends BroadcastReceiver {
private final BlockingQueue<Intent> intents;
private DiscoveryReceiver(BlockingQueue<Intent> intents) {
this.intents = intents;
}
@Override
public void onReceive(Context ctx, Intent intent) {
intents.add(intent);
}
}
} }

View File

@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -33,17 +34,19 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
private final Context appContext; private final Context appContext;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final EventBus eventBus; private final EventBus eventBus;
private final Clock clock;
private final BackoffFactory backoffFactory; private final BackoffFactory backoffFactory;
public AndroidBluetoothPluginFactory(Executor ioExecutor, public AndroidBluetoothPluginFactory(Executor ioExecutor,
AndroidExecutor androidExecutor, Context appContext, AndroidExecutor androidExecutor, Context appContext,
SecureRandom secureRandom, EventBus eventBus, SecureRandom secureRandom, EventBus eventBus, Clock clock,
BackoffFactory backoffFactory) { BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.androidExecutor = androidExecutor; this.androidExecutor = androidExecutor;
this.appContext = appContext; this.appContext = appContext;
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.eventBus = eventBus; this.eventBus = eventBus;
this.clock = clock;
this.backoffFactory = backoffFactory; this.backoffFactory = backoffFactory;
} }
@@ -65,7 +68,7 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin( AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
connectionLimiter, ioExecutor, androidExecutor, appContext, connectionLimiter, ioExecutor, androidExecutor, appContext,
secureRandom, backoff, callback, MAX_LATENCY); secureRandom, clock, backoff, callback, MAX_LATENCY);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }

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.identity.AuthorId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; 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 @Immutable
@NotNullByDefault @NotNullByDefault
public class Contact { public class Contact {
@@ -13,13 +17,21 @@ public class Contact {
private final ContactId id; private final ContactId id;
private final Author author; private final Author author;
private final AuthorId localAuthorId; private final AuthorId localAuthorId;
@Nullable
private final String alias;
private final boolean verified, active; private final boolean verified, active;
public Contact(ContactId id, Author author, AuthorId localAuthorId, 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.id = id;
this.author = author; this.author = author;
this.localAuthorId = localAuthorId; this.localAuthorId = localAuthorId;
this.alias = alias;
this.verified = verified; this.verified = verified;
this.active = active; this.active = active;
} }
@@ -36,6 +48,11 @@ public class Contact {
return localAuthorId; return localAuthorId;
} }
@Nullable
public String getAlias() {
return alias;
}
public boolean isVerified() { public boolean isVerified() {
return verified; 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.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; 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.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collection; import java.util.Collection;
import javax.annotation.Nullable;
@NotNullByDefault @NotNullByDefault
public interface ContactManager { public interface ContactManager {
@@ -93,6 +96,12 @@ public interface ContactManager {
void setContactActive(Transaction txn, ContactId c, boolean active) void setContactActive(Transaction txn, ContactId c, boolean active)
throws DbException; 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 * 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) boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
throws DbException; 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 { interface ContactHook {
void addingContact(Transaction txn, Contact c) throws DbException; 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) void setContactActive(Transaction txn, ContactId c, boolean active)
throws DbException; 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. * 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 @NotNullByDefault
public class Author implements Nameable { public class Author implements Nameable {
public enum Status {
NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES
}
/** /**
* The current version of the author structure. * 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.crypto.CryptoExecutor;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
@@ -37,14 +36,4 @@ public interface IdentityManager {
*/ */
LocalAuthor getLocalAuthor(Transaction txn) throws DbException; 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

@@ -21,7 +21,7 @@ public interface KeyAgreementConstants {
/** /**
* The connection timeout in milliseconds. * The connection timeout in milliseconds.
*/ */
long CONNECTION_TIMEOUT = 20 * 1000; long CONNECTION_TIMEOUT = 60_000;
/** /**
* The transport identifier for Bluetooth. * The transport identifier for Bluetooth.

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.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; 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.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
@@ -18,21 +21,32 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; 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 @ThreadSafe
@NotNullByDefault @NotNullByDefault
class ContactManagerImpl implements ContactManager { class ContactManagerImpl implements ContactManager {
private final DatabaseComponent db; private final DatabaseComponent db;
private final KeyManager keyManager; private final KeyManager keyManager;
private final IdentityManager identityManager;
private final List<ContactHook> hooks; private final List<ContactHook> hooks;
@Inject @Inject
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager) { ContactManagerImpl(DatabaseComponent db, KeyManager keyManager,
IdentityManager identityManager) {
this.db = db; this.db = db;
this.keyManager = keyManager; this.keyManager = keyManager;
this.identityManager = identityManager;
hooks = new CopyOnWriteArrayList<>(); hooks = new CopyOnWriteArrayList<>();
} }
@@ -148,6 +162,17 @@ class ContactManagerImpl implements ContactManager {
db.setContactActive(txn, c, active); 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 @Override
public boolean contactExists(Transaction txn, AuthorId remoteAuthorId, public boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException { AuthorId localAuthorId) throws DbException {
@@ -176,4 +201,23 @@ class ContactManagerImpl implements ContactManager {
db.removeContact(txn, c); 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) void setContactActive(T txn, ContactId c, boolean active)
throws DbException; 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 * Sets the given group's visibility to the given contact to either
* {@link Visibility VISIBLE} or {@link Visibility SHARED}. * {@link Visibility VISIBLE} or {@link Visibility SHARED}.

View File

@@ -859,6 +859,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
transaction.attach(new ContactStatusChangedEvent(c, active)); 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 @Override
public void setGroupVisibility(Transaction transaction, ContactId c, public void setGroupVisibility(Transaction transaction, ContactId c,
GroupId g, Visibility v) throws DbException { 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 BINARY_TYPE = "BINARY";
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT"; private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
private static final String STRING_TYPE = "VARCHAR"; 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 DatabaseConfig config;
private final String url; private final String url;
@@ -40,8 +42,7 @@ class H2Database extends JdbcDatabase {
@Inject @Inject
H2Database(DatabaseConfig config, MessageFactory messageFactory, H2Database(DatabaseConfig config, MessageFactory messageFactory,
Clock clock) { Clock clock) {
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE, super(dbTypes, messageFactory, clock);
messageFactory, clock);
this.config = config; this.config = config;
File dir = config.getDatabaseDirectory(); File dir = config.getDatabaseDirectory();
String path = new File(dir, "db").getAbsolutePath(); String path = new File(dir, "db").getAbsolutePath();

View File

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

View File

@@ -56,6 +56,7 @@ import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static java.sql.Types.INTEGER; 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.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.db.Metadata.REMOVE; 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> { abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing // 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 // Rotation period offsets for incoming transport keys
private static final int OFFSET_PREV = -1; private static final int OFFSET_PREV = -1;
@@ -113,6 +114,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " authorId _HASH NOT NULL," + " authorId _HASH NOT NULL,"
+ " formatVersion INT NOT NULL," + " formatVersion INT NOT NULL,"
+ " name _STRING NOT NULL," + " name _STRING NOT NULL,"
+ " alias _STRING," // Null if no alias exists
+ " publicKey _BINARY NOT NULL," + " publicKey _BINARY NOT NULL,"
+ " localAuthorId _HASH NOT NULL," + " localAuthorId _HASH NOT NULL,"
+ " verified BOOLEAN NOT NULL," + " verified BOOLEAN NOT NULL,"
@@ -310,10 +312,9 @@ abstract class JdbcDatabase implements Database<Connection> {
Logger.getLogger(JdbcDatabase.class.getName()); Logger.getLogger(JdbcDatabase.class.getName());
// Different database libraries use different names for certain types // 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 MessageFactory messageFactory;
private final Clock clock; private final Clock clock;
private final DatabaseTypes dbTypes;
// Locking: connectionsLock // Locking: connectionsLock
private final LinkedList<Connection> connections = new LinkedList<>(); 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 Lock connectionsLock = new ReentrantLock();
private final Condition connectionsChanged = connectionsLock.newCondition(); private final Condition connectionsChanged = connectionsLock.newCondition();
JdbcDatabase(String hashType, String secretType, String binaryType, JdbcDatabase(DatabaseTypes databaseTypes, MessageFactory messageFactory,
String counterType, String stringType, Clock clock) {
MessageFactory messageFactory, Clock clock) { this.dbTypes = databaseTypes;
this.hashType = hashType;
this.secretType = secretType;
this.binaryType = binaryType;
this.counterType = counterType;
this.stringType = stringType;
this.messageFactory = messageFactory; this.messageFactory = messageFactory;
this.clock = clock; this.clock = clock;
} }
@@ -427,7 +423,11 @@ abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing // Package access for testing
List<Migration<Connection>> getMigrations() { 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) { private boolean isCompactionDue(Settings s) {
@@ -486,20 +486,20 @@ abstract class JdbcDatabase implements Database<Connection> {
Statement s = null; Statement s = null;
try { try {
s = txn.createStatement(); s = txn.createStatement();
s.executeUpdate(insertTypeNames(CREATE_SETTINGS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_SETTINGS));
s.executeUpdate(insertTypeNames(CREATE_LOCAL_AUTHORS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_LOCAL_AUTHORS));
s.executeUpdate(insertTypeNames(CREATE_CONTACTS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_CONTACTS));
s.executeUpdate(insertTypeNames(CREATE_GROUPS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_GROUPS));
s.executeUpdate(insertTypeNames(CREATE_GROUP_METADATA)); s.executeUpdate(dbTypes.replaceTypes(CREATE_GROUP_METADATA));
s.executeUpdate(insertTypeNames(CREATE_GROUP_VISIBILITIES)); s.executeUpdate(dbTypes.replaceTypes(CREATE_GROUP_VISIBILITIES));
s.executeUpdate(insertTypeNames(CREATE_MESSAGES)); s.executeUpdate(dbTypes.replaceTypes(CREATE_MESSAGES));
s.executeUpdate(insertTypeNames(CREATE_MESSAGE_METADATA)); s.executeUpdate(dbTypes.replaceTypes(CREATE_MESSAGE_METADATA));
s.executeUpdate(insertTypeNames(CREATE_MESSAGE_DEPENDENCIES)); s.executeUpdate(dbTypes.replaceTypes(CREATE_MESSAGE_DEPENDENCIES));
s.executeUpdate(insertTypeNames(CREATE_OFFERS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_OFFERS));
s.executeUpdate(insertTypeNames(CREATE_STATUSES)); s.executeUpdate(dbTypes.replaceTypes(CREATE_STATUSES));
s.executeUpdate(insertTypeNames(CREATE_TRANSPORTS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_TRANSPORTS));
s.executeUpdate(insertTypeNames(CREATE_OUTGOING_KEYS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_OUTGOING_KEYS));
s.executeUpdate(insertTypeNames(CREATE_INCOMING_KEYS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_INCOMING_KEYS));
s.close(); s.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(s); 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 @Override
public Connection startTransaction() throws DbException { public Connection startTransaction() throws DbException {
Connection txn; Connection txn;
@@ -1258,8 +1249,8 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT authorId, formatVersion, name, publicKey," String sql = "SELECT authorId, formatVersion, name, alias,"
+ " localAuthorId, verified, active" + " publicKey, localAuthorId, verified, active"
+ " FROM contacts" + " FROM contacts"
+ " WHERE contactId = ?"; + " WHERE contactId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
@@ -1269,15 +1260,17 @@ abstract class JdbcDatabase implements Database<Connection> {
AuthorId authorId = new AuthorId(rs.getBytes(1)); AuthorId authorId = new AuthorId(rs.getBytes(1));
int formatVersion = rs.getInt(2); int formatVersion = rs.getInt(2);
String name = rs.getString(3); String name = rs.getString(3);
byte[] publicKey = rs.getBytes(4); String alias = rs.getString(4);
AuthorId localAuthorId = new AuthorId(rs.getBytes(5)); byte[] publicKey = rs.getBytes(5);
boolean verified = rs.getBoolean(6); AuthorId localAuthorId = new AuthorId(rs.getBytes(6));
boolean active = rs.getBoolean(7); boolean verified = rs.getBoolean(7);
boolean active = rs.getBoolean(8);
rs.close(); rs.close();
ps.close(); ps.close();
Author author = Author author =
new Author(authorId, formatVersion, name, publicKey); 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) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs);
tryToClose(ps); tryToClose(ps);
@@ -1292,7 +1285,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT contactId, authorId, formatVersion, name," String sql = "SELECT contactId, authorId, formatVersion, name,"
+ " publicKey, localAuthorId, verified, active" + " alias, publicKey, localAuthorId, verified, active"
+ " FROM contacts"; + " FROM contacts";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
rs = ps.executeQuery(); rs = ps.executeQuery();
@@ -1302,14 +1295,15 @@ abstract class JdbcDatabase implements Database<Connection> {
AuthorId authorId = new AuthorId(rs.getBytes(2)); AuthorId authorId = new AuthorId(rs.getBytes(2));
int formatVersion = rs.getInt(3); int formatVersion = rs.getInt(3);
String name = rs.getString(4); String name = rs.getString(4);
byte[] publicKey = rs.getBytes(5); String alias = rs.getString(5);
byte[] publicKey = rs.getBytes(6);
Author author = Author author =
new Author(authorId, formatVersion, name, publicKey); new Author(authorId, formatVersion, name, publicKey);
AuthorId localAuthorId = new AuthorId(rs.getBytes(6)); AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
boolean verified = rs.getBoolean(7); boolean verified = rs.getBoolean(8);
boolean active = rs.getBoolean(8); boolean active = rs.getBoolean(9);
contacts.add(new Contact(contactId, author, localAuthorId, contacts.add(new Contact(contactId, author, localAuthorId,
verified, active)); alias, verified, active));
} }
rs.close(); rs.close();
ps.close(); ps.close();
@@ -1350,8 +1344,8 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT contactId, formatVersion, name, publicKey," String sql = "SELECT contactId, formatVersion, name, alias,"
+ " localAuthorId, verified, active" + " publicKey, localAuthorId, verified, active"
+ " FROM contacts" + " FROM contacts"
+ " WHERE authorId = ?"; + " WHERE authorId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
@@ -1362,14 +1356,15 @@ abstract class JdbcDatabase implements Database<Connection> {
ContactId c = new ContactId(rs.getInt(1)); ContactId c = new ContactId(rs.getInt(1));
int formatVersion = rs.getInt(2); int formatVersion = rs.getInt(2);
String name = rs.getString(3); String name = rs.getString(3);
byte[] publicKey = rs.getBytes(4); String alias = rs.getString(4);
AuthorId localAuthorId = new AuthorId(rs.getBytes(5)); byte[] publicKey = rs.getBytes(5);
boolean verified = rs.getBoolean(6); AuthorId localAuthorId = new AuthorId(rs.getBytes(6));
boolean active = rs.getBoolean(7); boolean verified = rs.getBoolean(7);
boolean active = rs.getBoolean(8);
Author author = Author author =
new Author(remote, formatVersion, name, publicKey); new Author(remote, formatVersion, name, publicKey);
contacts.add(new Contact(c, author, localAuthorId, verified, contacts.add(new Contact(c, author, localAuthorId, alias,
active)); verified, active));
} }
rs.close(); rs.close();
ps.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 @Override
public void setGroupVisibility(Connection txn, ContactId c, GroupId g, public void setGroupVisibility(Connection txn, ContactId c, GroupId g,
boolean shared) throws DbException { 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; 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.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; 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.AuthorFactory;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collection;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; 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.logDuration;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
@@ -118,26 +110,4 @@ class IdentityManagerImpl implements IdentityManager {
return db.getLocalAuthors(txn).iterator().next(); 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

@@ -23,7 +23,6 @@ import org.briarproject.bramble.api.plugin.event.EnableBluetoothEvent;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent; import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import org.briarproject.bramble.util.StringUtils;
import java.io.IOException; import java.io.IOException;
import java.security.SecureRandom; import java.security.SecureRandom;
@@ -46,6 +45,9 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES; import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress; import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
import static org.briarproject.bramble.util.StringUtils.macToBytes;
import static org.briarproject.bramble.util.StringUtils.macToString;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -96,6 +98,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
abstract DuplexTransportConnection connectTo(String address, String uuid) abstract DuplexTransportConnection connectTo(String address, String uuid)
throws IOException; throws IOException;
@Nullable
abstract DuplexTransportConnection discoverAndConnect(String uuid);
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter, BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
Executor ioExecutor, SecureRandom secureRandom, Executor ioExecutor, SecureRandom secureRandom,
Backoff backoff, DuplexPluginCallback callback, int maxLatency) { Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
@@ -193,7 +198,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
address = getBluetoothAddress(); address = getBluetoothAddress();
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Local address " + scrubMacAddress(address)); LOG.info("Local address " + scrubMacAddress(address));
if (!StringUtils.isNullOrEmpty(address)) { if (!isNullOrEmpty(address)) {
p.put(PROP_ADDRESS, address); p.put(PROP_ADDRESS, address);
changed = true; changed = true;
} }
@@ -256,9 +261,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
// Try to connect to known devices in parallel // Try to connect to known devices in parallel
for (Entry<ContactId, TransportProperties> e : contacts.entrySet()) { for (Entry<ContactId, TransportProperties> e : contacts.entrySet()) {
String address = e.getValue().get(PROP_ADDRESS); String address = e.getValue().get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) continue; if (isNullOrEmpty(address)) continue;
String uuid = e.getValue().get(PROP_UUID); String uuid = e.getValue().get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) continue; if (isNullOrEmpty(uuid)) continue;
ContactId c = e.getKey(); ContactId c = e.getKey();
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
if (!isRunning() || !shouldAllowContactConnections()) return; if (!isRunning() || !shouldAllowContactConnections()) return;
@@ -309,9 +314,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
if (!isRunning() || !shouldAllowContactConnections()) return null; if (!isRunning() || !shouldAllowContactConnections()) return null;
if (!connectionLimiter.canOpenContactConnection()) return null; if (!connectionLimiter.canOpenContactConnection()) return null;
String address = p.get(PROP_ADDRESS); String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) return null; if (isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID); String uuid = p.get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) return null; if (isNullOrEmpty(uuid)) return null;
DuplexTransportConnection conn = connect(address, uuid); DuplexTransportConnection conn = connect(address, uuid);
if (conn == null) return null; if (conn == null) return null;
// TODO: Why don't we reset the backoff here? // TODO: Why don't we reset the backoff here?
@@ -326,9 +331,6 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
@Override @Override
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) { public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
if (!isRunning()) return null; if (!isRunning()) return null;
// There's no point listening if we can't discover our own address
String address = getBluetoothAddress();
if (address == null) return null;
// No truncation necessary because COMMIT_LENGTH = 16 // No truncation necessary because COMMIT_LENGTH = 16
String uuid = UUID.nameUUIDFromBytes(commitment).toString(); String uuid = UUID.nameUUIDFromBytes(commitment).toString();
if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid); if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid);
@@ -346,7 +348,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
} }
BdfList descriptor = new BdfList(); BdfList descriptor = new BdfList();
descriptor.add(TRANSPORT_ID_BLUETOOTH); descriptor.add(TRANSPORT_ID_BLUETOOTH);
descriptor.add(StringUtils.macToBytes(address)); String address = getBluetoothAddress();
if (address != null) descriptor.add(macToBytes(address));
return new BluetoothKeyAgreementListener(descriptor, ss); return new BluetoothKeyAgreementListener(descriptor, ss);
} }
@@ -354,18 +357,25 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
public DuplexTransportConnection createKeyAgreementConnection( public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor) { byte[] commitment, BdfList descriptor) {
if (!isRunning()) return null; if (!isRunning()) return null;
String address;
try {
address = parseAddress(descriptor);
} catch (FormatException e) {
LOG.info("Invalid address in key agreement descriptor");
return null;
}
// No truncation necessary because COMMIT_LENGTH = 16 // No truncation necessary because COMMIT_LENGTH = 16
String uuid = UUID.nameUUIDFromBytes(commitment).toString(); String uuid = UUID.nameUUIDFromBytes(commitment).toString();
if (LOG.isLoggable(INFO)) DuplexTransportConnection conn;
LOG.info("Connecting to key agreement UUID " + uuid); if (descriptor.size() == 1) {
DuplexTransportConnection conn = connect(address, uuid); if (LOG.isLoggable(INFO))
LOG.info("Discovering address for key agreement UUID " + uuid);
conn = discoverAndConnect(uuid);
} else {
String address;
try {
address = parseAddress(descriptor);
} catch (FormatException e) {
LOG.info("Invalid address in key agreement descriptor");
return null;
}
if (LOG.isLoggable(INFO))
LOG.info("Connecting to key agreement UUID " + uuid);
conn = connect(address, uuid);
}
if (conn != null) connectionLimiter.keyAgreementConnectionOpened(conn); if (conn != null) connectionLimiter.keyAgreementConnectionOpened(conn);
return conn; return conn;
} }
@@ -373,7 +383,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
private String parseAddress(BdfList descriptor) throws FormatException { private String parseAddress(BdfList descriptor) throws FormatException {
byte[] mac = descriptor.getRaw(1); byte[] mac = descriptor.getRaw(1);
if (mac.length != 6) throw new FormatException(); if (mac.length != 6) throw new FormatException();
return StringUtils.macToString(mac); return macToString(mac);
} }
@Override @Override

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.contact.ContactManager;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; 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.NoSuchContactException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; 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.api.transport.KeyManager;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.Mockery; import org.jmock.Mockery;
import org.junit.Test; import org.junit.Test;
@@ -20,10 +25,20 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Random; 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.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; 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.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
public class ContactManagerImplTest extends BrambleMockTestCase { public class ContactManagerImplTest extends BrambleMockTestCase {
@@ -31,16 +46,20 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
private final Mockery context = new Mockery(); private final Mockery context = new Mockery();
private final DatabaseComponent db = context.mock(DatabaseComponent.class); private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final KeyManager keyManager = context.mock(KeyManager.class); private final KeyManager keyManager = context.mock(KeyManager.class);
private final IdentityManager identityManager =
context.mock(IdentityManager.class);
private final ContactManager contactManager; private final ContactManager contactManager;
private final ContactId contactId = new ContactId(42); private final ContactId contactId = new ContactId(42);
private final Author remote = getAuthor(); private final Author remote = getAuthor();
private final AuthorId local = new AuthorId(getRandomId()); 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 boolean verified = false, active = true;
private final Contact contact = private final Contact contact =
new Contact(contactId, remote, local, verified, active); new Contact(contactId, remote, local, alias, verified, active);
public ContactManagerImplTest() { public ContactManagerImplTest() {
contactManager = new ContactManagerImpl(db, keyManager); contactManager = new ContactManagerImpl(db, keyManager, identityManager);
} }
@Test @Test
@@ -105,7 +124,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
oneOf(db).startTransaction(true); oneOf(db).startTransaction(true);
will(returnValue(txn)); will(returnValue(txn));
oneOf(db).getContactsByAuthorId(txn, remote.getId()); oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(Collections.emptyList())); will(returnValue(emptyList()));
oneOf(db).endTransaction(txn); oneOf(db).endTransaction(txn);
}}); }});
@@ -131,7 +150,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
public void testActiveContacts() throws Exception { public void testActiveContacts() throws Exception {
Collection<Contact> activeContacts = Collections.singletonList(contact); Collection<Contact> activeContacts = Collections.singletonList(contact);
Collection<Contact> contacts = new ArrayList<>(activeContacts); 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); Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(true); oneOf(db).startTransaction(true);
@@ -171,6 +191,23 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
contactManager.setContactActive(txn, contactId, active); 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 @Test
public void testContactExists() throws Exception { public void testContactExists() throws Exception {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
@@ -186,4 +223,79 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
assertTrue(contactManager.contactExists(remote.getId(), local)); 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.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId; 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.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@@ -99,6 +100,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
private final Group group; private final Group group;
private final Author author; private final Author author;
private final LocalAuthor localAuthor; private final LocalAuthor localAuthor;
private final String alias;
private final Message message, message1; private final Message message, message1;
private final MessageId messageId, messageId1; private final MessageId messageId, messageId1;
private final Metadata metadata; private final Metadata metadata;
@@ -115,6 +117,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
groupId = group.getId(); groupId = group.getId();
author = getAuthor(); author = getAuthor();
localAuthor = getLocalAuthor(); localAuthor = getLocalAuthor();
alias = getRandomString(5);
message = getMessage(groupId); message = getMessage(groupId);
message1 = getMessage(groupId); message1 = getMessage(groupId);
messageId = message.getId(); messageId = message.getId();
@@ -124,7 +127,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
transportId = getTransportId(); transportId = getTransportId();
maxLatency = Integer.MAX_VALUE; maxLatency = Integer.MAX_VALUE;
contactId = new ContactId(234); contactId = new ContactId(234);
contact = new Contact(contactId, author, localAuthor.getId(), contact = new Contact(contactId, author, localAuthor.getId(), alias,
true, true); true, true);
keySetId = new KeySetId(345); keySetId = new KeySetId(345);
} }
@@ -288,11 +291,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the contact is in the DB (which it's not) // 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)); will(returnValue(txn));
exactly(16).of(database).containsContact(txn, contactId); exactly(17).of(database).containsContact(txn, contactId);
will(returnValue(false)); will(returnValue(false));
exactly(16).of(database).abortTransaction(txn); exactly(17).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
@@ -450,6 +453,16 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
db.endTransaction(transaction); 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); transaction = db.startTransaction(false);
try { try {
db.setGroupVisibility(transaction, contactId, groupId, SHARED); 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.Collections.singletonMap;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.db.Metadata.REMOVE; 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.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE; 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.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory; import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getTransportId; 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.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@@ -1713,6 +1715,39 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close(); 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 @Test
public void testSetMessageState() throws Exception { public void testSetMessageState() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);

View File

@@ -1,7 +1,5 @@
package org.briarproject.bramble.identity; 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.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PrivateKey; 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.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; 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.AuthorFactory;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
@@ -19,15 +15,9 @@ import org.jmock.Expectations;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; 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.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -107,60 +97,4 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
assertEquals(localAuthor, identityManager.getLocalAuthor()); 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.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId; 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.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
@@ -612,7 +613,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
private Contact getContact(boolean active) { private Contact getContact(boolean active) {
ContactId c = new ContactId(nextContactId++); ContactId c = new ContactId(nextContactId++);
return new Contact(c, getAuthor(), localAuthor.getId(), return new Contact(c, getAuthor(), localAuthor.getId(),
true, active); getRandomString(5), true, active);
} }
private void expectGetLocalProperties(Transaction txn) throws Exception { 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.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId; 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.assertEquals;
public class KeyManagerImplTest extends BrambleMockTestCase { public class KeyManagerImplTest extends BrambleMockTestCase {
@@ -66,10 +67,10 @@ public class KeyManagerImplTest extends BrambleMockTestCase {
Author remoteAuthor = getAuthor(); Author remoteAuthor = getAuthor();
AuthorId localAuthorId = new AuthorId(getRandomId()); AuthorId localAuthorId = new AuthorId(getRandomId());
Collection<Contact> contacts = new ArrayList<>(); Collection<Contact> contacts = new ArrayList<>();
contacts.add(new Contact(contactId, remoteAuthor, localAuthorId, true, contacts.add(new Contact(contactId, remoteAuthor, localAuthorId,
true)); getRandomString(5), true, true));
contacts.add(new Contact(inactiveContactId, remoteAuthor, localAuthorId, contacts.add(new Contact(inactiveContactId, remoteAuthor, localAuthorId,
true, false)); getRandomString(5), true, false));
SimplexPluginFactory pluginFactory = SimplexPluginFactory pluginFactory =
context.mock(SimplexPluginFactory.class); context.mock(SimplexPluginFactory.class);
Collection<SimplexPluginFactory> factories = 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.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId; 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.GROUP_KEY_CONTACT_ID;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL; import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION; 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 localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Contact contact = new Contact(new ContactId(123), 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 ClientId clientId = getClientId();
private final long now = System.currentTimeMillis(); private final long now = System.currentTimeMillis();
private final Transaction txn = new Transaction(null, false); private final Transaction txn = new Transaction(null, false);

View File

@@ -108,6 +108,12 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
return wrapSocket((StreamConnection) Connector.open(url)); return wrapSocket((StreamConnection) Connector.open(url));
} }
@Override
@Nullable
DuplexTransportConnection discoverAndConnect(String uuid) {
return null; // TODO
}
private String makeUrl(String address, String uuid) { private String makeUrl(String address, String uuid) {
return "btspp://" + address + ":" + uuid + ";name=RFCOMM"; return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
} }

View File

@@ -105,6 +105,7 @@ dependencies {
implementation "com.android.support:cardview-v7:$supportVersion" implementation "com.android.support:cardview-v7:$supportVersion"
implementation "com.android.support:support-annotations:$supportVersion" implementation "com.android.support:support-annotations:$supportVersion"
implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation "android.arch.lifecycle:extensions:1.1.1"
implementation('ch.acra:acra:4.11') { implementation('ch.acra:acra:4.11') {
exclude module: 'support-v4' exclude module: 'support-v4'

View File

@@ -7,6 +7,7 @@
<uses-feature android:name="android.hardware.camera" android:required="false"/> <uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.touchscreen" android:required="false" /> <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH" />

View File

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

View File

@@ -107,7 +107,7 @@ public class AppModule {
Context appContext = app.getApplicationContext(); Context appContext = app.getApplicationContext();
DuplexPluginFactory bluetooth = DuplexPluginFactory bluetooth =
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor, new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
appContext, random, eventBus, backoffFactory); appContext, random, eventBus, clock, backoffFactory);
DuplexPluginFactory tor = new AndroidTorPluginFactory(ioExecutor, DuplexPluginFactory tor = new AndroidTorPluginFactory(ioExecutor,
scheduler, appContext, networkManager, locationUtils, eventBus, scheduler, appContext, networkManager, locationUtils, eventBus,
torSocketFactory, backoffFactory, resourceProvider, torSocketFactory, backoffFactory, resourceProvider,

View File

@@ -34,6 +34,7 @@ import org.briarproject.briar.api.android.ScreenFilterMonitor.AppDetails;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
@@ -42,6 +43,8 @@ import static android.arch.lifecycle.Lifecycle.State.STARTED;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT; import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS; import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
/** /**
@@ -51,6 +54,8 @@ import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOT
public abstract class BaseActivity extends AppCompatActivity public abstract class BaseActivity extends AppCompatActivity
implements DestroyableContext, OnTapFilteredListener { implements DestroyableContext, OnTapFilteredListener {
private final static Logger LOG = getLogger(BaseActivity.class.getName());
@Inject @Inject
protected ScreenFilterMonitor screenFilterMonitor; protected ScreenFilterMonitor screenFilterMonitor;
@@ -119,6 +124,8 @@ public abstract class BaseActivity extends AppCompatActivity
@Override @Override
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();
if (LOG.isLoggable(INFO))
LOG.info("Starting " + this.getClass().getSimpleName());
for (ActivityLifecycleController alc : lifecycleControllers) { for (ActivityLifecycleController alc : lifecycleControllers) {
alc.onActivityStart(); alc.onActivityStart();
} }
@@ -137,6 +144,8 @@ public abstract class BaseActivity extends AppCompatActivity
@Override @Override
protected void onStop() { protected void onStop() {
super.onStop(); super.onStop();
if (LOG.isLoggable(INFO))
LOG.info("Stopping " + this.getClass().getSimpleName());
for (ActivityLifecycleController alc : lifecycleControllers) { for (ActivityLifecycleController alc : lifecycleControllers) {
alc.onActivityStop(); alc.onActivityStop();
} }

View File

@@ -9,9 +9,9 @@ public interface RequestCodes {
int REQUEST_WRITE_BLOG_POST = 5; int REQUEST_WRITE_BLOG_POST = 5;
int REQUEST_SHARE_BLOG = 6; int REQUEST_SHARE_BLOG = 6;
int REQUEST_RINGTONE = 7; int REQUEST_RINGTONE = 7;
int REQUEST_PERMISSION_CAMERA = 8; int REQUEST_PERMISSION_CAMERA_LOCATION = 8;
int REQUEST_DOZE_WHITELISTING = 9; int REQUEST_DOZE_WHITELISTING = 9;
int REQUEST_ENABLE_BLUETOOTH = 10; int REQUEST_BLUETOOTH_DISCOVERABLE = 10;
int REQUEST_UNLOCK = 11; int REQUEST_UNLOCK = 11;
int REQUEST_KEYGUARD_UNLOCK = 12; int REQUEST_KEYGUARD_UNLOCK = 12;

View File

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

View File

@@ -14,7 +14,6 @@ import android.view.ViewGroup;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.TextView; import android.widget.TextView;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.view.AuthorView; import org.briarproject.briar.android.view.AuthorView;
@@ -98,9 +97,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
// author and date // author and date
BlogPostHeader post = item.getPostHeader(); BlogPostHeader post = item.getPostHeader();
Author a = post.getAuthor(); author.setAuthor(post.getAuthor(), post.getAuthorInfo());
author.setAuthor(a);
author.setAuthorStatus(post.getAuthorStatus());
author.setDate(post.getTimestamp()); author.setDate(post.getTimestamp());
author.setPersona( author.setPersona(
item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL); item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL);
@@ -143,8 +140,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
private void onBindComment(BlogCommentItem item) { private void onBindComment(BlogCommentItem item) {
// reblogger // reblogger
reblogger.setAuthor(item.getAuthor()); reblogger.setAuthor(item.getAuthor(), item.getAuthorInfo());
reblogger.setAuthorStatus(item.getAuthorStatus());
reblogger.setDate(item.getTimestamp()); reblogger.setDate(item.getTimestamp());
if (!fullText) { if (!fullText) {
reblogger.setAuthorClickable(v -> listener.onAuthorClick(item)); reblogger.setAuthorClickable(v -> listener.onAuthorClick(item));
@@ -165,8 +161,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
AuthorView author = v.findViewById(R.id.authorView); AuthorView author = v.findViewById(R.id.authorView);
TextView text = v.findViewById(R.id.textView); TextView text = v.findViewById(R.id.textView);
author.setAuthor(c.getAuthor()); author.setAuthor(c.getAuthor(), c.getAuthorInfo());
author.setAuthorStatus(c.getAuthorStatus());
author.setDate(c.getTimestamp()); author.setDate(c.getTimestamp());
// TODO make author clickable #624 // 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; package org.briarproject.briar.android.contact;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull;
import android.view.View; import android.view.View;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
@@ -9,6 +10,7 @@ import org.briarproject.briar.android.util.BriarAdapter;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static android.support.v7.util.SortedList.INVALID_POSITION; 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>> public abstract class BaseContactListAdapter<I extends ContactItem, VH extends ContactItemViewHolder<I>>
extends BriarAdapter<I, VH> { extends BriarAdapter<I, VH> {
@@ -23,15 +25,15 @@ public abstract class BaseContactListAdapter<I extends ContactItem, VH extends C
} }
@Override @Override
public void onBindViewHolder(VH ui, int position) { public void onBindViewHolder(@NonNull VH ui, int position) {
I item = items.get(position); I item = items.get(position);
ui.bind(item, listener); ui.bind(item, listener);
} }
@Override @Override
public int compare(I c1, I c2) { public int compare(I c1, I c2) {
return c1.getContact().getAuthor().getName() return getContactDisplayName(c1.getContact())
.compareTo(c2.getContact().getAuthor().getName()); .compareTo(getContactDisplayName(c2.getContact()));
} }
@Override @Override

View File

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

View File

@@ -1,7 +1,8 @@
package org.briarproject.briar.android.contact; 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.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
@@ -23,7 +24,6 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.briarproject.bramble.api.FormatException; 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.ContactId;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; 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.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener; 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.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionRegistry; import org.briarproject.bramble.api.plugin.ConnectionRegistry;
@@ -131,8 +130,8 @@ public class ConversationActivity extends BriarActivity
Executor cryptoExecutor; Executor cryptoExecutor;
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>(); private final Map<MessageId, String> textCache = new ConcurrentHashMap<>();
private final MutableLiveData<String> contactName = new MutableLiveData<>();
private ConversationViewModel viewModel;
private ConversationVisitor visitor; private ConversationVisitor visitor;
private ConversationAdapter adapter; private ConversationAdapter adapter;
private Toolbar toolbar; private Toolbar toolbar;
@@ -166,8 +165,6 @@ public class ConversationActivity extends BriarActivity
private volatile ContactId contactId; private volatile ContactId contactId;
@Nullable @Nullable
private volatile AuthorId contactAuthorId;
@Nullable
private volatile GroupId messagingGroupId; private volatile GroupId messagingGroupId;
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
@@ -176,6 +173,9 @@ public class ConversationActivity extends BriarActivity
setSceneTransitionAnimation(); setSceneTransitionAnimation();
super.onCreate(state); super.onCreate(state);
viewModel =
ViewModelProviders.of(this).get(ConversationViewModel.class);
Intent i = getIntent(); Intent i = getIntent();
int id = i.getIntExtra(CONTACT_ID, -1); int id = i.getIntExtra(CONTACT_ID, -1);
if (id == -1) throw new IllegalStateException(); if (id == -1) throw new IllegalStateException();
@@ -185,16 +185,29 @@ public class ConversationActivity extends BriarActivity
// Custom Toolbar // Custom Toolbar
toolbar = setUpCustomToolbar(true); toolbar = setUpCustomToolbar(true);
if (toolbar != null) { toolbarAvatar = toolbar.findViewById(R.id.contactAvatar);
toolbarAvatar = toolbar.findViewById(R.id.contactAvatar); toolbarStatus = toolbar.findViewById(R.id.contactStatus);
toolbarStatus = toolbar.findViewById(R.id.contactStatus); toolbarTitle = toolbar.findViewById(R.id.contactName);
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(toolbarAvatar, getAvatarTransitionName(contactId));
setTransitionName(toolbarStatus, getBulbTransitionName(contactId)); setTransitionName(toolbarStatus, getBulbTransitionName(contactId));
visitor = new ConversationVisitor(this, this, contactName); visitor = new ConversationVisitor(this, this,
viewModel.getContactDisplayName());
adapter = new ConversationAdapter(this, this); adapter = new ConversationAdapter(this, this);
list = findViewById(R.id.conversationView); list = findViewById(R.id.conversationView);
list.setLayoutManager(new LinearLayoutManager(this)); list.setLayoutManager(new LinearLayoutManager(this));
@@ -229,7 +242,19 @@ public class ConversationActivity extends BriarActivity
notificationManager.blockContactNotification(contactId); notificationManager.blockContactNotification(contactId);
notificationManager.clearContactNotification(contactId); notificationManager.clearContactNotification(contactId);
displayContactOnlineStatus(); 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(); list.startPeriodicUpdate();
} }
@@ -266,6 +291,10 @@ public class ConversationActivity extends BriarActivity
intent.putExtra(CONTACT_ID, contactId.getInt()); intent.putExtra(CONTACT_ID, contactId.getInt());
startActivityForResult(intent, REQUEST_INTRODUCTION); startActivityForResult(intent, REQUEST_INTRODUCTION);
return true; return true;
case R.id.action_set_alias:
AliasDialogFragment.newInstance(contactId).show(
getSupportFragmentManager(), AliasDialogFragment.TAG);
return true;
case R.id.action_social_remove_person: case R.id.action_social_remove_person:
askToRemoveContact(); askToRemoveContact();
return true; 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() { private void displayContactOnlineStatus() {
runOnUiThreadUnlessDestroyed(() -> { runOnUiThreadUnlessDestroyed(() -> {
if (connectionRegistry.isConnected(contactId)) { if (connectionRegistry.isConnected(contactId)) {
@@ -453,15 +452,17 @@ public class ConversationActivity extends BriarActivity
private void onNewPrivateMessage(PrivateMessageHeader h) { private void onNewPrivateMessage(PrivateMessageHeader h) {
runOnUiThreadUnlessDestroyed(() -> { runOnUiThreadUnlessDestroyed(() -> {
if (h instanceof PrivateRequest || h instanceof PrivateResponse) { if (h instanceof PrivateRequest || h instanceof PrivateResponse) {
String cName = contactName.getValue(); String cName = viewModel.getContactDisplayName().getValue();
if (cName == null) { if (cName == null) {
// Wait for the contact name to be loaded // Wait for the contact name to be loaded
contactName.observe(this, new Observer<String>() { viewModel.getContactDisplayName()
.observe(this, new Observer<String>() {
@Override @Override
public void onChanged(@Nullable String cName) { public void onChanged(@Nullable String cName) {
if (cName != null) { if (cName != null) {
addConversationItem(h.accept(visitor)); 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.FORUM;
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.GROUP; 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.contact.ConversationRequestItem.RequestType.INTRODUCTION;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
@@ -188,33 +189,36 @@ class ConversationVisitor implements PrivateMessageVisitor<ConversationItem> {
@Override @Override
public ConversationItem visitIntroductionRequest(IntroductionRequest r) { public ConversationItem visitIntroductionRequest(IntroductionRequest r) {
String name = getContactDisplayName(r.getNameable(), r.getAlias());
if (r.isLocal()) { if (r.isLocal()) {
String text = ctx.getString(R.string.introduction_request_sent, String text = ctx.getString(R.string.introduction_request_sent,
contactName.getValue(), r.getName()); contactName.getValue(), name);
return new ConversationNoticeOutItem(text, r); return new ConversationNoticeOutItem(text, r);
} else { } else {
String text = ctx.getString(R.string.introduction_request_received, String text = ctx.getString(R.string.introduction_request_received,
contactName.getValue(), r.getName()); contactName.getValue(), name);
return new ConversationRequestItem(text, INTRODUCTION, r); return new ConversationRequestItem(text, INTRODUCTION, r);
} }
} }
@Override @Override
public ConversationItem visitIntroductionResponse(IntroductionResponse r) { public ConversationItem visitIntroductionResponse(IntroductionResponse r) {
String introducedAuthor =
getContactDisplayName(r.getIntroducedAuthor(),
r.getIntroducedAuthorInfo().getAlias());
if (r.isLocal()) { if (r.isLocal()) {
String text; String text;
if (r.wasAccepted()) { if (r.wasAccepted()) {
String introducee = r.getIntroducedAuthor().getName();
text = ctx.getString( text = ctx.getString(
R.string.introduction_response_accepted_sent, R.string.introduction_response_accepted_sent,
introducee) introducedAuthor)
+ "\n\n" + ctx.getString( + "\n\n" + ctx.getString(
R.string.introduction_response_accepted_sent_info, R.string.introduction_response_accepted_sent_info,
introducee); introducedAuthor);
} else { } else {
text = ctx.getString( text = ctx.getString(
R.string.introduction_response_declined_sent, R.string.introduction_response_declined_sent,
r.getIntroducedAuthor().getName()); introducedAuthor);
} }
return new ConversationNoticeOutItem(text, r); return new ConversationNoticeOutItem(text, r);
} else { } else {
@@ -223,17 +227,17 @@ class ConversationVisitor implements PrivateMessageVisitor<ConversationItem> {
text = ctx.getString( text = ctx.getString(
R.string.introduction_response_accepted_received, R.string.introduction_response_accepted_received,
contactName.getValue(), contactName.getValue(),
r.getIntroducedAuthor().getName()); introducedAuthor);
} else if (r.isIntroducer()) { } else if (r.isIntroducer()) {
text = ctx.getString( text = ctx.getString(
R.string.introduction_response_declined_received, R.string.introduction_response_declined_received,
contactName.getValue(), contactName.getValue(),
r.getIntroducedAuthor().getName()); introducedAuthor);
} else { } else {
text = ctx.getString( text = ctx.getString(
R.string.introduction_response_declined_received_by_introducee, R.string.introduction_response_declined_received_by_introducee,
contactName.getValue(), contactName.getValue(),
r.getIntroducedAuthor().getName()); introducedAuthor);
} }
return new ConversationNoticeInItem(text, r); return new ConversationNoticeInItem(text, r);
} }

View File

@@ -1,7 +1,7 @@
package org.briarproject.briar.android.forum; package org.briarproject.briar.android.forum;
import org.briarproject.bramble.api.identity.Author; 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.bramble.api.sync.MessageId;
import org.briarproject.briar.android.threaded.ThreadItem; import org.briarproject.briar.android.threaded.ThreadItem;
import org.briarproject.briar.api.forum.ForumPostHeader; import org.briarproject.briar.api.forum.ForumPostHeader;
@@ -14,11 +14,11 @@ class ForumItem extends ThreadItem {
ForumItem(ForumPostHeader h, String text) { ForumItem(ForumPostHeader h, String text) {
super(h.getId(), h.getParentId(), text, h.getTimestamp(), h.getAuthor(), 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, 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); 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 android.widget.Toast.LENGTH_SHORT;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException; 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; import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_TEXT_LENGTH;
public class IntroductionMessageFragment extends BaseFragment public class IntroductionMessageFragment extends BaseFragment
@@ -148,8 +149,8 @@ public class IntroductionMessageFragment extends BaseFragment
c2.getAuthor().getId().getBytes())); c2.getAuthor().getId().getBytes()));
// set contact names // set contact names
ui.contactName1.setText(c1.getAuthor().getName()); ui.contactName1.setText(getContactDisplayName(c1));
ui.contactName2.setText(c2.getAuthor().getName()); ui.contactName2.setText(getContactDisplayName(c2));
// hide progress bar // hide progress bar
ui.progressBar.setVisibility(GONE); ui.progressBar.setVisibility(GONE);

View File

@@ -3,7 +3,6 @@ package org.briarproject.briar.android.keyagreement;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.os.Bundle; import android.os.Bundle;
@@ -11,19 +10,15 @@ import android.support.annotation.StringRes;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog.Builder; import android.support.v7.app.AlertDialog.Builder;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.Toast;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent; import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.R.string;
import org.briarproject.briar.R.style;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity; import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.fragment.BaseFragment;
@@ -37,16 +32,19 @@ import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.CAMERA; import static android.Manifest.permission.CAMERA;
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_ENABLE; import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED; import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE; import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
import static android.bluetooth.BluetoothAdapter.STATE_ON; import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION.SDK_INT; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE;
import static android.widget.Toast.LENGTH_LONG; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_ENABLE_BLUETOOTH;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -55,7 +53,11 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
KeyAgreementEventListener { KeyAgreementEventListener {
private enum BluetoothState { private enum BluetoothState {
UNKNOWN, NO_ADAPTER, WAITING, REFUSED, ENABLED UNKNOWN, NO_ADAPTER, WAITING, REFUSED, ENABLED, DISCOVERABLE
}
private enum Permission {
UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED
} }
private static final Logger LOG = private static final Logger LOG =
@@ -64,8 +66,27 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
@Inject @Inject
EventBus eventBus; EventBus eventBus;
private boolean isResumed = false, enableWasRequested = false; /**
private boolean continueClicked, gotCameraPermission; * Set to true in onPostResume() and false in onPause(). This prevents the
* QR code fragment from being shown if onRequestPermissionsResult() is
* called while the activity is paused, which could cause a crash due to
* https://issuetracker.google.com/issues/37067655.
*/
private boolean isResumed = false;
/**
* Set to true when the continue button is clicked, and false when the QR
* code fragment is shown. This prevents the QR code fragment from being
* shown automatically before the continue button has been clicked.
*/
private boolean continueClicked = false;
/**
* Records whether the Bluetooth adapter was already enabled before we
* asked for Bluetooth discoverability, so we know whether to broadcast a
* {@link BluetoothEnabledEvent}.
*/
private boolean wasAdapterEnabled = false;
private Permission cameraPermission = Permission.UNKNOWN;
private Permission locationPermission = Permission.UNKNOWN;
private BluetoothState bluetoothState = BluetoothState.UNKNOWN; private BluetoothState bluetoothState = BluetoothState.UNKNOWN;
private BroadcastReceiver bluetoothReceiver = null; private BroadcastReceiver bluetoothReceiver = null;
@@ -85,7 +106,9 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
if (state == null) { if (state == null) {
showInitialFragment(IntroFragment.newInstance()); showInitialFragment(IntroFragment.newInstance());
} }
IntentFilter filter = new IntentFilter(ACTION_STATE_CHANGED); IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_STATE_CHANGED);
filter.addAction(ACTION_SCAN_MODE_CHANGED);
bluetoothReceiver = new BluetoothStateReceiver(); bluetoothReceiver = new BluetoothStateReceiver();
registerReceiver(bluetoothReceiver, filter); registerReceiver(bluetoothReceiver, filter);
} }
@@ -107,20 +130,40 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
} }
} }
@Override
public void onStart() {
super.onStart();
// Permissions may have been granted manually while we were stopped
cameraPermission = Permission.UNKNOWN;
locationPermission = Permission.UNKNOWN;
}
@Override @Override
protected void onPostResume() { protected void onPostResume() {
super.onPostResume(); super.onPostResume();
isResumed = true; isResumed = true;
// Workaround for // Workaround for
// https://code.google.com/p/android/issues/detail?id=190966 // https://code.google.com/p/android/issues/detail?id=190966
if (canShowQrCodeFragment()) showQrCodeFragment(); showQrCodeFragmentIfAllowed();
} }
private boolean canShowQrCodeFragment() { private void showQrCodeFragmentIfAllowed() {
return isResumed && continueClicked if (isResumed && continueClicked && areEssentialPermissionsGranted()) {
&& (SDK_INT < 23 || gotCameraPermission) if (bluetoothState == BluetoothState.UNKNOWN ||
&& bluetoothState != BluetoothState.UNKNOWN bluetoothState == BluetoothState.ENABLED) {
&& bluetoothState != BluetoothState.WAITING; requestBluetoothDiscoverable();
} else if (bluetoothState != BluetoothState.WAITING) {
showQrCodeFragment();
}
}
}
private boolean areEssentialPermissionsGranted() {
// If the camera permission has been granted, and the location
// permission has been granted or permanently denied, we can continue
return cameraPermission == Permission.GRANTED &&
(locationPermission == Permission.GRANTED ||
locationPermission == Permission.PERMANENTLY_DENIED);
} }
@Override @Override
@@ -132,50 +175,54 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
@Override @Override
public void showNextScreen() { public void showNextScreen() {
continueClicked = true; continueClicked = true;
if (checkPermissions()) { if (checkPermissions()) showQrCodeFragmentIfAllowed();
if (shouldRequestEnableBluetooth()) requestEnableBluetooth();
else if (canShowQrCodeFragment()) showQrCodeFragment();
}
} }
private boolean shouldRequestEnableBluetooth() { private void requestBluetoothDiscoverable() {
return bluetoothState == BluetoothState.UNKNOWN
|| bluetoothState == BluetoothState.REFUSED;
}
private void requestEnableBluetooth() {
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
if (bt == null) { if (bt == null) {
setBluetoothState(BluetoothState.NO_ADAPTER); setBluetoothState(BluetoothState.NO_ADAPTER);
} else if (bt.isEnabled()) {
setBluetoothState(BluetoothState.ENABLED);
} else { } else {
enableWasRequested = true;
setBluetoothState(BluetoothState.WAITING); setBluetoothState(BluetoothState.WAITING);
Intent i = new Intent(ACTION_REQUEST_ENABLE); wasAdapterEnabled = bt.isEnabled();
startActivityForResult(i, REQUEST_ENABLE_BLUETOOTH); Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
startActivityForResult(i, REQUEST_BLUETOOTH_DISCOVERABLE);
} }
} }
private void setBluetoothState(BluetoothState bluetoothState) { private void setBluetoothState(BluetoothState bluetoothState) {
LOG.info("Setting Bluetooth state to " + bluetoothState); LOG.info("Setting Bluetooth state to " + bluetoothState);
this.bluetoothState = bluetoothState; this.bluetoothState = bluetoothState;
if (enableWasRequested && bluetoothState == BluetoothState.ENABLED) { if (!wasAdapterEnabled && bluetoothState == BluetoothState.ENABLED) {
eventBus.broadcast(new BluetoothEnabledEvent()); eventBus.broadcast(new BluetoothEnabledEvent());
enableWasRequested = false; wasAdapterEnabled = true;
} }
if (canShowQrCodeFragment()) showQrCodeFragment(); showQrCodeFragmentIfAllowed();
} }
@Override @Override
public void onActivityResult(int request, int result, Intent data) { public void onActivityResult(int request, int result, Intent data) {
// If the request was granted we'll catch the state change event if (request == REQUEST_BLUETOOTH_DISCOVERABLE) {
if (request == REQUEST_ENABLE_BLUETOOTH && result == RESULT_CANCELED) if (result == RESULT_CANCELED) {
setBluetoothState(BluetoothState.REFUSED); setBluetoothState(BluetoothState.REFUSED);
} else {
// If Bluetooth is already discoverable, show the QR code -
// otherwise wait for the state or scan mode to change
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
if (bt == null) throw new AssertionError();
if (bt.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE)
setBluetoothState(BluetoothState.DISCOVERABLE);
}
}
} }
private void showQrCodeFragment() { private void showQrCodeFragment() {
// If we return to the intro fragment, the continue button needs to be
// clicked again before showing the QR code fragment
continueClicked = false; continueClicked = false;
// If we return to the intro fragment, ask for Bluetooth
// discoverability again before showing the QR code fragment
bluetoothState = BluetoothState.UNKNOWN;
// FIXME #824 // FIXME #824
FragmentManager fm = getSupportFragmentManager(); FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentByTag(KeyAgreementFragment.TAG) == null) { if (fm.findFragmentByTag(KeyAgreementFragment.TAG) == null) {
@@ -194,74 +241,113 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
} }
private boolean checkPermissions() { private boolean checkPermissions() {
if (ContextCompat.checkSelfPermission(this, CAMERA) != if (areEssentialPermissionsGranted()) return true;
PERMISSION_GRANTED) { // If the camera permission has been permanently denied, ask the
// Should we show an explanation? // user to change the setting
if (ActivityCompat.shouldShowRequestPermissionRationale(this, if (cameraPermission == Permission.PERMANENTLY_DENIED) {
CAMERA)) { Builder builder = new Builder(this, R.style.BriarDialogTheme);
OnClickListener continueListener = builder.setTitle(R.string.permission_camera_title);
(dialog, which) -> requestPermission(); builder.setMessage(R.string.permission_camera_denied_body);
Builder builder = new Builder(this, style.BriarDialogTheme); builder.setPositiveButton(R.string.ok,
builder.setTitle(string.permission_camera_title); UiUtils.getGoToSettingsListener(this));
builder.setMessage(string.permission_camera_request_body); builder.setNegativeButton(R.string.cancel,
builder.setNeutralButton(string.continue_button, (dialog, which) -> supportFinishAfterTransition());
continueListener); builder.show();
builder.show();
} else {
requestPermission();
}
gotCameraPermission = false;
return false; return false;
} else {
gotCameraPermission = true;
return true;
} }
// Should we show the rationale for one or both permissions?
if (cameraPermission == Permission.SHOW_RATIONALE &&
locationPermission == Permission.SHOW_RATIONALE) {
showRationale(R.string.permission_camera_location_title,
R.string.permission_camera_location_request_body);
} else if (cameraPermission == Permission.SHOW_RATIONALE) {
showRationale(R.string.permission_camera_title,
R.string.permission_camera_request_body);
} else if (locationPermission == Permission.SHOW_RATIONALE) {
showRationale(R.string.permission_location_title,
R.string.permission_location_request_body);
} else {
requestPermissions();
}
return false;
} }
private void requestPermission() { private void showRationale(@StringRes int title, @StringRes int body) {
ActivityCompat.requestPermissions(this, new String[] {CAMERA}, Builder builder = new Builder(this, R.style.BriarDialogTheme);
REQUEST_PERMISSION_CAMERA); builder.setTitle(title);
builder.setMessage(body);
builder.setNeutralButton(R.string.continue_button,
(dialog, which) -> requestPermissions());
builder.show();
}
private void requestPermissions() {
ActivityCompat.requestPermissions(this,
new String[] {CAMERA, ACCESS_COARSE_LOCATION},
REQUEST_PERMISSION_CAMERA_LOCATION);
} }
@Override @Override
@UiThread @UiThread
public void onRequestPermissionsResult(int requestCode, public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) { String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_PERMISSION_CAMERA) { if (requestCode != REQUEST_PERMISSION_CAMERA_LOCATION)
// If request is cancelled, the result arrays are empty. throw new AssertionError();
if (grantResults.length > 0 && if (gotPermission(CAMERA, permissions, grantResults)) {
grantResults[0] == PERMISSION_GRANTED) { cameraPermission = Permission.GRANTED;
gotCameraPermission = true; } else if (shouldShowRationale(CAMERA)) {
showNextScreen(); cameraPermission = Permission.SHOW_RATIONALE;
} else { } else {
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, cameraPermission = Permission.PERMANENTLY_DENIED;
CAMERA)) {
// The user has permanently denied the request
OnClickListener cancelListener =
(dialog, which) -> supportFinishAfterTransition();
Builder builder = new Builder(this, style.BriarDialogTheme);
builder.setTitle(string.permission_camera_title);
builder.setMessage(string.permission_camera_denied_body);
builder.setPositiveButton(string.ok,
UiUtils.getGoToSettingsListener(this));
builder.setNegativeButton(string.cancel, cancelListener);
builder.show();
} else {
Toast.makeText(this, string.permission_camera_denied_toast,
LENGTH_LONG).show();
supportFinishAfterTransition();
}
}
} }
if (gotPermission(ACCESS_COARSE_LOCATION, permissions, grantResults)) {
locationPermission = Permission.GRANTED;
} else if (shouldShowRationale(ACCESS_COARSE_LOCATION)) {
locationPermission = Permission.SHOW_RATIONALE;
} else {
locationPermission = Permission.PERMANENTLY_DENIED;
}
// If a permission dialog has been shown, showing the QR code fragment
// on this call path would cause a crash due to
// https://code.google.com/p/android/issues/detail?id=190966.
// In that case the isResumed flag prevents the fragment from being
// shown here, and showQrCodeFragmentIfAllowed() will be called again
// from onPostResume().
if (checkPermissions()) showQrCodeFragmentIfAllowed();
}
private boolean gotPermission(String permission, String[] permissions,
int[] grantResults) {
for (int i = 0; i < permissions.length; i++) {
if (permission.equals(permissions[i]))
return grantResults[i] == PERMISSION_GRANTED;
}
return false;
}
private boolean shouldShowRationale(String permission) {
return ActivityCompat.shouldShowRequestPermissionRationale(this,
permission);
} }
private class BluetoothStateReceiver extends BroadcastReceiver { private class BluetoothStateReceiver extends BroadcastReceiver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(EXTRA_STATE, 0); String action = intent.getAction();
if (state == STATE_ON) setBluetoothState(BluetoothState.ENABLED); if (ACTION_STATE_CHANGED.equals(action)) {
else setBluetoothState(BluetoothState.UNKNOWN); int state = intent.getIntExtra(EXTRA_STATE, 0);
if (state == STATE_ON)
setBluetoothState(BluetoothState.ENABLED);
else setBluetoothState(BluetoothState.UNKNOWN);
} else if (ACTION_SCAN_MODE_CHANGED.equals(action)) {
int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0);
if (scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE)
setBluetoothState(BluetoothState.DISCOVERABLE);
else if (scanMode == SCAN_MODE_CONNECTABLE)
setBluetoothState(BluetoothState.ENABLED);
else setBluetoothState(BluetoothState.UNKNOWN);
}
} }
} }
} }

View File

@@ -4,7 +4,7 @@ import android.support.annotation.LayoutRes;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import org.briarproject.bramble.api.identity.Author; 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.GroupId;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R; import org.briarproject.briar.R;
@@ -22,14 +22,14 @@ class GroupMessageItem extends ThreadItem {
private GroupMessageItem(MessageId messageId, GroupId groupId, private GroupMessageItem(MessageId messageId, GroupId groupId,
@Nullable MessageId parentId, String text, long timestamp, @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); super(messageId, parentId, text, timestamp, author, status, isRead);
this.groupId = groupId; this.groupId = groupId;
} }
GroupMessageItem(GroupMessageHeader h, String text) { GroupMessageItem(GroupMessageHeader h, String text) {
this(h.getId(), h.getGroupId(), h.getParentId(), text, h.getTimestamp(), 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() { 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.BaseThreadItemViewHolder;
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener; 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 @UiThread
@NotNullByDefault @NotNullByDefault
@@ -36,24 +37,27 @@ class JoinMessageItemViewHolder
if (item.isInitial()) { if (item.isInitial()) {
textView.setText(R.string.groups_member_created_you); textView.setText(R.string.groups_member_created_you);
} else { } else {
textView.setText( String name = getContactDisplayName(item.getAuthor(),
getContext().getString(R.string.groups_member_joined, item.getAuthorInfo().getAlias());
item.getAuthor().getName())); textView.setText(getContext()
.getString(R.string.groups_member_joined, name));
} }
} }
private void bind(JoinMessageItem item) { private void bind(JoinMessageItem item) {
Context ctx = getContext(); Context ctx = getContext();
String name = getContactDisplayName(item.getAuthor(),
item.getAuthorInfo().getAlias());
if (item.isInitial()) { if (item.isInitial()) {
textView.setText(ctx.getString(R.string.groups_member_created, textView.setText(
item.getAuthor().getName())); ctx.getString(R.string.groups_member_created, name));
} else { } else {
if (item.getStatus() == OURSELVES) { if (item.getAuthorInfo().getStatus() == OURSELVES) {
textView.setText(R.string.groups_member_joined_you); textView.setText(R.string.groups_member_joined_you);
} else { } else {
textView.setText(ctx.getString(R.string.groups_member_joined, textView.setText(
item.getAuthor().getName())); 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 javax.annotation.Nullable;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
class GroupInvitationViewHolder class GroupInvitationViewHolder
extends InvitationViewHolder<GroupInvitationItem> { extends InvitationViewHolder<GroupInvitationItem> {
@@ -24,7 +26,7 @@ class GroupInvitationViewHolder
sharedBy.setText( sharedBy.setText(
sharedBy.getContext().getString(R.string.groups_created_by, 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; package org.briarproject.briar.android.privategroup.list;
import org.briarproject.bramble.api.identity.Author; 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.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.api.client.MessageTracker.GroupCount; import org.briarproject.briar.api.client.MessageTracker.GroupCount;
@@ -12,12 +13,15 @@ import org.briarproject.briar.api.privategroup.PrivateGroup;
class GroupItem { class GroupItem {
private final PrivateGroup privateGroup; private final PrivateGroup privateGroup;
private final AuthorInfo authorInfo;
private int messageCount, unreadCount; private int messageCount, unreadCount;
private long timestamp; private long timestamp;
private boolean dissolved; private boolean dissolved;
GroupItem(PrivateGroup privateGroup, GroupCount count, boolean dissolved) { GroupItem(PrivateGroup privateGroup, AuthorInfo authorInfo,
GroupCount count, boolean dissolved) {
this.privateGroup = privateGroup; this.privateGroup = privateGroup;
this.authorInfo = authorInfo;
this.messageCount = count.getMsgCount(); this.messageCount = count.getMsgCount();
this.unreadCount = count.getUnreadCount(); this.unreadCount = count.getUnreadCount();
this.timestamp = count.getLatestMsgTime(); this.timestamp = count.getLatestMsgTime();
@@ -46,6 +50,10 @@ class GroupItem {
return privateGroup.getCreator(); return privateGroup.getCreator();
} }
AuthorInfo getCreatorInfo() {
return authorInfo;
}
String getName() { String getName() {
return privateGroup.getName(); return privateGroup.getName();
} }

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android.privategroup.memberlist; package org.briarproject.briar.android.privategroup.memberlist;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -8,6 +9,8 @@ import android.view.ViewGroup;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.util.BriarAdapter; import org.briarproject.briar.android.util.BriarAdapter;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
class MemberListAdapter extends class MemberListAdapter extends
BriarAdapter<MemberListItem, MemberListItemHolder> { BriarAdapter<MemberListItem, MemberListItemHolder> {
@@ -15,8 +18,9 @@ class MemberListAdapter extends
super(context, MemberListItem.class); super(context, MemberListItem.class);
} }
@NonNull
@Override @Override
public MemberListItemHolder onCreateViewHolder(ViewGroup viewGroup, public MemberListItemHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
int i) { int i) {
View v = LayoutInflater.from(ctx).inflate( View v = LayoutInflater.from(ctx).inflate(
R.layout.list_item_group_member, viewGroup, false); R.layout.list_item_group_member, viewGroup, false);
@@ -24,13 +28,18 @@ class MemberListAdapter extends
} }
@Override @Override
public void onBindViewHolder(MemberListItemHolder ui, int position) { public void onBindViewHolder(@NonNull MemberListItemHolder ui,
int position) {
ui.bind(items.get(position)); ui.bind(items.get(position));
} }
@Override @Override
public int compare(MemberListItem m1, MemberListItem m2) { 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 @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.contact.ContactId;
import org.briarproject.bramble.api.identity.Author; 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.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.privategroup.GroupMember; import org.briarproject.briar.api.privategroup.GroupMember;
@@ -25,8 +26,12 @@ class MemberListItem {
return groupMember.getAuthor(); return groupMember.getAuthor();
} }
AuthorInfo getAuthorInfo() {
return groupMember.getAuthorInfo();
}
Status getStatus() { Status getStatus() {
return groupMember.getStatus(); return groupMember.getAuthorInfo().getStatus();
} }
boolean isCreator() { boolean isCreator() {

View File

@@ -10,7 +10,10 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.view.AuthorView; 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 @UiThread
@NotNullByDefault @NotNullByDefault
@@ -29,33 +32,33 @@ class MemberListItemHolder extends RecyclerView.ViewHolder {
protected void bind(MemberListItem item) { protected void bind(MemberListItem item) {
// member name, avatar and status // member name, avatar and status
author.setAuthor(item.getMember()); author.setAuthor(item.getMember(), item.getAuthorInfo());
author.setAuthorStatus(item.getStatus());
// online status of visible contacts // online status of visible contacts
if (item.getContactId() != null) { if (item.getContactId() != null) {
bulb.setVisibility(View.VISIBLE); bulb.setVisibility(VISIBLE);
if (item.isOnline()) { if (item.isOnline()) {
bulb.setImageResource(R.drawable.contact_connected); bulb.setImageResource(R.drawable.contact_connected);
} else { } else {
bulb.setImageResource(R.drawable.contact_disconnected); bulb.setImageResource(R.drawable.contact_disconnected);
} }
} else { } else {
bulb.setVisibility(View.GONE); bulb.setVisibility(GONE);
} }
// text shown for creator // text shown for creator
if (item.isCreator()) { if (item.isCreator()) {
creator.setVisibility(View.VISIBLE); creator.setVisibility(VISIBLE);
if (item.getStatus() == OURSELVES) { if (item.getStatus() == OURSELVES) {
creator.setText(R.string.groups_member_created_you); creator.setText(R.string.groups_member_created_you);
} else { } else {
String name = getContactDisplayName(item.getMember(),
item.getAuthorInfo().getAlias());
creator.setText(creator.getContext() creator.setText(creator.getContext()
.getString(R.string.groups_member_created, .getString(R.string.groups_member_created, name));
item.getMember().getName()));
} }
} else { } 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.getVisibilityIcon;
import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityString; 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.GREY_OUT;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
@@ -36,7 +37,7 @@ class RevealableContactViewHolder
icon.setImageResource(getVisibilityIcon(item.getVisibility())); icon.setImageResource(getVisibilityIcon(item.getVisibility()));
info.setText( info.setText(
getVisibilityString(info.getContext(), item.getVisibility(), getVisibilityString(info.getContext(), item.getVisibility(),
item.getContact().getAuthor().getName())); getContactDisplayName(item.getContact())));
} }
@Override @Override

View File

@@ -44,6 +44,7 @@ import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.android.util.UiUtils; import org.briarproject.briar.android.util.UiUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -210,6 +211,13 @@ public class SettingsFragment extends PreferenceFragmentCompat
return true; return true;
}); });
if (SDK_INT < 27) {
// remove System Default Theme option
List<CharSequence> entries =
new ArrayList<>(Arrays.asList(theme.getEntries()));
entries.remove(getString(R.string.pref_theme_system));
theme.setEntries(entries.toArray(new CharSequence[0]));
}
if (IS_DEBUG_BUILD) { if (IS_DEBUG_BUILD) {
findPreference("pref_key_explode").setOnPreferenceClickListener( findPreference("pref_key_explode").setOnPreferenceClickListener(
preference -> { preference -> {

View File

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

View File

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

View File

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

View File

@@ -33,7 +33,9 @@ import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import org.acra.ACRA; import org.acra.ACRA;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; 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.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
@@ -74,6 +76,17 @@ public class UiUtils {
public static final int TEASER_LENGTH = 320; public static final int TEASER_LENGTH = 320;
public static final float GREY_OUT = 0.5f; 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, public static void setError(TextInputLayout til, @Nullable String error,
boolean set) { boolean set) {
if (set) { if (set) {
@@ -127,7 +140,9 @@ public class UiUtils {
return builder; 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); return Html.fromHtml(s);
} }

View File

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

View File

@@ -20,17 +20,12 @@ import android.widget.TextView;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION; import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION;
public class BriarRecyclerView extends FrameLayout { public class BriarRecyclerView extends FrameLayout {
private static final Logger LOG =
Logger.getLogger(BriarRecyclerView.class.getName());
private final Handler handler = new Handler(Looper.getMainLooper()); private final Handler handler = new Handler(Looper.getMainLooper());
private RecyclerView recyclerView; private RecyclerView recyclerView;
@@ -39,6 +34,7 @@ public class BriarRecyclerView extends FrameLayout {
private TextView emptyText, emptyAction; private TextView emptyText, emptyAction;
private ProgressBar progressBar; private ProgressBar progressBar;
private RecyclerView.AdapterDataObserver emptyObserver; private RecyclerView.AdapterDataObserver emptyObserver;
@Nullable
private Runnable refresher = null; private Runnable refresher = null;
private boolean isScrollingToEnd = false; private boolean isScrollingToEnd = false;
@@ -217,18 +213,15 @@ public class BriarRecyclerView extends FrameLayout {
throw new IllegalStateException("Need to call setAdapter() first!"); throw new IllegalStateException("Need to call setAdapter() first!");
} }
refresher = () -> { refresher = () -> {
LOG.info("Updating Content...");
Adapter adapter = recyclerView.getAdapter(); Adapter adapter = recyclerView.getAdapter();
adapter.notifyItemRangeChanged(0, adapter.getItemCount()); adapter.notifyItemRangeChanged(0, adapter.getItemCount());
handler.postDelayed(refresher, MIN_DATE_RESOLUTION); handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
}; };
LOG.info("Adding Handler Callback");
handler.postDelayed(refresher, MIN_DATE_RESOLUTION); handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
} }
public void stopPeriodicUpdate() { public void stopPeriodicUpdate() {
if (refresher != null) { if (refresher != null) {
LOG.info("Removing Handler Callback");
handler.removeCallbacks(refresher); handler.removeCallbacks(refresher);
refresher = null; refresher = null;
} }

View File

@@ -3,14 +3,14 @@ package org.briarproject.briar.android.view;
import android.content.Context; import android.content.Context;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet; 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; import org.briarproject.briar.R;
@UiThread @UiThread
public class TrustIndicatorView extends ImageView { public class TrustIndicatorView extends AppCompatImageView {
public TrustIndicatorView(Context context) { public TrustIndicatorView(Context context) {
super(context); super(context);

View File

@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<!-- Workaround for toolbar icons failing to apply proper layout direction -->
<group
android:pivotX="12"
android:pivotY="12"
android:rotation="180">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</group>
</vector>

View File

@@ -1,7 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:autoMirrored="true"
android:viewportHeight="24.0" android:viewportHeight="24.0"
android:viewportWidth="24.0"> android:viewportWidth="24.0">
<path <path

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"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" 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" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:orientation="vertical"> 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 <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_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 <Button
android:id="@+id/cancelButton" android:id="@+id/cancelButton"
style="@style/BriarButtonFlat.Positive" style="@style/BriarButtonFlat.Positive"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/cancel" android:text="@string/cancel"/>
app:layout_constraintEnd_toStartOf="@+id/openButton"
app:layout_constraintStart_toStartOf="parent"/>
<Button <Button
android:id="@+id/openButton" android:id="@+id/openButton"
style="@style/BriarButtonFlat.Negative" style="@style/BriarButtonFlat.Negative"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/link_warning_open_link" android:text="@string/link_warning_open_link"/>
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/cancelButton"/>
</android.support.constraint.ConstraintLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

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

View File

@@ -63,10 +63,10 @@
<item>@string/pref_theme_auto</item> <item>@string/pref_theme_auto</item>
<item>@string/pref_theme_system</item> <item>@string/pref_theme_system</item>
</string-array> </string-array>
<string name="pref_theme_light_value">light</string> <string name="pref_theme_light_value" translatable="false">light</string>
<string name="pref_theme_dark_value">dark</string> <string name="pref_theme_dark_value" translatable="false">dark</string>
<string name="pref_theme_auto_value">auto</string> <string name="pref_theme_auto_value" translatable="false">auto</string>
<string name="pref_theme_system_value">system</string> <string name="pref_theme_system_value" translatable="false">system</string>
<string-array name="pref_theme_values"> <string-array name="pref_theme_values">
<item>@string/pref_theme_light_value</item> <item>@string/pref_theme_light_value</item>
<item>@string/pref_theme_dark_value</item> <item>@string/pref_theme_dark_value</item>
@@ -82,8 +82,8 @@
<item>@string/pref_lock_timeout_30</item> <item>@string/pref_lock_timeout_30</item>
<item>@string/pref_lock_timeout_60</item> <item>@string/pref_lock_timeout_60</item>
</string-array> </string-array>
<string name="pref_lock_timeout_value_default">5</string> <string name="pref_lock_timeout_value_default" translatable="false">5</string>
<string name="pref_lock_timeout_value_never">-1</string> <string name="pref_lock_timeout_value_never" translatable="false">-1</string>
<string-array name="pref_key_lock_timeout_values"> <string-array name="pref_key_lock_timeout_values">
<item>@string/pref_lock_timeout_value_never</item> <item>@string/pref_lock_timeout_value_never</item>
<item>1</item> <item>1</item>

View File

@@ -127,6 +127,9 @@
<string name="date_no_private_messages">No messages.</string> <string name="date_no_private_messages">No messages.</string>
<string name="no_private_messages">No messages to show</string> <string name="no_private_messages">No messages to show</string>
<string name="message_hint">Type message</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="delete_contact">Delete contact</string>
<string name="dialog_title_delete_contact">Confirm Contact Deletion</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> <string name="dialog_message_delete_contact">Are you sure that you want to remove this contact and all messages exchanged with this contact?</string>
@@ -468,8 +471,11 @@
<!-- Permission Requests --> <!-- Permission Requests -->
<string name="permission_camera_title">Camera permission</string> <string name="permission_camera_title">Camera permission</string>
<string name="permission_camera_request_body">To scan the QR code, Briar needs access to the camera.</string> <string name="permission_camera_request_body">To scan the QR code, Briar needs access to the camera.</string>
<string name="permission_location_title">Location permission</string>
<string name="permission_location_request_body">To discover Bluetooth devices, Briar needs permission to access your location.\n\nBriar does not store your location or share it with anyone.</string>
<string name="permission_camera_location_title">Camera and location</string>
<string name="permission_camera_location_request_body">To scan the QR code, Briar needs access to the camera.\n\nTo discover Bluetooth devices, Briar needs permission to access your location.\n\nBriar does not store your location or share it with anyone.</string>
<string name="permission_camera_denied_body">You have denied access to the camera, but adding contacts requires using the camera.\n\nPlease consider granting access.</string> <string name="permission_camera_denied_body">You have denied access to the camera, but adding contacts requires using the camera.\n\nPlease consider granting access.</string>
<string name="permission_camera_denied_toast">Camera permission was not granted</string>
<string name="qr_code">QR code</string> <string name="qr_code">QR code</string>
<string name="show_qr_code_fullscreen">Show QR code fullscreen</string> <string name="show_qr_code_fullscreen">Show QR code fullscreen</string>

View File

@@ -6,6 +6,7 @@ import junit.framework.Assert;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.android.TestBriarApplication; import org.briarproject.briar.android.TestBriarApplication;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler; 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.assertEquals;
import static junit.framework.Assert.assertTrue; 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.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.bramble.util.StringUtils.getRandomString;
@@ -87,7 +88,8 @@ public class ForumActivityTest {
Author author = getAuthor(); Author author = getAuthor();
String text = getRandomString(MAX_FORUM_POST_TEXT_LENGTH); String text = getRandomString(MAX_FORUM_POST_TEXT_LENGTH);
forumItems[i] = new ForumItem(MESSAGE_IDS[i], PARENT_IDS[i], 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]); forumItems[i].setLevel(LEVELS[i]);
} }
ThreadItemList<ForumItem> list = new ThreadItemListImpl<>(); 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: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.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: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-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:livedata:1.1.1:livedata-1.1.1.aar:50ab0490c1ff1a7cfb4e554032998b080888946d0dd424f39900efc4a1bcd750',
'android.arch.lifecycle:runtime:1.1.1:runtime-1.1.1.aar:c4e4be66c1b2f0abec593571454e1de14013f7e0f96bf2a9f212931a48cae550', '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; package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.identity.Author; 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.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
@@ -23,10 +23,10 @@ public class BlogCommentHeader extends BlogPostHeader {
public BlogCommentHeader(MessageType type, GroupId groupId, public BlogCommentHeader(MessageType type, GroupId groupId,
@Nullable String comment, BlogPostHeader parent, MessageId id, @Nullable String comment, BlogPostHeader parent, MessageId id,
long timestamp, long timeReceived, Author author, long timestamp, long timeReceived, Author author,
Status authorStatus, boolean read) { AuthorInfo authorInfo, boolean read) {
super(type, groupId, id, parent.getId(), timestamp, super(type, groupId, id, parent.getId(), timestamp,
timeReceived, author, authorStatus, false, read); timeReceived, author, authorInfo, false, read);
if (type != COMMENT && type != WRAPPED_COMMENT) if (type != COMMENT && type != WRAPPED_COMMENT)
throw new IllegalArgumentException("Incompatible Message Type"); throw new IllegalArgumentException("Incompatible Message Type");

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.api.introduction; package org.briarproject.briar.api.introduction;
import org.briarproject.bramble.api.identity.Author; 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.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId; 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 { public class IntroductionResponse extends PrivateResponse {
private final Author introducedAuthor; private final Author introducedAuthor;
private final AuthorInfo introducedAuthorInfo;
private final Role ourRole; private final Role ourRole;
public IntroductionResponse(MessageId messageId, GroupId groupId, long time, public IntroductionResponse(MessageId messageId, GroupId groupId, long time,
boolean local, boolean sent, boolean seen, boolean read, 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, super(messageId, groupId, time, local, sent, seen, read, sessionId,
accepted); accepted);
this.introducedAuthor = author; this.introducedAuthor = author;
this.introducedAuthorInfo = introducedAuthorInfo;
this.ourRole = role; this.ourRole = role;
} }
@@ -32,6 +36,10 @@ public class IntroductionResponse extends PrivateResponse {
return introducedAuthor; return introducedAuthor;
} }
public AuthorInfo getIntroducedAuthorInfo() {
return introducedAuthorInfo;
}
public boolean isIntroducer() { public boolean isIntroducer() {
return ourRole == INTRODUCER; 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.contact.ContactId;
import org.briarproject.bramble.api.identity.Author; 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.nullsafety.NotNullByDefault;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -13,16 +13,16 @@ import javax.annotation.concurrent.Immutable;
public class GroupMember { public class GroupMember {
private final Author author; private final Author author;
private final Status status; private final AuthorInfo authorInfo;
private final boolean isCreator; private final boolean isCreator;
@Nullable @Nullable
private final ContactId contactId; private final ContactId contactId;
private final Visibility visibility; 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) { @Nullable ContactId contactId, Visibility visibility) {
this.author = author; this.author = author;
this.status = status; this.authorInfo = status;
this.isCreator = isCreator; this.isCreator = isCreator;
this.contactId = contactId; this.contactId = contactId;
this.visibility = visibility; this.visibility = visibility;
@@ -32,8 +32,8 @@ public class GroupMember {
return author; return author;
} }
public Status getStatus() { public AuthorInfo getAuthorInfo() {
return status; return authorInfo;
} }
public boolean isCreator() { public boolean isCreator() {

View File

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

View File

@@ -14,7 +14,7 @@ public class JoinMessageHeader extends GroupMessageHeader {
public JoinMessageHeader(GroupMessageHeader h, Visibility visibility, public JoinMessageHeader(GroupMessageHeader h, Visibility visibility,
boolean isInitial) { boolean isInitial) {
super(h.getGroupId(), h.getId(), h.getParentId(), h.getTimestamp(), 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.visibility = visibility;
this.isInitial = isInitial; 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.FormatException;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.Contact; 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.contact.ContactManager.ContactHook;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry; 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.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; 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.AuthorId;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -49,6 +50,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; 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_AUTHOR;
import static org.briarproject.briar.api.blog.BlogConstants.KEY_COMMENT; import static org.briarproject.briar.api.blog.BlogConstants.KEY_COMMENT;
import static org.briarproject.briar.api.blog.BlogConstants.KEY_ORIGINAL_MSG_ID; 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, class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
ContactHook, Client { ContactHook, Client {
private final ContactManager contactManager;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final BlogFactory blogFactory; private final BlogFactory blogFactory;
private final BlogPostFactory blogPostFactory; private final BlogPostFactory blogPostFactory;
private final List<RemoveBlogHook> removeHooks; private final List<RemoveBlogHook> removeHooks;
@Inject @Inject
BlogManagerImpl(DatabaseComponent db, IdentityManager identityManager, BlogManagerImpl(DatabaseComponent db, ContactManager contactManager,
ClientHelper clientHelper, MetadataParser metadataParser, IdentityManager identityManager, ClientHelper clientHelper,
BlogFactory blogFactory, BlogPostFactory blogPostFactory) { MetadataParser metadataParser, BlogFactory blogFactory,
BlogPostFactory blogPostFactory) {
super(db, clientHelper, metadataParser); super(db, clientHelper, metadataParser);
this.contactManager = contactManager;
this.identityManager = identityManager; this.identityManager = identityManager;
this.blogFactory = blogFactory; this.blogFactory = blogFactory;
this.blogPostFactory = blogPostFactory; this.blogPostFactory = blogPostFactory;
@@ -501,25 +505,24 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
new HashMap<>(metadata1.size() + metadata2.size()); new HashMap<>(metadata1.size() + metadata2.size());
metadata.putAll(metadata1); metadata.putAll(metadata1);
metadata.putAll(metadata2); 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<>(); Set<AuthorId> authors = new HashSet<>();
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) { for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
BdfList authorList = entry.getValue().getList(KEY_AUTHOR); BdfList authorList = entry.getValue().getList(KEY_AUTHOR);
Author a = clientHelper.parseAndValidateAuthor(authorList); Author a = clientHelper.parseAndValidateAuthor(authorList);
authors.add(a.getId()); authors.add(a.getId());
} }
// get statuses for all authors // get information for all authors
Map<AuthorId, Status> authorStatuses = new HashMap<>(); Map<AuthorId, AuthorInfo> authorInfos = new HashMap<>();
for (AuthorId authorId : authors) { for (AuthorId authorId : authors) {
authorStatuses.put(authorId, authorInfos.put(authorId,
identityManager.getAuthorStatus(txn, authorId)); contactManager.getAuthorInfo(txn, authorId));
} }
// get post headers // get post headers
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) { for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
BdfDictionary meta = entry.getValue(); BdfDictionary meta = entry.getValue();
BlogPostHeader h = BlogPostHeader h = getPostHeaderFromMetadata(txn, g,
getPostHeaderFromMetadata(txn, g, entry.getKey(), meta, entry.getKey(), meta, authorInfos);
authorStatuses);
headers.add(h); headers.add(h);
} }
db.commitTransaction(txn); db.commitTransaction(txn);
@@ -563,7 +566,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
private BlogPostHeader getPostHeaderFromMetadata(Transaction txn, private BlogPostHeader getPostHeaderFromMetadata(Transaction txn,
GroupId groupId, MessageId id, BdfDictionary meta, GroupId groupId, MessageId id, BdfDictionary meta,
Map<AuthorId, Status> authorStatuses) Map<AuthorId, AuthorInfo> authorInfos)
throws DbException, FormatException { throws DbException, FormatException {
MessageType type = getMessageType(meta); MessageType type = getMessageType(meta);
@@ -574,13 +577,13 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
BdfList authorList = meta.getList(KEY_AUTHOR); BdfList authorList = meta.getList(KEY_AUTHOR);
Author author = clientHelper.parseAndValidateAuthor(authorList); Author author = clientHelper.parseAndValidateAuthor(authorList);
boolean isFeedPost = meta.getBoolean(KEY_RSS_FEED, false); boolean isFeedPost = meta.getBoolean(KEY_RSS_FEED, false);
Status authorStatus; AuthorInfo authorInfo;
if (isFeedPost) { if (isFeedPost) {
authorStatus = Status.NONE; authorInfo = new AuthorInfo(NONE);
} else if (authorStatuses.containsKey(author.getId())) { } else if (authorInfos.containsKey(author.getId())) {
authorStatus = authorStatuses.get(author.getId()); authorInfo = authorInfos.get(author.getId());
} else { } else {
authorStatus = identityManager.getAuthorStatus(txn, author.getId()); authorInfo = contactManager.getAuthorInfo(txn, author.getId());
} }
boolean read = meta.getBoolean(KEY_READ, false); boolean read = meta.getBoolean(KEY_READ, false);
@@ -591,10 +594,10 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
BlogPostHeader parent = BlogPostHeader parent =
getPostHeaderFromMetadata(txn, groupId, parentId); getPostHeaderFromMetadata(txn, groupId, parentId);
return new BlogCommentHeader(type, groupId, comment, parent, id, return new BlogCommentHeader(type, groupId, comment, parent, id,
timestamp, timeReceived, author, authorStatus, read); timestamp, timeReceived, author, authorInfo, read);
} else { } else {
return new BlogPostHeader(type, groupId, id, timestamp, 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.FormatException;
import org.briarproject.bramble.api.client.ClientHelper; 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.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataParser; 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.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; 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.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.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
@@ -45,7 +45,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; 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_AUTHOR;
import static org.briarproject.briar.api.forum.ForumConstants.KEY_LOCAL; import static org.briarproject.briar.api.forum.ForumConstants.KEY_LOCAL;
import static org.briarproject.briar.api.forum.ForumConstants.KEY_PARENT; 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 @NotNullByDefault
class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager { class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
private final IdentityManager identityManager; private final ContactManager contactManager;
private final ForumFactory forumFactory; private final ForumFactory forumFactory;
private final ForumPostFactory forumPostFactory; private final ForumPostFactory forumPostFactory;
private final MessageTracker messageTracker; private final MessageTracker messageTracker;
private final List<RemoveForumHook> removeHooks; private final List<RemoveForumHook> removeHooks;
@Inject @Inject
ForumManagerImpl(DatabaseComponent db, IdentityManager identityManager, ForumManagerImpl(DatabaseComponent db, ContactManager contactManager,
ClientHelper clientHelper, MetadataParser metadataParser, ClientHelper clientHelper, MetadataParser metadataParser,
ForumFactory forumFactory, ForumPostFactory forumPostFactory, ForumFactory forumFactory, ForumPostFactory forumPostFactory,
MessageTracker messageTracker) { MessageTracker messageTracker) {
super(db, clientHelper, metadataParser); super(db, clientHelper, metadataParser);
this.identityManager = identityManager; this.contactManager = contactManager;
this.forumFactory = forumFactory; this.forumFactory = forumFactory;
this.forumPostFactory = forumPostFactory; this.forumPostFactory = forumPostFactory;
this.messageTracker = messageTracker; this.messageTracker = messageTracker;
@@ -142,8 +142,9 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
throw new AssertionError(e); throw new AssertionError(e);
} }
}); });
AuthorInfo authorInfo = new AuthorInfo(OURSELVES);
return new ForumPostHeader(p.getMessage().getId(), p.getParent(), return new ForumPostHeader(p.getMessage().getId(), p.getParent(),
p.getMessage().getTimestamp(), p.getAuthor(), OURSELVES, true); p.getMessage().getTimestamp(), p.getAuthor(), authorInfo, true);
} }
@Override @Override
@@ -196,7 +197,7 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
Collection<ForumPostHeader> headers = new ArrayList<>(); Collection<ForumPostHeader> headers = new ArrayList<>();
Map<MessageId, BdfDictionary> metadata = Map<MessageId, BdfDictionary> metadata =
clientHelper.getMessageMetadataAsDictionary(txn, g); 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<>(); Set<AuthorId> authors = new HashSet<>();
for (Entry<MessageId, BdfDictionary> entry : for (Entry<MessageId, BdfDictionary> entry :
metadata.entrySet()) { metadata.entrySet()) {
@@ -204,17 +205,17 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
Author a = clientHelper.parseAndValidateAuthor(authorList); Author a = clientHelper.parseAndValidateAuthor(authorList);
authors.add(a.getId()); authors.add(a.getId());
} }
// get statuses for all authors // get information for all authors
Map<AuthorId, Status> statuses = new HashMap<>(); Map<AuthorId, AuthorInfo> authorInfos = new HashMap<>();
for (AuthorId id : authors) { for (AuthorId id : authors) {
statuses.put(id, identityManager.getAuthorStatus(txn, id)); authorInfos.put(id, contactManager.getAuthorInfo(txn, id));
} }
// Parse the metadata // Parse the metadata
for (Entry<MessageId, BdfDictionary> entry : for (Entry<MessageId, BdfDictionary> entry :
metadata.entrySet()) { metadata.entrySet()) {
BdfDictionary meta = entry.getValue(); BdfDictionary meta = entry.getValue();
headers.add(getForumPostHeader(txn, entry.getKey(), meta, headers.add(getForumPostHeader(txn, entry.getKey(), meta,
statuses)); authorInfos));
} }
return headers; return headers;
}); });
@@ -252,7 +253,7 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
} }
private ForumPostHeader getForumPostHeader(Transaction txn, MessageId id, private ForumPostHeader getForumPostHeader(Transaction txn, MessageId id,
BdfDictionary meta, Map<AuthorId, Status> statuses) BdfDictionary meta, Map<AuthorId, AuthorInfo> authorInfos)
throws DbException, FormatException { throws DbException, FormatException {
long timestamp = meta.getLong(KEY_TIMESTAMP); long timestamp = meta.getLong(KEY_TIMESTAMP);
@@ -261,12 +262,12 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
parentId = new MessageId(meta.getRaw(KEY_PARENT)); parentId = new MessageId(meta.getRaw(KEY_PARENT));
BdfList authorList = meta.getList(KEY_AUTHOR); BdfList authorList = meta.getList(KEY_AUTHOR);
Author author = clientHelper.parseAndValidateAuthor(authorList); Author author = clientHelper.parseAndValidateAuthor(authorList);
Status status = statuses.get(author.getId()); AuthorInfo authorInfo = authorInfos.get(author.getId());
if (status == null) if (authorInfo == null)
status = identityManager.getAuthorStatus(txn, author.getId()); authorInfo = contactManager.getAuthorInfo(txn, author.getId());
boolean read = meta.getBoolean(MSG_KEY_READ); boolean read = meta.getBoolean(MSG_KEY_READ);
return new ForumPostHeader(id, parentId, timestamp, author, status, return new ForumPostHeader(id, parentId, timestamp, author, authorInfo,
read); 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.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; 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.IdentityManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
@@ -149,11 +150,13 @@ abstract class AbstractProtocolEngine<S extends Session>
throws DbException { throws DbException {
AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId(); AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId();
Contact c = contactManager.getContact(txn, sender, localAuthorId); Contact c = contactManager.getContact(txn, sender, localAuthorId);
AuthorInfo otherAuthorInfo =
contactManager.getAuthorInfo(txn, otherAuthor.getId());
IntroductionResponse response = IntroductionResponse response =
new IntroductionResponse(m.getMessageId(), m.getGroupId(), new IntroductionResponse(m.getMessageId(), m.getGroupId(),
m.getTimestamp(), false, false, false, false, m.getTimestamp(), false, false, false, false,
s.getSessionId(), m instanceof AcceptMessage, s.getSessionId(), m instanceof AcceptMessage,
otherAuthor, s.getRole()); otherAuthor, otherAuthorInfo, s.getRole());
IntroductionResponseReceivedEvent e = IntroductionResponseReceivedEvent e =
new IntroductionResponseReceivedEvent(response, c.getId()); new IntroductionResponseReceivedEvent(response, c.getId());
txn.attach(e); 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.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; 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.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -251,12 +252,12 @@ class IntroduceeProtocolEngine
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn); LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
Contact c = contactManager.getContact(txn, s.getIntroducer().getId(), Contact c = contactManager.getContact(txn, s.getIntroducer().getId(),
localAuthor.getId()); localAuthor.getId());
boolean contactExists = contactManager AuthorInfo authorInfo =
.contactExists(txn, m.getAuthor().getId(), localAuthor.getId()); contactManager.getAuthorInfo(txn, m.getAuthor().getId());
IntroductionRequest request = new IntroductionRequest(m.getMessageId(), IntroductionRequest request = new IntroductionRequest(m.getMessageId(),
m.getGroupId(), m.getTimestamp(), false, false, false, false, m.getGroupId(), m.getTimestamp(), false, false, false, false,
s.getSessionId(), m.getAuthor(), m.getText(), false, s.getSessionId(), m.getAuthor(), m.getText(), false,
contactExists); authorInfo);
IntroductionRequestReceivedEvent e = IntroductionRequestReceivedEvent e =
new IntroductionRequestReceivedEvent(request, c.getId()); new IntroductionRequestReceivedEvent(request, c.getId());
txn.attach(e); 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.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; 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.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; 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.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -408,6 +411,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
.getMessageMetadataAsDictionary(txn, contactGroupId, query); .getMessageMetadataAsDictionary(txn, contactGroupId, query);
List<PrivateMessageHeader> messages = List<PrivateMessageHeader> messages =
new ArrayList<>(results.size()); new ArrayList<>(results.size());
Map<AuthorId, AuthorInfo> authorInfos = new HashMap<>();
for (Entry<MessageId, BdfDictionary> e : results.entrySet()) { for (Entry<MessageId, BdfDictionary> e : results.entrySet()) {
MessageId m = e.getKey(); MessageId m = e.getKey();
MessageMetadata meta = MessageMetadata meta =
@@ -419,15 +423,17 @@ class IntroductionManagerImpl extends ConversationClientImpl
if (type == REQUEST) { if (type == REQUEST) {
messages.add( messages.add(
parseInvitationRequest(txn, contactGroupId, m, parseInvitationRequest(txn, contactGroupId, m,
meta, status, ss.bdfSession)); meta, status, ss.bdfSession, authorInfos));
} else if (type == ACCEPT) { } else if (type == ACCEPT) {
messages.add( messages.add(
parseInvitationResponse(contactGroupId, m, meta, parseInvitationResponse(txn, contactGroupId, m,
status, ss.bdfSession, true)); meta, status, ss.bdfSession, authorInfos,
true));
} else if (type == DECLINE) { } else if (type == DECLINE) {
messages.add( messages.add(
parseInvitationResponse(contactGroupId, m, meta, parseInvitationResponse(txn, contactGroupId, m,
status, ss.bdfSession, false)); meta, status, ss.bdfSession, authorInfos,
false));
} }
} }
return messages; return messages;
@@ -438,7 +444,8 @@ class IntroductionManagerImpl extends ConversationClientImpl
private IntroductionRequest parseInvitationRequest(Transaction txn, private IntroductionRequest parseInvitationRequest(Transaction txn,
GroupId contactGroupId, MessageId m, MessageMetadata meta, GroupId contactGroupId, MessageId m, MessageMetadata meta,
MessageStatus status, BdfDictionary bdfSession) MessageStatus status, BdfDictionary bdfSession,
Map<AuthorId, AuthorInfo> authorInfos)
throws DbException, FormatException { throws DbException, FormatException {
Role role = sessionParser.getRole(bdfSession); Role role = sessionParser.getRole(bdfSession);
SessionId sessionId; SessionId sessionId;
@@ -462,19 +469,22 @@ class IntroductionManagerImpl extends ConversationClientImpl
BdfList body = clientHelper.toList(msg); BdfList body = clientHelper.toList(msg);
RequestMessage rm = messageParser.parseRequestMessage(msg, body); RequestMessage rm = messageParser.parseRequestMessage(msg, body);
String text = rm.getText(); String text = rm.getText();
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn); AuthorInfo authorInfo = authorInfos.get(author.getId());
boolean contactExists = contactManager if (authorInfo == null) {
.contactExists(txn, rm.getAuthor().getId(), authorInfo = contactManager.getAuthorInfo(txn, author.getId());
localAuthor.getId()); authorInfos.put(author.getId(), authorInfo);
}
return new IntroductionRequest(m, contactGroupId, meta.getTimestamp(), return new IntroductionRequest(m, contactGroupId, meta.getTimestamp(),
meta.isLocal(), status.isSent(), status.isSeen(), meta.isRead(), meta.isLocal(), status.isSent(), status.isSeen(), meta.isRead(),
sessionId, author, text, !meta.isAvailableToAnswer(), sessionId, author, text, !meta.isAvailableToAnswer(),
contactExists); authorInfo);
} }
private IntroductionResponse parseInvitationResponse(GroupId contactGroupId, private IntroductionResponse parseInvitationResponse(Transaction txn,
MessageId m, MessageMetadata meta, MessageStatus status, GroupId contactGroupId, MessageId m, MessageMetadata meta,
BdfDictionary bdfSession, boolean accept) throws FormatException { MessageStatus status, BdfDictionary bdfSession,
Map<AuthorId, AuthorInfo> authorInfos, boolean accept)
throws FormatException, DbException {
Role role = sessionParser.getRole(bdfSession); Role role = sessionParser.getRole(bdfSession);
SessionId sessionId; SessionId sessionId;
Author author; Author author;
@@ -493,9 +503,14 @@ class IntroductionManagerImpl extends ConversationClientImpl
sessionId = session.getSessionId(); sessionId = session.getSessionId();
author = session.getRemote().author; author = session.getRemote().author;
} else throw new AssertionError(); } 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(), return new IntroductionResponse(m, contactGroupId, meta.getTimestamp(),
meta.isLocal(), status.isSent(), status.isSeen(), meta.isRead(), meta.isLocal(), status.isSent(), status.isSeen(), meta.isRead(),
sessionId, accept, author, role); sessionId, accept, author, authorInfo, role);
} }
private void removeSessionWithIntroducer(Transaction txn, 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.db.Transaction;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.identity.Author; 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.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.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -53,9 +54,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; 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.bramble.api.identity.Author.Status.UNVERIFIED; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
import static org.briarproject.bramble.api.identity.Author.Status.VERIFIED; 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.JOIN;
import static org.briarproject.briar.api.privategroup.MessageType.POST; import static org.briarproject.briar.api.privategroup.MessageType.POST;
import static org.briarproject.briar.api.privategroup.Visibility.INVISIBLE; import static org.briarproject.briar.api.privategroup.Visibility.INVISIBLE;
@@ -231,9 +232,10 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
} finally { } finally {
db.endTransaction(txn); db.endTransaction(txn);
} }
AuthorInfo authorInfo = new AuthorInfo(OURSELVES);
return new GroupMessageHeader(m.getMessage().getGroupId(), return new GroupMessageHeader(m.getMessage().getGroupId(),
m.getMessage().getId(), m.getParent(), 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) { private void addMessageMetadata(BdfDictionary meta, GroupMessage m) {
@@ -321,15 +323,15 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
try { try {
Map<MessageId, BdfDictionary> metadata = Map<MessageId, BdfDictionary> metadata =
clientHelper.getMessageMetadataAsDictionary(txn, g); 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<>(); Set<AuthorId> authors = new HashSet<>();
for (BdfDictionary meta : metadata.values()) { for (BdfDictionary meta : metadata.values()) {
authors.add(getAuthor(meta).getId()); authors.add(getAuthor(meta).getId());
} }
// get statuses for all authors // get information for all authors
Map<AuthorId, Status> statuses = new HashMap<>(); Map<AuthorId, AuthorInfo> authorInfos = new HashMap<>();
for (AuthorId id : authors) { for (AuthorId id : authors) {
statuses.put(id, identityManager.getAuthorStatus(txn, id)); authorInfos.put(id, contactManager.getAuthorInfo(txn, id));
} }
// get current visibilities for join messages // get current visibilities for join messages
Map<Author, Visibility> visibilities = getMembers(txn, g); Map<Author, Visibility> visibilities = getMembers(txn, g);
@@ -340,10 +342,10 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
Author member = getAuthor(meta); Author member = getAuthor(meta);
Visibility v = visibilities.get(member); Visibility v = visibilities.get(member);
headers.add(getJoinMessageHeader(txn, g, entry.getKey(), headers.add(getJoinMessageHeader(txn, g, entry.getKey(),
meta, statuses, v)); meta, authorInfos, v));
} else { } else {
headers.add(getGroupMessageHeader(txn, g, entry.getKey(), headers.add(getGroupMessageHeader(txn, g, entry.getKey(),
meta, statuses)); meta, authorInfos));
} }
} }
db.commitTransaction(txn); db.commitTransaction(txn);
@@ -356,7 +358,8 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
} }
private GroupMessageHeader getGroupMessageHeader(Transaction txn, GroupId g, 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 { throws DbException, FormatException {
MessageId parentId = null; MessageId parentId = null;
@@ -366,24 +369,25 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
long timestamp = meta.getLong(KEY_TIMESTAMP); long timestamp = meta.getLong(KEY_TIMESTAMP);
Author member = getAuthor(meta); Author member = getAuthor(meta);
Status status; AuthorInfo authorInfo;
if (statuses.containsKey(member.getId())) { if (authorInfos.containsKey(member.getId())) {
status = statuses.get(member.getId()); authorInfo = authorInfos.get(member.getId());
} else { } else {
status = identityManager.getAuthorStatus(txn, member.getId()); authorInfo = contactManager.getAuthorInfo(txn, member.getId());
} }
boolean read = meta.getBoolean(KEY_READ); boolean read = meta.getBoolean(KEY_READ);
return new GroupMessageHeader(g, id, parentId, timestamp, member, return new GroupMessageHeader(g, id, parentId, timestamp, member,
status, read); authorInfo, read);
} }
private JoinMessageHeader getJoinMessageHeader(Transaction txn, GroupId g, private JoinMessageHeader getJoinMessageHeader(Transaction txn, GroupId g,
MessageId id, BdfDictionary meta, Map<AuthorId, Status> statuses, MessageId id, BdfDictionary meta,
Visibility v) throws DbException, FormatException { Map<AuthorId, AuthorInfo> authorInfos, Visibility v)
throws DbException, FormatException {
GroupMessageHeader header = GroupMessageHeader header =
getGroupMessageHeader(txn, g, id, meta, statuses); getGroupMessageHeader(txn, g, id, meta, authorInfos);
boolean creator = meta.getBoolean(KEY_INITIAL_JOIN_MSG); boolean creator = meta.getBoolean(KEY_INITIAL_JOIN_MSG);
return new JoinMessageHeader(header, v, creator); return new JoinMessageHeader(header, v, creator);
} }
@@ -398,7 +402,8 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
PrivateGroup privateGroup = getPrivateGroup(txn, g); PrivateGroup privateGroup = getPrivateGroup(txn, g);
for (Entry<Author, Visibility> m : authors.entrySet()) { for (Entry<Author, Visibility> m : authors.entrySet()) {
Author a = m.getKey(); 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(); Visibility v = m.getValue();
ContactId c = null; ContactId c = null;
if (v != INVISIBLE && if (v != INVISIBLE &&
@@ -407,7 +412,7 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
.getId(); .getId();
} }
boolean isCreator = privateGroup.getCreator().equals(a); 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); db.commitTransaction(txn);
return members; 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.client.ClientHelper;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; 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.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry; import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList; 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.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; 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.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
@@ -29,9 +31,9 @@ import org.jmock.Expectations;
import org.jmock.Mockery; import org.jmock.Mockery;
import org.junit.Test; import org.junit.Test;
import static org.briarproject.bramble.api.identity.Author.Status.NONE; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.NONE;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.Author.Status.VERIFIED; 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.getGroup;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage; 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 Mockery context = new Mockery();
private final BlogManagerImpl blogManager; private final BlogManagerImpl blogManager;
private final DatabaseComponent db = context.mock(DatabaseComponent.class); private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final ContactManager contactManager =
context.mock(ContactManager.class);
private final IdentityManager identityManager = private final IdentityManager identityManager =
context.mock(IdentityManager.class); context.mock(IdentityManager.class);
private final ClientHelper clientHelper = context.mock(ClientHelper.class); private final ClientHelper clientHelper = context.mock(ClientHelper.class);
@@ -72,6 +76,8 @@ public class BlogManagerImplTest extends BriarTestCase {
context.mock(BlogPostFactory.class); context.mock(BlogPostFactory.class);
private final LocalAuthor localAuthor1, localAuthor2, rssLocalAuthor; 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 BdfList authorList1, authorList2, rssAuthorList;
private final Blog blog1, blog2, rssBlog; private final Blog blog1, blog2, rssBlog;
private final Message message, rssMessage; private final Message message, rssMessage;
@@ -81,7 +87,7 @@ public class BlogManagerImplTest extends BriarTestCase {
public BlogManagerImplTest() { public BlogManagerImplTest() {
MetadataParser metadataParser = context.mock(MetadataParser.class); MetadataParser metadataParser = context.mock(MetadataParser.class);
blogManager = new BlogManagerImpl(db, identityManager, clientHelper, blogManager = new BlogManagerImpl(db, contactManager, identityManager, clientHelper,
metadataParser, blogFactory, blogPostFactory); metadataParser, blogFactory, blogPostFactory);
localAuthor1 = getLocalAuthor(); localAuthor1 = getLocalAuthor();
@@ -124,7 +130,7 @@ public class BlogManagerImplTest extends BriarTestCase {
ContactId contactId = new ContactId(0); ContactId contactId = new ContactId(0);
Contact contact = new Contact(contactId, blog2.getAuthor(), Contact contact = new Contact(contactId, blog2.getAuthor(),
blog1.getAuthor().getId(), true, true); blog1.getAuthor().getId(), getRandomString(5), true, true);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(blogFactory).createBlog(blog2.getAuthor()); oneOf(blogFactory).createBlog(blog2.getAuthor());
@@ -146,7 +152,7 @@ public class BlogManagerImplTest extends BriarTestCase {
ContactId contactId = new ContactId(0); ContactId contactId = new ContactId(0);
Contact contact = new Contact(contactId, blog2.getAuthor(), Contact contact = new Contact(contactId, blog2.getAuthor(),
blog1.getAuthor().getId(), true, true); blog1.getAuthor().getId(), getRandomString(5), true, true);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(blogFactory).createBlog(blog2.getAuthor()); oneOf(blogFactory).createBlog(blog2.getAuthor());
@@ -175,8 +181,8 @@ public class BlogManagerImplTest extends BriarTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(clientHelper).parseAndValidateAuthor(authorList1); oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1)); will(returnValue(localAuthor1));
oneOf(identityManager).getAuthorStatus(txn, localAuthor1.getId()); oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(VERIFIED)); will(returnValue(verifiedInfo));
}}); }});
blogManager.incomingMessage(txn, message, body, meta); blogManager.incomingMessage(txn, message, body, meta);
@@ -281,8 +287,8 @@ public class BlogManagerImplTest extends BriarTestCase {
oneOf(clientHelper).addLocalMessage(txn, message, meta, true); oneOf(clientHelper).addLocalMessage(txn, message, meta, true);
oneOf(clientHelper).parseAndValidateAuthor(authorList1); oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1)); will(returnValue(localAuthor1));
oneOf(identityManager).getAuthorStatus(txn, localAuthor1.getId()); oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(OURSELVES)); will(returnValue(ourselvesInfo));
oneOf(db).commitTransaction(txn); oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn); oneOf(db).endTransaction(txn);
}}); }});
@@ -396,21 +402,21 @@ public class BlogManagerImplTest extends BriarTestCase {
// Create the headers for the comment and its parent // Create the headers for the comment and its parent
oneOf(clientHelper).parseAndValidateAuthor(authorList1); oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1)); will(returnValue(localAuthor1));
oneOf(identityManager).getAuthorStatus(txn, localAuthor1.getId()); oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(OURSELVES)); will(returnValue(ourselvesInfo));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, messageId); oneOf(clientHelper).getMessageMetadataAsDictionary(txn, messageId);
will(returnValue(postMeta)); will(returnValue(postMeta));
oneOf(clientHelper).parseAndValidateAuthor(authorList1); oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1)); will(returnValue(localAuthor1));
oneOf(identityManager).getAuthorStatus(txn, localAuthor1.getId()); oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(OURSELVES)); will(returnValue(ourselvesInfo));
oneOf(db).commitTransaction(txn); oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn); oneOf(db).endTransaction(txn);
}}); }});
BlogPostHeader postHeader = new BlogPostHeader(POST, blog1.getId(), BlogPostHeader postHeader = new BlogPostHeader(POST, blog1.getId(),
messageId, null, timestamp, timeReceived, localAuthor1, messageId, null, timestamp, timeReceived, localAuthor1,
OURSELVES, false, true); ourselvesInfo, false, true);
blogManager.addLocalComment(localAuthor1, blog1.getId(), comment, blogManager.addLocalComment(localAuthor1, blog1.getId(), comment,
postHeader); postHeader);
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -504,22 +510,22 @@ public class BlogManagerImplTest extends BriarTestCase {
// Create the headers for the comment and the wrapped post // Create the headers for the comment and the wrapped post
oneOf(clientHelper).parseAndValidateAuthor(authorList2); oneOf(clientHelper).parseAndValidateAuthor(authorList2);
will(returnValue(localAuthor2)); will(returnValue(localAuthor2));
oneOf(identityManager).getAuthorStatus(txn, localAuthor2.getId()); oneOf(contactManager).getAuthorInfo(txn, localAuthor2.getId());
will(returnValue(OURSELVES)); will(returnValue(ourselvesInfo));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
wrappedPostId); wrappedPostId);
will(returnValue(wrappedPostMeta)); will(returnValue(wrappedPostMeta));
oneOf(clientHelper).parseAndValidateAuthor(authorList1); oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1)); will(returnValue(localAuthor1));
oneOf(identityManager).getAuthorStatus(txn, localAuthor1.getId()); oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(VERIFIED)); will(returnValue(verifiedInfo));
oneOf(db).commitTransaction(txn); oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn); oneOf(db).endTransaction(txn);
}}); }});
BlogPostHeader originalPostHeader = new BlogPostHeader(POST, BlogPostHeader originalPostHeader = new BlogPostHeader(POST,
blog1.getId(), messageId, null, timestamp, timeReceived, blog1.getId(), messageId, null, timestamp, timeReceived,
localAuthor1, VERIFIED, false, true); localAuthor1, verifiedInfo, false, true);
blogManager.addLocalComment(localAuthor2, blog2.getId(), comment, blogManager.addLocalComment(localAuthor2, blog2.getId(), comment,
originalPostHeader); originalPostHeader);
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -613,8 +619,8 @@ public class BlogManagerImplTest extends BriarTestCase {
// Create the headers for the comment and the wrapped post // Create the headers for the comment and the wrapped post
oneOf(clientHelper).parseAndValidateAuthor(authorList1); oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1)); will(returnValue(localAuthor1));
oneOf(identityManager).getAuthorStatus(txn, localAuthor1.getId()); oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(OURSELVES)); will(returnValue(ourselvesInfo));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
wrappedPostId); wrappedPostId);
will(returnValue(wrappedPostMeta)); will(returnValue(wrappedPostMeta));
@@ -626,7 +632,7 @@ public class BlogManagerImplTest extends BriarTestCase {
BlogPostHeader originalPostHeader = new BlogPostHeader(POST, BlogPostHeader originalPostHeader = new BlogPostHeader(POST,
rssBlog.getId(), rssMessageId, null, timestamp, timeReceived, rssBlog.getId(), rssMessageId, null, timestamp, timeReceived,
rssLocalAuthor, NONE, true, true); rssLocalAuthor, new AuthorInfo(NONE), true, true);
blogManager.addLocalComment(localAuthor1, blog1.getId(), comment, blogManager.addLocalComment(localAuthor1, blog1.getId(), comment,
originalPostHeader); originalPostHeader);
context.assertIsSatisfied(); context.assertIsSatisfied();
@@ -752,15 +758,15 @@ public class BlogManagerImplTest extends BriarTestCase {
// the rewrapped post // the rewrapped post
oneOf(clientHelper).parseAndValidateAuthor(authorList2); oneOf(clientHelper).parseAndValidateAuthor(authorList2);
will(returnValue(localAuthor2)); will(returnValue(localAuthor2));
oneOf(identityManager).getAuthorStatus(txn, localAuthor2.getId()); oneOf(contactManager).getAuthorInfo(txn, localAuthor2.getId());
will(returnValue(OURSELVES)); will(returnValue(ourselvesInfo));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
wrappedCommentId); wrappedCommentId);
will(returnValue(wrappedCommentMeta)); will(returnValue(wrappedCommentMeta));
oneOf(clientHelper).parseAndValidateAuthor(authorList1); oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1)); will(returnValue(localAuthor1));
oneOf(identityManager).getAuthorStatus(txn, localAuthor1.getId()); oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(VERIFIED)); will(returnValue(verifiedInfo));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
rewrappedPostId); rewrappedPostId);
will(returnValue(rewrappedPostMeta)); will(returnValue(rewrappedPostMeta));
@@ -772,10 +778,10 @@ public class BlogManagerImplTest extends BriarTestCase {
BlogPostHeader wrappedPostHeader = new BlogPostHeader(WRAPPED_POST, BlogPostHeader wrappedPostHeader = new BlogPostHeader(WRAPPED_POST,
blog1.getId(), wrappedPostId, null, timestamp, timeReceived, blog1.getId(), wrappedPostId, null, timestamp, timeReceived,
rssLocalAuthor, NONE, true, true); rssLocalAuthor, new AuthorInfo(NONE), true, true);
BlogCommentHeader originalCommentHeader = new BlogCommentHeader(COMMENT, BlogCommentHeader originalCommentHeader = new BlogCommentHeader(COMMENT,
blog1.getId(), comment, wrappedPostHeader, originalCommentId, blog1.getId(), comment, wrappedPostHeader, originalCommentId,
timestamp, timeReceived, localAuthor1, VERIFIED, true); timestamp, timeReceived, localAuthor1, verifiedInfo, true);
blogManager.addLocalComment(localAuthor2, blog2.getId(), localComment, blogManager.addLocalComment(localAuthor2, blog2.getId(), localComment,
originalCommentHeader); originalCommentHeader);

View File

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

View File

@@ -24,7 +24,7 @@ import java.util.Collection;
import javax.annotation.Nullable; import javax.annotation.Nullable;
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.privategroup.Visibility.INVISIBLE; import static org.briarproject.briar.api.privategroup.Visibility.INVISIBLE;
import static org.briarproject.briar.api.privategroup.Visibility.REVEALED_BY_CONTACT; import static org.briarproject.briar.api.privategroup.Visibility.REVEALED_BY_CONTACT;
import static org.briarproject.briar.api.privategroup.Visibility.REVEALED_BY_US; import static org.briarproject.briar.api.privategroup.Visibility.REVEALED_BY_US;
@@ -92,7 +92,8 @@ public class PrivateGroupIntegrationTest
Collection<GroupMember> members = groupManager0.getMembers(groupId0); Collection<GroupMember> members = groupManager0.getMembers(groupId0);
assertEquals(1, members.size()); assertEquals(1, members.size());
assertEquals(author0, members.iterator().next().getAuthor()); assertEquals(author0, members.iterator().next().getAuthor());
assertEquals(OURSELVES, members.iterator().next().getStatus()); assertEquals(OURSELVES,
members.iterator().next().getAuthorInfo().getStatus());
sync0To1(1, true); sync0To1(1, true);
groupInvitationManager1 groupInvitationManager1
@@ -107,7 +108,7 @@ public class PrivateGroupIntegrationTest
members = groupManager0.getMembers(groupId0); members = groupManager0.getMembers(groupId0);
assertEquals(2, members.size()); assertEquals(2, members.size());
for (GroupMember m : members) { for (GroupMember m : members) {
if (m.getStatus() == OURSELVES) { if (m.getAuthorInfo().getStatus() == OURSELVES) {
assertEquals(author0.getId(), m.getAuthor().getId()); assertEquals(author0.getId(), m.getAuthor().getId());
} else { } else {
assertEquals(author1.getId(), m.getAuthor().getId()); assertEquals(author1.getId(), m.getAuthor().getId());
@@ -117,7 +118,7 @@ public class PrivateGroupIntegrationTest
members = groupManager1.getMembers(groupId0); members = groupManager1.getMembers(groupId0);
assertEquals(2, members.size()); assertEquals(2, members.size());
for (GroupMember m : members) { for (GroupMember m : members) {
if (m.getStatus() == OURSELVES) { if (m.getAuthorInfo().getStatus() == OURSELVES) {
assertEquals(author1.getId(), m.getAuthor().getId()); assertEquals(author1.getId(), m.getAuthor().getId());
} else { } else {
assertEquals(author0.getId(), m.getAuthor().getId()); assertEquals(author0.getId(), m.getAuthor().getId());

View File

@@ -20,7 +20,7 @@ import org.junit.Test;
import java.util.Collection; import java.util.Collection;
import static org.briarproject.bramble.api.identity.Author.Status.VERIFIED; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;

View File

@@ -83,7 +83,7 @@ abstract class AbstractProtocolEngineTest extends BrambleMockTestCase {
BdfDictionary.of(new BdfEntry("me", "ta")); BdfDictionary.of(new BdfEntry("me", "ta"));
final ContactId contactId = new ContactId(5); final ContactId contactId = new ContactId(5);
final Contact contact = new Contact(contactId, author, final Contact contact = new Contact(contactId, author,
new AuthorId(getRandomId()), true, true); new AuthorId(getRandomId()), getRandomString(5), true, true);
final InviteMessage inviteMessage = final InviteMessage inviteMessage =
new InviteMessage(new MessageId(getRandomId()), contactGroupId, new InviteMessage(new MessageId(getRandomId()), contactGroupId,

View File

@@ -100,7 +100,7 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
private final ContactId contactId = new ContactId(0); private final ContactId contactId = new ContactId(0);
private final Author author = getAuthor(); private final Author author = getAuthor();
private final Contact contact = new Contact(contactId, author, private final Contact contact = new Contact(contactId, author,
new AuthorId(getRandomId()), true, true); new AuthorId(getRandomId()), getRandomString(5), true, true);
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION); private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Group privateGroup = getGroup(CLIENT_ID, MAJOR_VERSION); private final Group privateGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
@@ -845,9 +845,9 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testRemovingGroupEndsSessions() throws Exception { public void testRemovingGroupEndsSessions() throws Exception {
Contact contact2 = new Contact(new ContactId(2), author, Contact contact2 = new Contact(new ContactId(2), author,
author.getId(), true, true); author.getId(), getRandomString(5), true, true);
Contact contact3 = new Contact(new ContactId(3), author, Contact contact3 = new Contact(new ContactId(3), author,
author.getId(), true, true); author.getId(), getRandomString(5), true, true);
Collection<Contact> contacts = Collection<Contact> contacts =
Arrays.asList(contact, contact2, contact3); Arrays.asList(contact, contact2, contact3);

View File

@@ -331,7 +331,7 @@ public class InviteeProtocolEngineTest extends AbstractProtocolEngineTest {
signature); signature);
Author notCreator = getAuthor(); Author notCreator = getAuthor();
Contact notCreatorContact = new Contact(contactId, notCreator, Contact notCreatorContact = new Contact(contactId, notCreator,
localAuthor.getId(), true, true); localAuthor.getId(), getRandomString(5), true, true);
expectGetContactId(); expectGetContactId();
context.checking(new Expectations() {{ context.checking(new Expectations() {{

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.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.api.blog.BlogSharingManager.CLIENT_ID; import static org.briarproject.briar.api.blog.BlogSharingManager.CLIENT_ID;
import static org.briarproject.briar.api.blog.BlogSharingManager.MAJOR_VERSION; import static org.briarproject.briar.api.blog.BlogSharingManager.MAJOR_VERSION;
import static org.briarproject.briar.sharing.SharingConstants.GROUP_KEY_CONTACT_ID; import static org.briarproject.briar.sharing.SharingConstants.GROUP_KEY_CONTACT_ID;
@@ -63,7 +64,8 @@ public class BlogSharingManagerImplTest extends BrambleMockTestCase {
private final ContactId contactId = new ContactId(0); private final ContactId contactId = new ContactId(0);
private final Author author = getAuthor(); private final Author author = getAuthor();
private final Contact contact = private final Contact contact =
new Contact(contactId, author, localAuthor.getId(), true, true); new Contact(contactId, author, localAuthor.getId(),
getRandomString(5), true, true);
private final Collection<Contact> contacts = private final Collection<Contact> contacts =
Collections.singletonList(contact); Collections.singletonList(contact);
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION); private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);

View File

@@ -72,9 +72,16 @@ Returns a JSON array of contacts:
The only workaround is to add a contact to the Briar app running on a rooted Android phone The only workaround is to add a contact to the Briar app running on a rooted Android phone
and then move its database (and key files) to the headless peer. and then move its database (and key files) to the headless peer.
### Removing a contact
`DELETE /v1/contacts/{contactId}`
The `{contactId}` is the `contactId` of the contact (`1` in the example above).
It returns with a status code `200`, if removal was successful.
### Listing all private messages ### Listing all private messages
`GET /messages/{contactId}` `GET /v1/messages/{contactId}`
The `{contactId}` is the `contactId` of the contact (`1` in the example above). The `{contactId}` is the `contactId` of the contact (`1` in the example above).
It returns a JSON array of private messages: It returns a JSON array of private messages:
@@ -100,7 +107,7 @@ Attention: There can messages of other `type`s where the message `text` is `null
### Writing a private message ### Writing a private message
`POST /messages/{contactId}` `POST /v1/messages/{contactId}`
The text of the message should be posted as JSON: The text of the message should be posted as JSON:

View File

@@ -1,8 +1,8 @@
plugins { plugins {
id 'java' id 'java'
id 'idea' id 'idea'
id 'org.jetbrains.kotlin.jvm' version '1.2.70' id 'org.jetbrains.kotlin.jvm' version '1.2.71'
id 'org.jetbrains.kotlin.kapt' version '1.2.70' id 'org.jetbrains.kotlin.kapt' version '1.2.71'
id 'witness' id 'witness'
} }
apply from: 'witness.gradle' apply from: 'witness.gradle'
@@ -14,9 +14,9 @@ dependencies {
implementation project(path: ':briar-core', configuration: 'default') implementation project(path: ':briar-core', configuration: 'default')
implementation project(path: ':bramble-java', configuration: 'default') implementation project(path: ':bramble-java', configuration: 'default')
implementation 'io.javalin:javalin:2.2.0' implementation 'io.javalin:javalin:2.3.0'
implementation 'org.slf4j:slf4j-simple:1.7.25' implementation 'org.slf4j:slf4j-simple:1.7.25'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.6' implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.7'
implementation 'com.github.ajalt:clikt:1.5.0' implementation 'com.github.ajalt:clikt:1.5.0'
kapt 'com.google.dagger:dagger-compiler:2.0.2' kapt 'com.google.dagger:dagger-compiler:2.0.2'
@@ -24,11 +24,11 @@ dependencies {
testImplementation project(path: ':bramble-api', configuration: 'testOutput') testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation project(path: ':bramble-core', configuration: 'testOutput') testImplementation project(path: ':bramble-core', configuration: 'testOutput')
def junitVersion = '5.2.0' def junitVersion = '5.3.1'
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion" testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion"
testRuntime "org.junit.jupiter:junit-jupiter-engine:$junitVersion" testRuntime "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
testImplementation "io.mockk:mockk:1.8.6" testImplementation "io.mockk:mockk:1.8.9"
testImplementation "org.skyscreamer:jsonassert:1.5.0" testImplementation "org.skyscreamer:jsonassert:1.5.0"
} }

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.identity package org.briarproject.bramble.identity
import org.briarproject.bramble.api.identity.Author import org.briarproject.bramble.api.identity.Author
import org.briarproject.bramble.api.identity.AuthorInfo
import org.briarproject.briar.headless.json.JsonDict import org.briarproject.briar.headless.json.JsonDict
fun Author.output() = JsonDict( fun Author.output() = JsonDict(
@@ -10,4 +11,4 @@ fun Author.output() = JsonDict(
"publicKey" to publicKey "publicKey" to publicKey
) )
fun Author.Status.output() = name.toLowerCase() fun AuthorInfo.Status.output() = name.toLowerCase()

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