Compare commits

...

39 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
389b2b5b8e Merge branch '1423-check-lifecycle-state' into 'master'
Check lifecycle state before changing fragment

Closes #1423

See merge request briar/briar!958
2018-10-22 14:27:30 +00:00
Torsten Grote
78abfa3698 Check lifecycle state before changing fragment 2018-10-19 16:40:00 -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
e47d41596a Merge branch '1254-inject-object-mapper' into 'master'
briar-headless: Inject a singleton ObjectMapper for JSON parsing

See merge request briar/briar!957
2018-10-18 15:40:20 +00:00
Torsten Grote
8cf54bcedb briar-headless: Inject a singleton ObjectMapper for JSON parsing 2018-10-18 09:55:34 -03:00
akwizgran
89d5145665 Bump version numbers for 1.1.3 release. 2018-10-17 17:51:11 +01:00
Torsten Grote
0706498b03 Merge branch 'upgrade-android-gradle-plugin' into 'master'
Upgrade Android Gradle plugin to 3.2.1.

See merge request briar/briar!956
2018-10-17 16:36:24 +00:00
akwizgran
60a8b03344 Upgrade Android Gradle plugin to 3.2.1. 2018-10-17 12:34:13 +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
117 changed files with 1881 additions and 795 deletions

View File

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

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.DuplexTransportConnection;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.AndroidUtils;
import java.io.Closeable;
import java.io.IOException;
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.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Logger;
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_STATE_CHANGED;
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.STATE_OFF;
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 org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -47,8 +61,11 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
private static final Logger LOG =
Logger.getLogger(AndroidBluetoothPlugin.class.getName());
private static final int MAX_DISCOVERY_MS = 10_000;
private final AndroidExecutor androidExecutor;
private final Context appContext;
private final Clock clock;
private volatile boolean wasEnabledByUs = false;
private volatile BluetoothStateReceiver receiver = null;
@@ -58,12 +75,13 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
Executor ioExecutor, AndroidExecutor androidExecutor,
Context appContext, SecureRandom secureRandom, Backoff backoff,
DuplexPluginCallback callback, int maxLatency) {
Context appContext, SecureRandom secureRandom, Clock clock,
Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
maxLatency);
this.androidExecutor = androidExecutor;
this.appContext = appContext;
this.clock = clock;
}
@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) {
try {
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.DuplexPluginFactory;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock;
import java.security.SecureRandom;
import java.util.concurrent.Executor;
@@ -33,17 +34,19 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
private final Context appContext;
private final SecureRandom secureRandom;
private final EventBus eventBus;
private final Clock clock;
private final BackoffFactory backoffFactory;
public AndroidBluetoothPluginFactory(Executor ioExecutor,
AndroidExecutor androidExecutor, Context appContext,
SecureRandom secureRandom, EventBus eventBus,
SecureRandom secureRandom, EventBus eventBus, Clock clock,
BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor;
this.androidExecutor = androidExecutor;
this.appContext = appContext;
this.secureRandom = secureRandom;
this.eventBus = eventBus;
this.clock = clock;
this.backoffFactory = backoffFactory;
}
@@ -65,7 +68,7 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
connectionLimiter, ioExecutor, androidExecutor, appContext,
secureRandom, backoff, callback, MAX_LATENCY);
secureRandom, clock, backoff, callback, MAX_LATENCY);
eventBus.addListener(plugin);
return plugin;
}

View File

@@ -1,34 +1,34 @@
dependencyVerification {
verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.android.tools.analytics-library:protos:26.2.0:protos-26.2.0.jar:306784ac579be37f64f110204ea3fad8568e09e10817bb574bac0fc71b6402c5',
'com.android.tools.analytics-library:shared:26.2.0:shared-26.2.0.jar:10782c593b7edcaf63cf79f7179c4652fee1c01298e0d81d888eea5ea1c320d4',
'com.android.tools.analytics-library:tracker:26.2.0:tracker-26.2.0.jar:74e3cf587bcd6f5f14a6cddb8123c6940baf9947582f5356c25dd852c4726564',
'com.android.tools.build:aapt2:3.2.0-4818971:aapt2-3.2.0-4818971-linux.jar:0d86e045e9eb009ab8add53cf0e5c0f48674e8c8a4edd3b19fc978ea38508bb1',
'com.android.tools.build:apksig:3.2.0:apksig-3.2.0.jar:d393b14a2f85a4052645fa0b59e111b46dea0fd59a934bec6cf1e997a0640e30',
'com.android.tools.build:apkzlib:3.2.0:apkzlib-3.2.0.jar:d8a3404d63a1037207bc2eb472209c606f8143d201f484da9dacd04ffa538c31',
'com.android.tools.build:builder-model:3.2.0:builder-model-3.2.0.jar:7deb28465f2303fdc9df58ba83d080193edefe1624784d3ce9e0a0949ae05a49',
'com.android.tools.build:builder-test-api:3.2.0:builder-test-api-3.2.0.jar:b7c6676da0793240f16874c4008f12ed3ed1bc56a4a73f5e760044024ac9a02a',
'com.android.tools.build:builder:3.2.0:builder-3.2.0.jar:191df45703f615a02db16ce5ce78489f1ec91f9b83e677bec5de29de765e326d',
'com.android.tools.build:gradle-api:3.2.0:gradle-api-3.2.0.jar:5645c5ee4138b1354b24bed43e005964296a5f1ef48b6772ed9800159dcc2830',
'com.android.tools.build:manifest-merger:26.2.0:manifest-merger-26.2.0.jar:b821830bf067852688673cae01fb571c04da094985a3894878070b50f0587edf',
'com.android.tools.ddms:ddmlib:26.2.0:ddmlib-26.2.0.jar:ff9b6951698a6ec005b25e5e3b5f7fe960f566131ba8fcdf713392e10cc2dd50',
'com.android.tools.external.com-intellij:intellij-core:26.2.0:intellij-core-26.2.0.jar:03724f5aff30c8fff3d0b0c05e71f8dc21f246952fe1f3b5916bfb594e337bad',
'com.android.tools.external.com-intellij:kotlin-compiler:26.2.0:kotlin-compiler-26.2.0.jar:2f82e0c2687afe7c4941bdf6702d9a16d71d030a7ddb384a04a0537a361f4bb1',
'com.android.tools.external.org-jetbrains:uast:26.2.0:uast-26.2.0.jar:a439cafcddb2963620fd737005e3ce0560313362ede37e6c6eac5f10ff19eb94',
'com.android.tools.layoutlib:layoutlib-api:26.2.0:layoutlib-api-26.2.0.jar:5987488847729bad6a1e72cd5dcc46b68b61d8f05ef60858b87ed1d3b9bacc58',
'com.android.tools.lint:lint-api:26.2.0:lint-api-26.2.0.jar:aa4dc0399b6ae602286b9b106c661b50f6a8e1143c5949d3e7fd93c4a4d1513d',
'com.android.tools.lint:lint-checks:26.2.0:lint-checks-26.2.0.jar:c8c8dfdbd5c97a371e239b6000f4c6929bce315c2b13e690aaf34e3fcb045520',
'com.android.tools.lint:lint-gradle-api:26.2.0:lint-gradle-api-26.2.0.jar:befc1ad242e5e89e8977b2f799c465fee1b096ac645eba659acab832980599cd',
'com.android.tools.lint:lint-gradle:26.2.0:lint-gradle-26.2.0.jar:332d257f20537020fd268468f678ccfbcc0be494fa50526b63c532836dc7c5dd',
'com.android.tools.lint:lint-kotlin:26.2.0:lint-kotlin-26.2.0.jar:c149e09434aa24a6f7161dbd4b09f08c46f018cb0ee1ee2d1a32bf3b5015e081',
'com.android.tools.lint:lint:26.2.0:lint-26.2.0.jar:4df411ea5ef106780073c6e953ab591a55d52e94c9b1cd2f99970a124eca405e',
'com.android.tools:annotations:26.2.0:annotations-26.2.0.jar:af775f75168acbadb2ff61d6955e4fbeca9b1bd9f7a40b54ab7a489e30af45bb',
'com.android.tools:common:26.2.0:common-26.2.0.jar:3e60e91bc7c7a634bbfae3522c5e0b49e8a268a2d69904bed4bd55833f8bb0c9',
'com.android.tools:dvlib:26.2.0:dvlib-26.2.0.jar:c4d8fac5b8432e3676256ece4e25c3e55e8ef2da4298fb060cc6086d7e2235fc',
'com.android.tools:repository:26.2.0:repository-26.2.0.jar:9c9e320195e7e6d9bbdf9b5b0ed1d7e4da3625ad344902cb47b32c9ef8306d82',
'com.android.tools:sdk-common:26.2.0:sdk-common-26.2.0.jar:1e32745633177b28419041d9bd182bb99b51d1a7ad3f9b1c8b5292ed799656cd',
'com.android.tools:sdklib:26.2.0:sdklib-26.2.0.jar:08e2715f9abd3870df8e311f77ff6f3dae61193c1f6a2de78bf2e49c6ba4725a',
'com.android.tools.analytics-library:protos:26.2.1:protos-26.2.1.jar:2f371f5b1f551e85ab08be4d6a2873471b3d44afd1ebf6aa3298f3b796bf691f',
'com.android.tools.analytics-library:shared:26.2.1:shared-26.2.1.jar:4c1e4e705fa4d45f23aaea230557f6508155012d9c296337787c1d7b26a97f5a',
'com.android.tools.analytics-library:tracker:26.2.1:tracker-26.2.1.jar:4a624ecc976539f755ddb0bb8dfc2dd3d08326cfec59a098dbd70f701ca7fb75',
'com.android.tools.build:aapt2:3.2.1-4818971:aapt2-3.2.1-4818971-linux.jar:f431b6f96c91a2c155144b091a9c97d9805c589fe8efc9c930b6cd346cb60a1e',
'com.android.tools.build:apksig:3.2.1:apksig-3.2.1.jar:2b46f2feffea66037aab29e4261b2433c190194a6ef97b958511eb157f2ccba5',
'com.android.tools.build:apkzlib:3.2.1:apkzlib-3.2.1.jar:c39ad0313905932431fe81c8899c2cf39a4d92ad6c4edcaa4b25432f461452aa',
'com.android.tools.build:builder-model:3.2.1:builder-model-3.2.1.jar:a9f68e6abcec122f9cb5ad352d3f05a3eb03acbcdca95e4d25c16310c2c965ff',
'com.android.tools.build:builder-test-api:3.2.1:builder-test-api-3.2.1.jar:533ac6c2b5884bb54967a33791f2628dfdfac7981af39417a333b43d4379b6be',
'com.android.tools.build:builder:3.2.1:builder-3.2.1.jar:aedcbfd115dbe91d09b4113e66ef50589b558d0aa3b2f133b1d867c9b87fae83',
'com.android.tools.build:gradle-api:3.2.1:gradle-api-3.2.1.jar:57cf0ac5ac1dca8afdb3f62b94265e776e7dcfa641cc3844fb53a05193de208d',
'com.android.tools.build:manifest-merger:26.2.1:manifest-merger-26.2.1.jar:8830573263361035d38cfdcb51e2db94029c93865b21334f5fbf8a27984281a6',
'com.android.tools.ddms:ddmlib:26.2.1:ddmlib-26.2.1.jar:a4bf0a29a19980bf27269465cc782064656750b77c26728f82f9e148b705218b',
'com.android.tools.external.com-intellij:intellij-core:26.2.1:intellij-core-26.2.1.jar:4925ad1892c2687cb1a63427d440ef519c8c59215fefe0dc5d541d5d411fcafe',
'com.android.tools.external.com-intellij:kotlin-compiler:26.2.1:kotlin-compiler-26.2.1.jar:daa064fd708f340ee25fb9823c4c74104ac77f1370b76d907eb9ae6daec0a2ae',
'com.android.tools.external.org-jetbrains:uast:26.2.1:uast-26.2.1.jar:f10f7258d2ab9189562cc0f9ad838c0378fdba439229173390a99de02ebac75b',
'com.android.tools.layoutlib:layoutlib-api:26.2.1:layoutlib-api-26.2.1.jar:ddbf4fca123733fa011595b1cc1f4ac2937ed327b60990711fafc33c775c2ade',
'com.android.tools.lint:lint-api:26.2.1:lint-api-26.2.1.jar:3b57e739de567b98bc9ab56c2c0ee66fc026b4adf5843e8f9804ca0666a6f66e',
'com.android.tools.lint:lint-checks:26.2.1:lint-checks-26.2.1.jar:c86f4cc9aaee722ee4ad70062f7b5af91e9b041914af27adc09f545ab0fb3bc6',
'com.android.tools.lint:lint-gradle-api:26.2.1:lint-gradle-api-26.2.1.jar:2283e7af32e301565f2a797e531f0fc8c648077d457afb3ffdddbee638976c2f',
'com.android.tools.lint:lint-gradle:26.2.1:lint-gradle-26.2.1.jar:8fd90b2f3ec788cbb9801c07ab3e1ea2255aa31a6093157d7ea0ff13d0315ecb',
'com.android.tools.lint:lint-kotlin:26.2.1:lint-kotlin-26.2.1.jar:7a6a5d2b18f69cf1b900d857c2632b4c683713c533295933b8b759f8cab4a877',
'com.android.tools.lint:lint:26.2.1:lint-26.2.1.jar:7848b82ae988b90dee259ae7c7e86e05cbf52db6cd21c8bbd38ce7df08f3f8c5',
'com.android.tools:annotations:26.2.1:annotations-26.2.1.jar:7391c6a1e080174b96e64ceb078dadd31ce4d8a2d2fee0ec65be202126f90f24',
'com.android.tools:common:26.2.1:common-26.2.1.jar:a50aab2d6411ff68f4004a87c7e93d87d8e980a0ec3b352246549897ea2d78e5',
'com.android.tools:dvlib:26.2.1:dvlib-26.2.1.jar:72a83bf2839b1df9b1fbf67ba45d1bfb9f966cd774da4320c762b2be8f1688aa',
'com.android.tools:repository:26.2.1:repository-26.2.1.jar:fa74dae09103faef703df38550ad8fa244c5b6d1bf90d6198be932292b3d9cc1',
'com.android.tools:sdk-common:26.2.1:sdk-common-26.2.1.jar:759d4b292ca69a35cf961fca377b54158fc6c88108978006999442e80a011cf4',
'com.android.tools:sdklib:26.2.1:sdklib-26.2.1.jar:248df7ad5eac4aeb6f96c394c76760de4b7b89ac056e54d0c21a739368b91b45',
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
'com.google.code.gson:gson:2.8.0:gson-2.8.0.jar:c6221763bd79c4f1c3dc7f750b5f29a0bb38b367b81314c4f71896e340c40825',
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
@@ -72,9 +72,10 @@ dependencyVerification {
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.jetbrains.kotlin:kotlin-reflect:1.2.0:kotlin-reflect-1.2.0.jar:4f48a872bad6e4d9c053f4ad610d11e4012ad7e58dc19a03dd5eb811f36069dd',
'org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0:kotlin-stdlib-jre7-1.2.0.jar:c7a20fb951d437797afe8980aff6c1e5a03f310c661ba58ba1d4fa90cb0f2926',
'org.jetbrains.kotlin:kotlin-stdlib-jre8:1.2.0:kotlin-stdlib-jre8-1.2.0.jar:633524eee6ef1941f7cb1dab7ee3927b0a221ceee9047aeb5515f4cbb990c82a',
'org.jetbrains.kotlin:kotlin-stdlib:1.2.0:kotlin-stdlib-1.2.0.jar:05cfd9f5ac0b41910703a8925f7211a495909b27a2ffdd1c5106f1689aeafcd4',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71:kotlin-stdlib-common-1.2.71.jar:63999687ff2fce8a592dd180ffbbf8f1d21c26b4044c55cdc74ff3cf3b3cf328',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71:kotlin-stdlib-jdk7-1.2.71.jar:b136bd61b240e07d4d92ce00d3bd1dbf584400a7bf5f220c2f3cd22446858082',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71:kotlin-stdlib-jdk8-1.2.71.jar:ac3c8abf47790b64b4f7e2509a53f0c145e061ac1612a597520535d199946ea9',
'org.jetbrains.kotlin:kotlin-stdlib:1.2.71:kotlin-stdlib-1.2.71.jar:4c895c270b87f5fec2a2796e1d89c15407ee821de961527c28588bb46afbc68b',
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,7 +23,6 @@ import org.briarproject.bramble.api.plugin.event.EnableBluetoothEvent;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import org.briarproject.bramble.util.StringUtils;
import java.io.IOException;
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.util.LogUtils.logException;
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
@ParametersNotNullByDefault
@@ -96,6 +98,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
abstract DuplexTransportConnection connectTo(String address, String uuid)
throws IOException;
@Nullable
abstract DuplexTransportConnection discoverAndConnect(String uuid);
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
Executor ioExecutor, SecureRandom secureRandom,
Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
@@ -193,7 +198,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
address = getBluetoothAddress();
if (LOG.isLoggable(INFO))
LOG.info("Local address " + scrubMacAddress(address));
if (!StringUtils.isNullOrEmpty(address)) {
if (!isNullOrEmpty(address)) {
p.put(PROP_ADDRESS, address);
changed = true;
}
@@ -256,9 +261,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
// Try to connect to known devices in parallel
for (Entry<ContactId, TransportProperties> e : contacts.entrySet()) {
String address = e.getValue().get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) continue;
if (isNullOrEmpty(address)) continue;
String uuid = e.getValue().get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) continue;
if (isNullOrEmpty(uuid)) continue;
ContactId c = e.getKey();
ioExecutor.execute(() -> {
if (!isRunning() || !shouldAllowContactConnections()) return;
@@ -309,9 +314,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
if (!isRunning() || !shouldAllowContactConnections()) return null;
if (!connectionLimiter.canOpenContactConnection()) return null;
String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) return null;
if (isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) return null;
if (isNullOrEmpty(uuid)) return null;
DuplexTransportConnection conn = connect(address, uuid);
if (conn == null) return null;
// TODO: Why don't we reset the backoff here?
@@ -326,9 +331,6 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
@Override
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
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
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
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();
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);
}
@@ -354,18 +357,25 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, BdfList descriptor) {
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
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
if (LOG.isLoggable(INFO))
LOG.info("Connecting to key agreement UUID " + uuid);
DuplexTransportConnection conn = connect(address, uuid);
DuplexTransportConnection conn;
if (descriptor.size() == 1) {
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);
return conn;
}
@@ -373,7 +383,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
private String parseAddress(BdfList descriptor) throws FormatException {
byte[] mac = descriptor.getRaw(1);
if (mac.length != 6) throw new FormatException();
return StringUtils.macToString(mac);
return macToString(mac);
}
@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.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
@@ -20,10 +25,20 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Random;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class ContactManagerImplTest extends BrambleMockTestCase {
@@ -31,16 +46,20 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
private final Mockery context = new Mockery();
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final KeyManager keyManager = context.mock(KeyManager.class);
private final IdentityManager identityManager =
context.mock(IdentityManager.class);
private final ContactManager contactManager;
private final ContactId contactId = new ContactId(42);
private final Author remote = getAuthor();
private final AuthorId local = new AuthorId(getRandomId());
private final LocalAuthor localAuthor = getLocalAuthor();
private final String alias = getRandomString(MAX_AUTHOR_NAME_LENGTH);
private final boolean verified = false, active = true;
private final Contact contact =
new Contact(contactId, remote, local, verified, active);
new Contact(contactId, remote, local, alias, verified, active);
public ContactManagerImplTest() {
contactManager = new ContactManagerImpl(db, keyManager);
contactManager = new ContactManagerImpl(db, keyManager, identityManager);
}
@Test
@@ -105,7 +124,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(Collections.emptyList()));
will(returnValue(emptyList()));
oneOf(db).endTransaction(txn);
}});
@@ -131,7 +150,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
public void testActiveContacts() throws Exception {
Collection<Contact> activeContacts = Collections.singletonList(contact);
Collection<Contact> contacts = new ArrayList<>(activeContacts);
contacts.add(new Contact(new ContactId(3), remote, local, true, false));
contacts.add(new Contact(new ContactId(3), remote, local, alias, true,
false));
Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
@@ -171,6 +191,23 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
contactManager.setContactActive(txn, contactId, active);
}
@Test
public void testSetContactAlias() throws Exception {
Transaction txn = new Transaction(null, false);
context.checking(new DbExpectations() {{
oneOf(db).transaction(with(equal(false)), withDbRunnable(txn));
oneOf(db).setContactAlias(txn, contactId, alias);
}});
contactManager.setContactAlias(contactId, alias);
}
@Test(expected = IllegalArgumentException.class)
public void testSetContactAliasTooLong() throws Exception {
contactManager.setContactAlias(contactId,
getRandomString(MAX_AUTHOR_NAME_LENGTH + 1));
}
@Test
public void testContactExists() throws Exception {
Transaction txn = new Transaction(null, true);
@@ -186,4 +223,79 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
assertTrue(contactManager.contactExists(remote.getId(), local));
}
@Test
public void testGetAuthorStatus() throws Exception {
Transaction txn = new Transaction(null, true);
Collection<Contact> contacts = singletonList(
new Contact(new ContactId(1), remote, localAuthor.getId(),
alias, false, true));
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(equal(true)),
withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(contacts));
}});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(UNVERIFIED, authorInfo.getStatus());
assertEquals(alias, contact.getAlias());
}
@Test
public void testGetAuthorStatusTransaction() throws DbException {
Transaction txn = new Transaction(null, true);
// check unknown author
context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(emptyList()));
}});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(UNKNOWN, authorInfo.getStatus());
assertNull(authorInfo.getAlias());
// check unverified contact
Collection<Contact> contacts = singletonList(
new Contact(new ContactId(1), remote, localAuthor.getId(),
alias, false, true));
checkAuthorStatusContext(txn, remote.getId(), contacts);
authorInfo = contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(UNVERIFIED, authorInfo.getStatus());
assertEquals(alias, contact.getAlias());
// check verified contact
contacts = singletonList(new Contact(new ContactId(1), remote,
localAuthor.getId(), alias, true, true));
checkAuthorStatusContext(txn, remote.getId(), contacts);
authorInfo = contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(VERIFIED, authorInfo.getStatus());
assertEquals(alias, contact.getAlias());
// check ourselves
context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
never(db).getContactsByAuthorId(txn, remote.getId());
}});
authorInfo = contactManager.getAuthorInfo(txn, localAuthor.getId());
assertEquals(OURSELVES, authorInfo.getStatus());
assertNull(authorInfo.getAlias());
}
private void checkAuthorStatusContext(Transaction txn, AuthorId authorId,
Collection<Contact> contacts) throws DbException {
context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContactsByAuthorId(txn, authorId);
will(returnValue(contacts));
}});
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,6 +7,7 @@
<uses-feature android:name="android.hardware.camera" 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_WIFI_STATE" />
<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.briar.BriarCoreEagerSingletons;
import org.briarproject.briar.BriarCoreModule;
import org.briarproject.briar.android.contact.ConversationViewModel;
import org.briarproject.briar.android.login.SignInReminderReceiver;
import org.briarproject.briar.android.reporting.BriarReportSender;
import org.briarproject.briar.android.view.TextInputView;
@@ -164,6 +165,8 @@ public interface AndroidComponent
void inject(TextInputView textInputView);
void inject(ConversationViewModel conversationViewModel);
// Eager singleton load
void inject(AppModule.EagerSingletons init);
}

View File

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

View File

@@ -34,13 +34,17 @@ import org.briarproject.briar.api.android.ScreenFilterMonitor.AppDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.arch.lifecycle.Lifecycle.State.STARTED;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
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;
/**
@@ -50,6 +54,8 @@ import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOT
public abstract class BaseActivity extends AppCompatActivity
implements DestroyableContext, OnTapFilteredListener {
private final static Logger LOG = getLogger(BaseActivity.class.getName());
@Inject
protected ScreenFilterMonitor screenFilterMonitor;
@@ -118,6 +124,8 @@ public abstract class BaseActivity extends AppCompatActivity
@Override
protected void onStart() {
super.onStart();
if (LOG.isLoggable(INFO))
LOG.info("Starting " + this.getClass().getSimpleName());
for (ActivityLifecycleController alc : lifecycleControllers) {
alc.onActivityStart();
}
@@ -136,6 +144,8 @@ public abstract class BaseActivity extends AppCompatActivity
@Override
protected void onStop() {
super.onStop();
if (LOG.isLoggable(INFO))
LOG.info("Stopping " + this.getClass().getSimpleName());
for (ActivityLifecycleController alc : lifecycleControllers) {
alc.onActivityStop();
}
@@ -148,6 +158,7 @@ public abstract class BaseActivity extends AppCompatActivity
}
public void showNextFragment(BaseFragment f) {
if (!getLifecycle().getCurrentState().isAtLeast(STARTED)) return;
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.step_next_in,
R.anim.step_previous_out, R.anim.step_previous_in,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,6 @@ package org.briarproject.briar.android.keyagreement;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
@@ -11,19 +10,15 @@ import android.support.annotation.StringRes;
import android.support.annotation.UiThread;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog.Builder;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.widget.Toast;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent;
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.BriarActivity;
import org.briarproject.briar.android.fragment.BaseFragment;
@@ -37,16 +32,19 @@ import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
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.EXTRA_SCAN_MODE;
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.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION.SDK_INT;
import static android.widget.Toast.LENGTH_LONG;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_ENABLE_BLUETOOTH;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -55,7 +53,11 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
KeyAgreementEventListener {
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 =
@@ -64,8 +66,27 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
@Inject
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 BroadcastReceiver bluetoothReceiver = null;
@@ -85,7 +106,9 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
if (state == null) {
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();
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
protected void onPostResume() {
super.onPostResume();
isResumed = true;
// Workaround for
// https://code.google.com/p/android/issues/detail?id=190966
if (canShowQrCodeFragment()) showQrCodeFragment();
showQrCodeFragmentIfAllowed();
}
private boolean canShowQrCodeFragment() {
return isResumed && continueClicked
&& (SDK_INT < 23 || gotCameraPermission)
&& bluetoothState != BluetoothState.UNKNOWN
&& bluetoothState != BluetoothState.WAITING;
private void showQrCodeFragmentIfAllowed() {
if (isResumed && continueClicked && areEssentialPermissionsGranted()) {
if (bluetoothState == BluetoothState.UNKNOWN ||
bluetoothState == BluetoothState.ENABLED) {
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
@@ -132,50 +175,54 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
@Override
public void showNextScreen() {
continueClicked = true;
if (checkPermissions()) {
if (shouldRequestEnableBluetooth()) requestEnableBluetooth();
else if (canShowQrCodeFragment()) showQrCodeFragment();
}
if (checkPermissions()) showQrCodeFragmentIfAllowed();
}
private boolean shouldRequestEnableBluetooth() {
return bluetoothState == BluetoothState.UNKNOWN
|| bluetoothState == BluetoothState.REFUSED;
}
private void requestEnableBluetooth() {
private void requestBluetoothDiscoverable() {
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
if (bt == null) {
setBluetoothState(BluetoothState.NO_ADAPTER);
} else if (bt.isEnabled()) {
setBluetoothState(BluetoothState.ENABLED);
} else {
enableWasRequested = true;
setBluetoothState(BluetoothState.WAITING);
Intent i = new Intent(ACTION_REQUEST_ENABLE);
startActivityForResult(i, REQUEST_ENABLE_BLUETOOTH);
wasAdapterEnabled = bt.isEnabled();
Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
startActivityForResult(i, REQUEST_BLUETOOTH_DISCOVERABLE);
}
}
private void setBluetoothState(BluetoothState bluetoothState) {
LOG.info("Setting Bluetooth state to " + bluetoothState);
this.bluetoothState = bluetoothState;
if (enableWasRequested && bluetoothState == BluetoothState.ENABLED) {
if (!wasAdapterEnabled && bluetoothState == BluetoothState.ENABLED) {
eventBus.broadcast(new BluetoothEnabledEvent());
enableWasRequested = false;
wasAdapterEnabled = true;
}
if (canShowQrCodeFragment()) showQrCodeFragment();
showQrCodeFragmentIfAllowed();
}
@Override
public void onActivityResult(int request, int result, Intent data) {
// If the request was granted we'll catch the state change event
if (request == REQUEST_ENABLE_BLUETOOTH && result == RESULT_CANCELED)
setBluetoothState(BluetoothState.REFUSED);
if (request == REQUEST_BLUETOOTH_DISCOVERABLE) {
if (result == RESULT_CANCELED) {
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() {
// If we return to the intro fragment, the continue button needs to be
// clicked again before showing the QR code fragment
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
FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentByTag(KeyAgreementFragment.TAG) == null) {
@@ -194,74 +241,113 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
}
private boolean checkPermissions() {
if (ContextCompat.checkSelfPermission(this, CAMERA) !=
PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
CAMERA)) {
OnClickListener continueListener =
(dialog, which) -> requestPermission();
Builder builder = new Builder(this, style.BriarDialogTheme);
builder.setTitle(string.permission_camera_title);
builder.setMessage(string.permission_camera_request_body);
builder.setNeutralButton(string.continue_button,
continueListener);
builder.show();
} else {
requestPermission();
}
gotCameraPermission = false;
if (areEssentialPermissionsGranted()) return true;
// If the camera permission has been permanently denied, ask the
// user to change the setting
if (cameraPermission == Permission.PERMANENTLY_DENIED) {
Builder builder = new Builder(this, R.style.BriarDialogTheme);
builder.setTitle(R.string.permission_camera_title);
builder.setMessage(R.string.permission_camera_denied_body);
builder.setPositiveButton(R.string.ok,
UiUtils.getGoToSettingsListener(this));
builder.setNegativeButton(R.string.cancel,
(dialog, which) -> supportFinishAfterTransition());
builder.show();
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() {
ActivityCompat.requestPermissions(this, new String[] {CAMERA},
REQUEST_PERMISSION_CAMERA);
private void showRationale(@StringRes int title, @StringRes int body) {
Builder builder = new Builder(this, R.style.BriarDialogTheme);
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
@UiThread
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
if (requestCode == REQUEST_PERMISSION_CAMERA) {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 &&
grantResults[0] == PERMISSION_GRANTED) {
gotCameraPermission = true;
showNextScreen();
} else {
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
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();
}
}
String[] permissions, int[] grantResults) {
if (requestCode != REQUEST_PERMISSION_CAMERA_LOCATION)
throw new AssertionError();
if (gotPermission(CAMERA, permissions, grantResults)) {
cameraPermission = Permission.GRANTED;
} else if (shouldShowRationale(CAMERA)) {
cameraPermission = Permission.SHOW_RATIONALE;
} else {
cameraPermission = Permission.PERMANENTLY_DENIED;
}
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 {
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(EXTRA_STATE, 0);
if (state == STATE_ON) setBluetoothState(BluetoothState.ENABLED);
else setBluetoothState(BluetoothState.UNKNOWN);
String action = intent.getAction();
if (ACTION_STATE_CHANGED.equals(action)) {
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 org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R;
@@ -22,14 +22,14 @@ class GroupMessageItem extends ThreadItem {
private GroupMessageItem(MessageId messageId, GroupId groupId,
@Nullable MessageId parentId, String text, long timestamp,
Author author, Status status, boolean isRead) {
Author author, AuthorInfo status, boolean isRead) {
super(messageId, parentId, text, timestamp, author, status, isRead);
this.groupId = groupId;
}
GroupMessageItem(GroupMessageHeader h, String text) {
this(h.getId(), h.getGroupId(), h.getParentId(), text, h.getTimestamp(),
h.getAuthor(), h.getAuthorStatus(), h.isRead());
h.getAuthor(), h.getAuthorInfo(), h.isRead());
}
public GroupId getGroupId() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -44,6 +44,7 @@ import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.android.util.UiUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.logging.Logger;
@@ -210,6 +211,13 @@ public class SettingsFragment extends PreferenceFragmentCompat
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) {
findPreference("pref_key_explode").setOnPreferenceClickListener(
preference -> {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -63,10 +63,10 @@
<item>@string/pref_theme_auto</item>
<item>@string/pref_theme_system</item>
</string-array>
<string name="pref_theme_light_value">light</string>
<string name="pref_theme_dark_value">dark</string>
<string name="pref_theme_auto_value">auto</string>
<string name="pref_theme_system_value">system</string>
<string name="pref_theme_light_value" translatable="false">light</string>
<string name="pref_theme_dark_value" translatable="false">dark</string>
<string name="pref_theme_auto_value" translatable="false">auto</string>
<string name="pref_theme_system_value" translatable="false">system</string>
<string-array name="pref_theme_values">
<item>@string/pref_theme_light_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_60</item>
</string-array>
<string name="pref_lock_timeout_value_default">5</string>
<string name="pref_lock_timeout_value_never">-1</string>
<string name="pref_lock_timeout_value_default" translatable="false">5</string>
<string name="pref_lock_timeout_value_never" translatable="false">-1</string>
<string-array name="pref_key_lock_timeout_values">
<item>@string/pref_lock_timeout_value_never</item>
<item>1</item>

View File

@@ -127,6 +127,9 @@
<string name="date_no_private_messages">No messages.</string>
<string name="no_private_messages">No messages to show</string>
<string name="message_hint">Type message</string>
<string name="set_contact_alias">Set alias name</string>
<string name="set_contact_alias_hint">Contact alias</string>
<string name="set_alias">Set Alias</string>
<string name="delete_contact">Delete contact</string>
<string name="dialog_title_delete_contact">Confirm Contact Deletion</string>
<string name="dialog_message_delete_contact">Are you sure that you want to remove this contact and all messages exchanged with this contact?</string>
@@ -468,8 +471,11 @@
<!-- Permission Requests -->
<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_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_toast">Camera permission was not granted</string>
<string name="qr_code">QR code</string>
<string name="show_qr_code_fullscreen">Show QR code fullscreen</string>

View File

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

View File

@@ -3,6 +3,7 @@ dependencyVerification {
'android.arch.core:common:1.1.1:common-1.1.1.jar:3a616a32f433e9e23f556b38575c31b013613d3ae85206263b7625fe1f4c151a',
'android.arch.core:runtime:1.1.1:runtime-1.1.1.aar:c3215aa5873311b3f88a6f4e4a3c25ad89971bc127de8c3e1291c57f93a05c39',
'android.arch.lifecycle:common:1.1.1:common-1.1.1.jar:8d378e88ebd5189e09eef623414812c868fd90aa519d6160e2311fb8b81cff56',
'android.arch.lifecycle:extensions:1.1.1:extensions-1.1.1.aar:429426b2feec2245ffc5e75b3b5309bedb36159cf06dc71843ae43526ac289b6',
'android.arch.lifecycle:livedata-core:1.1.1:livedata-core-1.1.1.aar:d6fdd8b985d6178d7ea2f16986a24e83f1bee936b74d43167c69e08d3cc12c50',
'android.arch.lifecycle:livedata:1.1.1:livedata-1.1.1.aar:50ab0490c1ff1a7cfb4e554032998b080888946d0dd424f39900efc4a1bcd750',
'android.arch.lifecycle:runtime:1.1.1:runtime-1.1.1.aar:c4e4be66c1b2f0abec593571454e1de14013f7e0f96bf2a9f212931a48cae550',
@@ -53,34 +54,34 @@ dependencyVerification {
'com.android.support:transition:28.0.0:transition-28.0.0.aar:45d09fc51284c17bbab300f5122512ac7d7348a6d23bda2051648bbe76cc9aa5',
'com.android.support:versionedparcelable:28.0.0:versionedparcelable-28.0.0.aar:60eb1cb08f71b65c3f6123135e03ebeb5930b5e126e1e5b2ac91b386908c9d02',
'com.android.support:viewpager:28.0.0:viewpager-28.0.0.aar:013c4c53058758ec104dbae970be58159f75dfe342ba8b937d15ff5282e35ffc',
'com.android.tools.analytics-library:protos:26.2.0:protos-26.2.0.jar:306784ac579be37f64f110204ea3fad8568e09e10817bb574bac0fc71b6402c5',
'com.android.tools.analytics-library:shared:26.2.0:shared-26.2.0.jar:10782c593b7edcaf63cf79f7179c4652fee1c01298e0d81d888eea5ea1c320d4',
'com.android.tools.analytics-library:tracker:26.2.0:tracker-26.2.0.jar:74e3cf587bcd6f5f14a6cddb8123c6940baf9947582f5356c25dd852c4726564',
'com.android.tools.build:aapt2:3.2.0-4818971:aapt2-3.2.0-4818971-linux.jar:0d86e045e9eb009ab8add53cf0e5c0f48674e8c8a4edd3b19fc978ea38508bb1',
'com.android.tools.build:apksig:3.2.0:apksig-3.2.0.jar:d393b14a2f85a4052645fa0b59e111b46dea0fd59a934bec6cf1e997a0640e30',
'com.android.tools.build:apkzlib:3.2.0:apkzlib-3.2.0.jar:d8a3404d63a1037207bc2eb472209c606f8143d201f484da9dacd04ffa538c31',
'com.android.tools.build:builder-model:3.2.0:builder-model-3.2.0.jar:7deb28465f2303fdc9df58ba83d080193edefe1624784d3ce9e0a0949ae05a49',
'com.android.tools.build:builder-test-api:3.2.0:builder-test-api-3.2.0.jar:b7c6676da0793240f16874c4008f12ed3ed1bc56a4a73f5e760044024ac9a02a',
'com.android.tools.build:builder:3.2.0:builder-3.2.0.jar:191df45703f615a02db16ce5ce78489f1ec91f9b83e677bec5de29de765e326d',
'com.android.tools.build:gradle-api:3.2.0:gradle-api-3.2.0.jar:5645c5ee4138b1354b24bed43e005964296a5f1ef48b6772ed9800159dcc2830',
'com.android.tools.build:manifest-merger:26.2.0:manifest-merger-26.2.0.jar:b821830bf067852688673cae01fb571c04da094985a3894878070b50f0587edf',
'com.android.tools.ddms:ddmlib:26.2.0:ddmlib-26.2.0.jar:ff9b6951698a6ec005b25e5e3b5f7fe960f566131ba8fcdf713392e10cc2dd50',
'com.android.tools.external.com-intellij:intellij-core:26.2.0:intellij-core-26.2.0.jar:03724f5aff30c8fff3d0b0c05e71f8dc21f246952fe1f3b5916bfb594e337bad',
'com.android.tools.external.com-intellij:kotlin-compiler:26.2.0:kotlin-compiler-26.2.0.jar:2f82e0c2687afe7c4941bdf6702d9a16d71d030a7ddb384a04a0537a361f4bb1',
'com.android.tools.external.org-jetbrains:uast:26.2.0:uast-26.2.0.jar:a439cafcddb2963620fd737005e3ce0560313362ede37e6c6eac5f10ff19eb94',
'com.android.tools.layoutlib:layoutlib-api:26.2.0:layoutlib-api-26.2.0.jar:5987488847729bad6a1e72cd5dcc46b68b61d8f05ef60858b87ed1d3b9bacc58',
'com.android.tools.lint:lint-api:26.2.0:lint-api-26.2.0.jar:aa4dc0399b6ae602286b9b106c661b50f6a8e1143c5949d3e7fd93c4a4d1513d',
'com.android.tools.lint:lint-checks:26.2.0:lint-checks-26.2.0.jar:c8c8dfdbd5c97a371e239b6000f4c6929bce315c2b13e690aaf34e3fcb045520',
'com.android.tools.lint:lint-gradle-api:26.2.0:lint-gradle-api-26.2.0.jar:befc1ad242e5e89e8977b2f799c465fee1b096ac645eba659acab832980599cd',
'com.android.tools.lint:lint-gradle:26.2.0:lint-gradle-26.2.0.jar:332d257f20537020fd268468f678ccfbcc0be494fa50526b63c532836dc7c5dd',
'com.android.tools.lint:lint-kotlin:26.2.0:lint-kotlin-26.2.0.jar:c149e09434aa24a6f7161dbd4b09f08c46f018cb0ee1ee2d1a32bf3b5015e081',
'com.android.tools.lint:lint:26.2.0:lint-26.2.0.jar:4df411ea5ef106780073c6e953ab591a55d52e94c9b1cd2f99970a124eca405e',
'com.android.tools:annotations:26.2.0:annotations-26.2.0.jar:af775f75168acbadb2ff61d6955e4fbeca9b1bd9f7a40b54ab7a489e30af45bb',
'com.android.tools:common:26.2.0:common-26.2.0.jar:3e60e91bc7c7a634bbfae3522c5e0b49e8a268a2d69904bed4bd55833f8bb0c9',
'com.android.tools:dvlib:26.2.0:dvlib-26.2.0.jar:c4d8fac5b8432e3676256ece4e25c3e55e8ef2da4298fb060cc6086d7e2235fc',
'com.android.tools:repository:26.2.0:repository-26.2.0.jar:9c9e320195e7e6d9bbdf9b5b0ed1d7e4da3625ad344902cb47b32c9ef8306d82',
'com.android.tools:sdk-common:26.2.0:sdk-common-26.2.0.jar:1e32745633177b28419041d9bd182bb99b51d1a7ad3f9b1c8b5292ed799656cd',
'com.android.tools:sdklib:26.2.0:sdklib-26.2.0.jar:08e2715f9abd3870df8e311f77ff6f3dae61193c1f6a2de78bf2e49c6ba4725a',
'com.android.tools.analytics-library:protos:26.2.1:protos-26.2.1.jar:2f371f5b1f551e85ab08be4d6a2873471b3d44afd1ebf6aa3298f3b796bf691f',
'com.android.tools.analytics-library:shared:26.2.1:shared-26.2.1.jar:4c1e4e705fa4d45f23aaea230557f6508155012d9c296337787c1d7b26a97f5a',
'com.android.tools.analytics-library:tracker:26.2.1:tracker-26.2.1.jar:4a624ecc976539f755ddb0bb8dfc2dd3d08326cfec59a098dbd70f701ca7fb75',
'com.android.tools.build:aapt2:3.2.1-4818971:aapt2-3.2.1-4818971-linux.jar:f431b6f96c91a2c155144b091a9c97d9805c589fe8efc9c930b6cd346cb60a1e',
'com.android.tools.build:apksig:3.2.1:apksig-3.2.1.jar:2b46f2feffea66037aab29e4261b2433c190194a6ef97b958511eb157f2ccba5',
'com.android.tools.build:apkzlib:3.2.1:apkzlib-3.2.1.jar:c39ad0313905932431fe81c8899c2cf39a4d92ad6c4edcaa4b25432f461452aa',
'com.android.tools.build:builder-model:3.2.1:builder-model-3.2.1.jar:a9f68e6abcec122f9cb5ad352d3f05a3eb03acbcdca95e4d25c16310c2c965ff',
'com.android.tools.build:builder-test-api:3.2.1:builder-test-api-3.2.1.jar:533ac6c2b5884bb54967a33791f2628dfdfac7981af39417a333b43d4379b6be',
'com.android.tools.build:builder:3.2.1:builder-3.2.1.jar:aedcbfd115dbe91d09b4113e66ef50589b558d0aa3b2f133b1d867c9b87fae83',
'com.android.tools.build:gradle-api:3.2.1:gradle-api-3.2.1.jar:57cf0ac5ac1dca8afdb3f62b94265e776e7dcfa641cc3844fb53a05193de208d',
'com.android.tools.build:manifest-merger:26.2.1:manifest-merger-26.2.1.jar:8830573263361035d38cfdcb51e2db94029c93865b21334f5fbf8a27984281a6',
'com.android.tools.ddms:ddmlib:26.2.1:ddmlib-26.2.1.jar:a4bf0a29a19980bf27269465cc782064656750b77c26728f82f9e148b705218b',
'com.android.tools.external.com-intellij:intellij-core:26.2.1:intellij-core-26.2.1.jar:4925ad1892c2687cb1a63427d440ef519c8c59215fefe0dc5d541d5d411fcafe',
'com.android.tools.external.com-intellij:kotlin-compiler:26.2.1:kotlin-compiler-26.2.1.jar:daa064fd708f340ee25fb9823c4c74104ac77f1370b76d907eb9ae6daec0a2ae',
'com.android.tools.external.org-jetbrains:uast:26.2.1:uast-26.2.1.jar:f10f7258d2ab9189562cc0f9ad838c0378fdba439229173390a99de02ebac75b',
'com.android.tools.layoutlib:layoutlib-api:26.2.1:layoutlib-api-26.2.1.jar:ddbf4fca123733fa011595b1cc1f4ac2937ed327b60990711fafc33c775c2ade',
'com.android.tools.lint:lint-api:26.2.1:lint-api-26.2.1.jar:3b57e739de567b98bc9ab56c2c0ee66fc026b4adf5843e8f9804ca0666a6f66e',
'com.android.tools.lint:lint-checks:26.2.1:lint-checks-26.2.1.jar:c86f4cc9aaee722ee4ad70062f7b5af91e9b041914af27adc09f545ab0fb3bc6',
'com.android.tools.lint:lint-gradle-api:26.2.1:lint-gradle-api-26.2.1.jar:2283e7af32e301565f2a797e531f0fc8c648077d457afb3ffdddbee638976c2f',
'com.android.tools.lint:lint-gradle:26.2.1:lint-gradle-26.2.1.jar:8fd90b2f3ec788cbb9801c07ab3e1ea2255aa31a6093157d7ea0ff13d0315ecb',
'com.android.tools.lint:lint-kotlin:26.2.1:lint-kotlin-26.2.1.jar:7a6a5d2b18f69cf1b900d857c2632b4c683713c533295933b8b759f8cab4a877',
'com.android.tools.lint:lint:26.2.1:lint-26.2.1.jar:7848b82ae988b90dee259ae7c7e86e05cbf52db6cd21c8bbd38ce7df08f3f8c5',
'com.android.tools:annotations:26.2.1:annotations-26.2.1.jar:7391c6a1e080174b96e64ceb078dadd31ce4d8a2d2fee0ec65be202126f90f24',
'com.android.tools:common:26.2.1:common-26.2.1.jar:a50aab2d6411ff68f4004a87c7e93d87d8e980a0ec3b352246549897ea2d78e5',
'com.android.tools:dvlib:26.2.1:dvlib-26.2.1.jar:72a83bf2839b1df9b1fbf67ba45d1bfb9f966cd774da4320c762b2be8f1688aa',
'com.android.tools:repository:26.2.1:repository-26.2.1.jar:fa74dae09103faef703df38550ad8fa244c5b6d1bf90d6198be932292b3d9cc1',
'com.android.tools:sdk-common:26.2.1:sdk-common-26.2.1.jar:759d4b292ca69a35cf961fca377b54158fc6c88108978006999442e80a011cf4',
'com.android.tools:sdklib:26.2.1:sdklib-26.2.1.jar:248df7ad5eac4aeb6f96c394c76760de4b7b89ac056e54d0c21a739368b91b45',
'com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.0:accessibility-test-framework-2.0.jar:cdf16ef8f5b8023d003ce3cc1b0d51bda737762e2dab2fedf43d1c4292353f7f',
'com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.1:accessibility-test-framework-2.1.jar:7b0aa6ed7553597ce0610684a9f7eca8021eee218f2e2f427c04a7fbf5f920bd',
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
@@ -161,9 +162,10 @@ dependencyVerification {
'org.hamcrest:hamcrest-integration:1.3:hamcrest-integration-1.3.jar:70f418efbb506c5155da5f9a5a33262ea08a9e4d7fea186aa9015c41a7224ac2',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.jetbrains.kotlin:kotlin-reflect:1.2.0:kotlin-reflect-1.2.0.jar:4f48a872bad6e4d9c053f4ad610d11e4012ad7e58dc19a03dd5eb811f36069dd',
'org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0:kotlin-stdlib-jre7-1.2.0.jar:c7a20fb951d437797afe8980aff6c1e5a03f310c661ba58ba1d4fa90cb0f2926',
'org.jetbrains.kotlin:kotlin-stdlib-jre8:1.2.0:kotlin-stdlib-jre8-1.2.0.jar:633524eee6ef1941f7cb1dab7ee3927b0a221ceee9047aeb5515f4cbb990c82a',
'org.jetbrains.kotlin:kotlin-stdlib:1.2.0:kotlin-stdlib-1.2.0.jar:05cfd9f5ac0b41910703a8925f7211a495909b27a2ffdd1c5106f1689aeafcd4',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71:kotlin-stdlib-common-1.2.71.jar:63999687ff2fce8a592dd180ffbbf8f1d21c26b4044c55cdc74ff3cf3b3cf328',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71:kotlin-stdlib-jdk7-1.2.71.jar:b136bd61b240e07d4d92ce00d3bd1dbf584400a7bf5f220c2f3cd22446858082',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71:kotlin-stdlib-jdk8-1.2.71.jar:ac3c8abf47790b64b4f7e2509a53f0c145e061ac1612a597520535d199946ea9',
'org.jetbrains.kotlin:kotlin-stdlib:1.2.71:kotlin-stdlib-1.2.71.jar:4c895c270b87f5fec2a2796e1d89c15407ee821de961527c28588bb46afbc68b',
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,7 +20,7 @@ import org.junit.Test;
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.test.TestUtils.getRandomBytes;
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"));
final ContactId contactId = new ContactId(5);
final Contact contact = new Contact(contactId, author,
new AuthorId(getRandomId()), true, true);
new AuthorId(getRandomId()), getRandomString(5), true, true);
final InviteMessage inviteMessage =
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 Author author = getAuthor();
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 contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Group privateGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
@@ -845,9 +845,9 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
@Test
public void testRemovingGroupEndsSessions() throws Exception {
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,
author.getId(), true, true);
author.getId(), getRandomString(5), true, true);
Collection<Contact> contacts =
Arrays.asList(contact, contact2, contact3);

View File

@@ -331,7 +331,7 @@ public class InviteeProtocolEngineTest extends AbstractProtocolEngineTest {
signature);
Author notCreator = getAuthor();
Contact notCreatorContact = new Contact(contactId, notCreator,
localAuthor.getId(), true, true);
localAuthor.getId(), getRandomString(5), true, true);
expectGetContactId();
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.getMessage;
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.MAJOR_VERSION;
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 Author author = getAuthor();
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 =
Collections.singletonList(contact);
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
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
`GET /messages/{contactId}`
`GET /v1/messages/{contactId}`
The `{contactId}` is the `contactId` of the contact (`1` in the example above).
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
`POST /messages/{contactId}`
`POST /v1/messages/{contactId}`
The text of the message should be posted as JSON:

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