Compare commits

..

28 Commits

Author SHA1 Message Date
akwizgran
5f36ba9014 Fix tests. 2018-12-06 18:05:03 +00:00
akwizgran
438d200afe Set paths on blocks after hashing. 2018-12-06 17:43:46 +00:00
akwizgran
bd9ebe75a0 Add initial stream hashing implementation. 2018-12-06 17:43:46 +00:00
akwizgran
4b04e6a21d Add basic hash tree API and implementation. 2018-12-06 17:43:46 +00:00
akwizgran
f915eb4d36 Add BlockSink interface, using temporary message ID. 2018-12-06 17:43:41 +00:00
akwizgran
a5c9e7c74d Merge branch '1242-display-image-attachments-fullscreen' into 'master'
Add ImageActivity to show image attachment in full-screen

See merge request briar/briar!999
2018-11-30 18:04:55 +00:00
Torsten Grote
8a4a343147 [android] Move image to the top if it is overlapping the toolbar 2018-11-30 15:53:38 -02:00
Torsten Grote
7b22d3b84d [android] Address review issues for image fullscreen view 2018-11-28 17:26:01 -02:00
Torsten Grote
c8fa23273f [android] support pull down to dismiss pattern for ImageActivity 2018-11-28 17:26:01 -02:00
Torsten Grote
fbe5df8938 [android] Add ImageActivity to show images in full-screen 2018-11-28 17:26:01 -02:00
akwizgran
008cf95741 Merge branch '1467-conversation-scrolling' into 'master'
Only scroll conversation list to bottom, when already at bottom

Closes #1467

See merge request briar/briar!1000
2018-11-27 09:32:05 +00:00
Torsten Grote
3eb066a836 [android] Use new IoUtils to close InputStreams 2018-11-26 16:28:06 -02:00
Torsten Grote
674b29af25 [android] static constant all caps 2018-11-26 16:23:51 -02:00
Torsten Grote
b8ca5ab557 [android] Only scroll conversation list to bottom, when already at bottom
Closes #1467
2018-11-26 16:23:17 -02:00
Torsten Grote
6e17709f46 Merge branch 'try-to-close' into 'master'
Move tryToClose() methods into utility classes

See merge request briar/briar!1002
2018-11-26 18:22:24 +00:00
akwizgran
726d90145c Merge branch '1242-display-image-attachments' into 'master'
[android] display image attachments for conversation messages

See merge request briar/briar!997
2018-11-26 17:19:37 +00:00
Torsten Grote
165211eb9b Merge branch '1259-headless-mac-os' into 'master'
Enable headless app to start on MacOS

See merge request briar/briar!1003
2018-11-26 12:01:27 +00:00
akwizgran
868c61e5d6 Move tryToClose() methods into utility classes. 2018-11-23 15:02:27 +00:00
Torsten Grote
798bb6d4f7 [android] scale thumbnails to minimum size, don't upscale to maximum size 2018-11-23 11:25:18 -02:00
akwizgran
bc352a2dc6 Enable Tor on Mac OS once binaries are available. 2018-11-23 13:07:12 +00:00
akwizgran
ce7d6d3db5 Code cleanup. 2018-11-23 12:56:34 +00:00
akwizgran
61276c81d2 Make it possible to start the headless app on MacOS.
The app is still non-functional because we don't have a Tor plugin.
2018-11-23 12:52:40 +00:00
Torsten Grote
152ac3df43 [android] improve bitmap transformation hashKey and DiskCacheKey 2018-11-20 11:49:21 -02:00
Torsten Grote
dd5ad86db8 [android] Use DataFetcherFactory to create data fetchers and allow cancelling loads 2018-11-20 11:49:21 -02:00
Torsten Grote
10e9fb308d [android] Display Image Attachements: Address first round of review comments 2018-11-19 20:35:07 -02:00
Torsten Grote
de8e95692a [android] support RTL languages when rounding thumbnail corners 2018-11-19 20:35:07 -02:00
Torsten Grote
d6b52cf4ec [android] Use our own BitmapTransformation for rounded image corners 2018-11-19 20:35:07 -02:00
Torsten Grote
8a839fb5e4 [android] display image attachments for conversation messages 2018-11-19 20:35:07 -02:00
116 changed files with 2747 additions and 788 deletions

View File

@@ -12,7 +12,7 @@ test:
script: script:
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest - ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom test --continue - ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom test
after_script: after_script:
# these file change every time but should not be cached # these file change every time but should not be cached

View File

@@ -18,8 +18,8 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.AndroidUtils; import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.util.IoUtils;
import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
@@ -51,7 +51,6 @@ import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress; import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@@ -161,11 +160,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
@Override @Override
void tryToClose(@Nullable BluetoothServerSocket ss) { void tryToClose(@Nullable BluetoothServerSocket ss) {
try { IoUtils.tryToClose(ss, LOG, WARNING);
if (ss != null) ss.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
} }
@Override @Override
@@ -195,7 +190,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
s.connect(); s.connect();
return wrapSocket(s); return wrapSocket(s);
} catch (IOException e) { } catch (IOException e) {
tryToClose(s); IoUtils.tryToClose(s, LOG, WARNING);
throw e; throw e;
} }
} }
@@ -268,14 +263,6 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
return addresses; return addresses;
} }
private void tryToClose(@Nullable Closeable c) {
try {
if (c != null) c.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private class BluetoothStateReceiver extends BroadcastReceiver { private class BluetoothStateReceiver extends BroadcastReceiver {
@Override @Override

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.system; package org.briarproject.bramble.system;
import android.annotation.SuppressLint;
import android.app.Application; import android.app.Application;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
@@ -26,7 +27,7 @@ import static android.provider.Settings.Secure.ANDROID_ID;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class AndroidSecureRandomProvider extends LinuxSecureRandomProvider { class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
private static final int SEED_LENGTH = 32; private static final int SEED_LENGTH = 32;
@@ -37,6 +38,7 @@ class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
appContext = app.getApplicationContext(); appContext = app.getApplicationContext();
} }
@SuppressLint("HardwareIds")
@Override @Override
protected void writeToEntropyPool(DataOutputStream out) throws IOException { protected void writeToEntropyPool(DataOutputStream out) throws IOException {
super.writeToEntropyPool(out); super.writeToEntropyPool(out);
@@ -49,12 +51,14 @@ class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
String id = Settings.Secure.getString(contentResolver, ANDROID_ID); String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
if (id != null) out.writeUTF(id); if (id != null) out.writeUTF(id);
Parcel parcel = Parcel.obtain(); Parcel parcel = Parcel.obtain();
WifiManager wm = WifiManager wm = (WifiManager) appContext.getApplicationContext()
(WifiManager) appContext.getSystemService(WIFI_SERVICE); .getSystemService(WIFI_SERVICE);
List<WifiConfiguration> configs = wm.getConfiguredNetworks(); if (wm != null) {
if (configs != null) { List<WifiConfiguration> configs = wm.getConfiguredNetworks();
for (WifiConfiguration config : configs) if (configs != null) {
parcel.writeParcelable(config, 0); for (WifiConfiguration config : configs)
parcel.writeParcelable(config, 0);
}
} }
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter(); BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
if (bt != null) { if (bt != null) {
@@ -77,13 +81,13 @@ class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html // Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
private void applyOpenSslFix() { private void applyOpenSslFix() {
byte[] seed = new LinuxSecureRandomSpi().engineGenerateSeed( byte[] seed = new UnixSecureRandomSpi().engineGenerateSeed(
SEED_LENGTH); SEED_LENGTH);
try { try {
// Seed the OpenSSL PRNG // Seed the OpenSSL PRNG
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto") Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_seed", byte[].class) .getMethod("RAND_seed", byte[].class)
.invoke(null, seed); .invoke(null, (Object) seed);
// Mix the output of the Linux PRNG into the OpenSSL PRNG // Mix the output of the Linux PRNG into the OpenSSL PRNG
int bytesRead = (Integer) Class.forName( int bytesRead = (Integer) Class.forName(
"org.apache.harmony.xnet.provider.jsse.NativeCrypto") "org.apache.harmony.xnet.provider.jsse.NativeCrypto")

View File

@@ -0,0 +1,20 @@
package org.briarproject.bramble.api.io;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.sync.tree.TreeHash;
import java.util.List;
public interface BlockSink {
/**
* Stores a block of the message with the given temporary ID.
*/
void putBlock(HashingId h, int blockNumber, byte[] data) throws DbException;
/**
* Sets the hash tree path of a previously stored block.
*/
void setPath(HashingId h, int blockNumber, List<TreeHash> path)
throws DbException;
}

View File

@@ -0,0 +1,27 @@
package org.briarproject.bramble.api.io;
import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.concurrent.ThreadSafe;
/**
* Type-safe wrapper for a byte array that uniquely identifies a
* {@link Message} while it's being hashed and the {@link MessageId} is not
* yet known.
*/
@ThreadSafe
@NotNullByDefault
public class HashingId extends UniqueId {
public HashingId(byte[] id) {
super(id);
}
@Override
public boolean equals(Object o) {
return o instanceof HashingId && super.equals(o);
}
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.api.sync; package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.tree.TreeHash;
@NotNullByDefault @NotNullByDefault
public interface MessageFactory { public interface MessageFactory {
@@ -10,4 +11,6 @@ public interface MessageFactory {
Message createMessage(byte[] raw); Message createMessage(byte[] raw);
byte[] getRawMessage(Message m); byte[] getRawMessage(Message m);
MessageId getMessageId(GroupId g, long timestamp, TreeHash rootHash);
} }

View File

@@ -24,6 +24,12 @@ public class MessageId extends UniqueId {
public static final String BLOCK_LABEL = public static final String BLOCK_LABEL =
"org.briarproject.bramble/MESSAGE_BLOCK"; "org.briarproject.bramble/MESSAGE_BLOCK";
/**
* Label for hashing two tree hashes to produce a parent.
*/
public static final String TREE_LABEL =
"org.briarproject.bramble/MESSAGE_TREE";
public MessageId(byte[] id) { public MessageId(byte[] id) {
super(id); super(id);
} }

View File

@@ -35,4 +35,9 @@ public interface SyncConstants {
* The maximum number of message IDs in an ack, offer or request record. * The maximum number of message IDs in an ack, offer or request record.
*/ */
int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_BYTES / UniqueId.LENGTH; int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_BYTES / UniqueId.LENGTH;
/**
* The maximum length of a message block in bytes.
*/
int MAX_BLOCK_LENGTH = 32 * 2014; // 32 KiB
} }

View File

@@ -0,0 +1,21 @@
package org.briarproject.bramble.api.sync.tree;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public class LeafNode extends TreeNode {
public LeafNode(TreeHash hash, int blockNumber) {
super(hash, 0, blockNumber, blockNumber);
}
@Override
public TreeNode getLeftChild() {
throw new UnsupportedOperationException();
}
@Override
public TreeNode getRightChild() {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,26 @@
package org.briarproject.bramble.api.sync.tree;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public class ParentNode extends TreeNode {
private final TreeNode left, right;
public ParentNode(TreeHash hash, TreeNode left, TreeNode right) {
super(hash, Math.max(left.getHeight(), right.getHeight()) + 1,
left.getFirstBlockNumber(), right.getLastBlockNumber());
this.left = left;
this.right = right;
}
@Override
public TreeNode getLeftChild() {
return left;
}
@Override
public TreeNode getRightChild() {
return right;
}
}

View File

@@ -0,0 +1,21 @@
package org.briarproject.bramble.api.sync.tree;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.io.BlockSink;
import org.briarproject.bramble.api.io.HashingId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException;
import java.io.InputStream;
@NotNullByDefault
public interface StreamHasher {
/**
* Reads the given input stream, divides the data into blocks, stores
* the blocks and the resulting hash tree using the given block sink and
* temporary ID, and returns the hash tree.
*/
TreeNode hash(InputStream in, BlockSink sink, HashingId h)
throws IOException, DbException;
}

View File

@@ -0,0 +1,24 @@
package org.briarproject.bramble.api.sync.tree;
import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.ThreadSafe;
/**
* Type-safe wrapper for a byte array that uniquely identifies a sequence of
* one or more message blocks.
*/
@ThreadSafe
@NotNullByDefault
public class TreeHash extends UniqueId {
public TreeHash(byte[] id) {
super(id);
}
@Override
public boolean equals(Object o) {
return o instanceof TreeHash && super.equals(o);
}
}

View File

@@ -0,0 +1,11 @@
package org.briarproject.bramble.api.sync.tree;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface TreeHasher {
LeafNode hashBlock(int blockNumber, byte[] data);
ParentNode mergeTrees(TreeNode left, TreeNode right);
}

View File

@@ -0,0 +1,38 @@
package org.briarproject.bramble.api.sync.tree;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public abstract class TreeNode {
private final TreeHash hash;
private final int height, firstBlockNumber, lastBlockNumber;
TreeNode(TreeHash hash, int height, int firstBlockNumber,
int lastBlockNumber) {
this.hash = hash;
this.height = height;
this.firstBlockNumber = firstBlockNumber;
this.lastBlockNumber = lastBlockNumber;
}
public TreeHash getHash() {
return hash;
}
public int getHeight() {
return height;
}
public int getFirstBlockNumber() {
return firstBlockNumber;
}
public int getLastBlockNumber() {
return lastBlockNumber;
}
public abstract TreeNode getLeftChild();
public abstract TreeNode getRightChild();
}

View File

@@ -8,12 +8,15 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault @NotNullByDefault
public class IoUtils { public class IoUtils {
@@ -54,16 +57,35 @@ public class IoUtils {
out.flush(); out.flush();
out.close(); out.close();
} catch (IOException e) { } catch (IOException e) {
tryToClose(in); tryToClose(in, LOG, WARNING);
tryToClose(out); tryToClose(out, LOG, WARNING);
} }
} }
private static void tryToClose(@Nullable Closeable c) { public static void tryToClose(@Nullable Closeable c, Logger logger,
Level level) {
try { try {
if (c != null) c.close(); if (c != null) c.close();
} catch (IOException e) { } catch (IOException e) {
// We did our best logException(logger, level, e);
}
}
public static void tryToClose(@Nullable Socket s, Logger logger,
Level level) {
try {
if (s != null) s.close();
} catch (IOException e) {
logException(logger, level, e);
}
}
public static void tryToClose(@Nullable ServerSocket ss, Logger logger,
Level level) {
try {
if (ss != null) ss.close();
} catch (IOException e) {
logException(logger, level, e);
} }
} }

View File

@@ -10,8 +10,6 @@ public class OsUtils {
@Nullable @Nullable
private static final String os = System.getProperty("os.name"); private static final String os = System.getProperty("os.name");
@Nullable @Nullable
private static final String version = System.getProperty("os.version");
@Nullable
private static final String vendor = System.getProperty("java.vendor"); private static final String vendor = System.getProperty("java.vendor");
public static boolean isWindows() { public static boolean isWindows() {

View File

@@ -15,16 +15,23 @@ import java.sql.DriverManager;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.Properties; import java.util.Properties;
import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
/** /**
* Contains all the H2-specific code for the database. * Contains all the H2-specific code for the database.
*/ */
@NotNullByDefault @NotNullByDefault
class H2Database extends JdbcDatabase { class H2Database extends JdbcDatabase {
private static final Logger LOG = getLogger(H2Database.class.getName());
private static final String HASH_TYPE = "BINARY(32)"; private static final String HASH_TYPE = "BINARY(32)";
private static final String SECRET_TYPE = "BINARY(32)"; private static final String SECRET_TYPE = "BINARY(32)";
private static final String BINARY_TYPE = "BINARY"; private static final String BINARY_TYPE = "BINARY";
@@ -121,8 +128,8 @@ class H2Database extends JdbcDatabase {
s.close(); s.close();
c.close(); c.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(s); tryToClose(s, LOG, WARNING);
tryToClose(c); tryToClose(c, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }

View File

@@ -14,16 +14,24 @@ import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
/** /**
* Contains all the HSQLDB-specific code for the database. * Contains all the HSQLDB-specific code for the database.
*/ */
@NotNullByDefault @NotNullByDefault
class HyperSqlDatabase extends JdbcDatabase { class HyperSqlDatabase extends JdbcDatabase {
private static final Logger LOG =
getLogger(HyperSqlDatabase.class.getName());
private static final String HASH_TYPE = "BINARY(32)"; private static final String HASH_TYPE = "BINARY(32)";
private static final String SECRET_TYPE = "BINARY(32)"; private static final String SECRET_TYPE = "BINARY(32)";
private static final String BINARY_TYPE = "BINARY"; private static final String BINARY_TYPE = "BINARY";
@@ -72,8 +80,8 @@ class HyperSqlDatabase extends JdbcDatabase {
s.close(); s.close();
c.close(); c.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(s); tryToClose(s, LOG, WARNING);
tryToClose(c); tryToClose(c, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -122,8 +130,8 @@ class HyperSqlDatabase extends JdbcDatabase {
s.close(); s.close();
c.close(); c.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(s); tryToClose(s, LOG, WARNING);
tryToClose(c); tryToClose(c, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }

View File

@@ -72,6 +72,7 @@ import static org.briarproject.bramble.db.DatabaseConstants.LAST_COMPACTED_KEY;
import static org.briarproject.bramble.db.DatabaseConstants.MAX_COMPACTION_INTERVAL_MS; import static org.briarproject.bramble.db.DatabaseConstants.MAX_COMPACTION_INTERVAL_MS;
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY; import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry; import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
@@ -458,30 +459,6 @@ abstract class JdbcDatabase implements Database<Connection> {
mergeSettings(txn, s, DB_SETTINGS_NAMESPACE); mergeSettings(txn, s, DB_SETTINGS_NAMESPACE);
} }
private void tryToClose(@Nullable ResultSet rs) {
try {
if (rs != null) rs.close();
} catch (SQLException e) {
logException(LOG, WARNING, e);
}
}
protected void tryToClose(@Nullable Statement s) {
try {
if (s != null) s.close();
} catch (SQLException e) {
logException(LOG, WARNING, e);
}
}
protected void tryToClose(@Nullable Connection c) {
try {
if (c != null) c.close();
} catch (SQLException e) {
logException(LOG, WARNING, e);
}
}
private void createTables(Connection txn) throws DbException { private void createTables(Connection txn) throws DbException {
Statement s = null; Statement s = null;
try { try {
@@ -502,7 +479,7 @@ abstract class JdbcDatabase implements Database<Connection> {
s.executeUpdate(dbTypes.replaceTypes(CREATE_INCOMING_KEYS)); s.executeUpdate(dbTypes.replaceTypes(CREATE_INCOMING_KEYS));
s.close(); s.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(s); tryToClose(s, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -519,7 +496,7 @@ abstract class JdbcDatabase implements Database<Connection> {
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TIMESTAMP); s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TIMESTAMP);
s.close(); s.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(s); tryToClose(s, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -566,11 +543,7 @@ abstract class JdbcDatabase implements Database<Connection> {
} catch (SQLException e) { } catch (SQLException e) {
// Try to close the connection // Try to close the connection
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
try { tryToClose(txn, LOG, WARNING);
txn.close();
} catch (SQLException e1) {
logException(LOG, WARNING, e1);
}
// Whatever happens, allow the database to close // Whatever happens, allow the database to close
connectionsLock.lock(); connectionsLock.lock();
try { try {
@@ -659,8 +632,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return c; return c;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -681,7 +654,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -704,7 +677,7 @@ abstract class JdbcDatabase implements Database<Connection> {
// Create a status row for each message in the group // Create a status row for each message in the group
addStatus(txn, c, g, groupShared); addStatus(txn, c, g, groupShared);
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -735,8 +708,8 @@ abstract class JdbcDatabase implements Database<Connection> {
rs.close(); rs.close();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -761,7 +734,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -810,7 +783,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException(); if (affected < 0) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -840,8 +813,8 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -873,7 +846,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -915,8 +888,8 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -935,7 +908,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1015,8 +988,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return keySetId; return keySetId;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1039,8 +1012,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return found; return found;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1061,8 +1034,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return found; return found;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1083,8 +1056,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return found; return found;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1105,8 +1078,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return found; return found;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1127,8 +1100,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return found; return found;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1149,8 +1122,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return found; return found;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1174,8 +1147,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return found; return found;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1198,8 +1171,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return count; return count;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1223,7 +1196,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException(); if (affected < 0) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1240,7 +1213,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException(); if (affected < 0) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1273,8 +1246,8 @@ abstract class JdbcDatabase implements Database<Connection> {
return new Contact(c, author, localAuthorId, alias, verified, return new Contact(c, author, localAuthorId, alias, verified,
active); active);
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1310,8 +1283,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return contacts; return contacts;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1333,8 +1306,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return ids; return ids;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1371,8 +1344,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return contacts; return contacts;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1395,8 +1368,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return new Group(g, clientId, majorVersion, descriptor); return new Group(g, clientId, majorVersion, descriptor);
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1423,8 +1396,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return groups; return groups;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1449,8 +1422,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return v; return v;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1473,8 +1446,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return visible; return visible;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1505,8 +1478,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return localAuthor; return localAuthor;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1537,8 +1510,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return authors; return authors;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1566,8 +1539,8 @@ abstract class JdbcDatabase implements Database<Connection> {
System.arraycopy(raw, MESSAGE_HEADER_LENGTH, body, 0, body.length); System.arraycopy(raw, MESSAGE_HEADER_LENGTH, body, 0, body.length);
return new Message(m, g, timestamp, body); return new Message(m, g, timestamp, body);
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1590,8 +1563,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return ids; return ids;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1628,8 +1601,8 @@ abstract class JdbcDatabase implements Database<Connection> {
if (intersection == null) throw new AssertionError(); if (intersection == null) throw new AssertionError();
return intersection; return intersection;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1661,8 +1634,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return all; return all;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1696,8 +1669,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return metadata; return metadata;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1720,8 +1693,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return metadata; return metadata;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1746,8 +1719,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return metadata; return metadata;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1776,8 +1749,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return statuses; return statuses;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1807,8 +1780,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return status; return status;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1837,8 +1810,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return dependencies; return dependencies;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1868,8 +1841,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return dependents; return dependents;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1891,8 +1864,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return state; return state;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1916,8 +1889,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return ids; return ids;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1950,8 +1923,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return ids; return ids;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -1975,8 +1948,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return ids; return ids;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2014,8 +1987,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return ids; return ids;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2048,8 +2021,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return ids; return ids;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2076,8 +2049,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return ids; return ids;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2106,8 +2079,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return nextSendTime; return nextSendTime;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2145,8 +2118,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return ids; return ids;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2168,8 +2141,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return s; return s;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2234,8 +2207,8 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return keys; return keys;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2254,7 +2227,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2281,7 +2254,7 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2308,7 +2281,7 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2338,7 +2311,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (rows != 1) throw new DbStateException(); if (rows != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2383,8 +2356,8 @@ abstract class JdbcDatabase implements Database<Connection> {
if (rows != 1) throw new DbStateException(); if (rows != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2450,7 +2423,7 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
return added; return added;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2497,7 +2470,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (rows != 1) throw new DbStateException(); if (rows != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2516,7 +2489,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2535,7 +2508,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2554,7 +2527,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2571,7 +2544,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2587,7 +2560,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2615,7 +2588,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException(); if (affected < 0) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2632,7 +2605,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2648,7 +2621,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2667,7 +2640,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
return affected == 1; return affected == 1;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2692,7 +2665,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (rows != 1) throw new DbStateException(); if (rows != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2709,7 +2682,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2730,7 +2703,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException(); if (affected < 0) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2749,7 +2722,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2767,7 +2740,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2785,7 +2758,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2804,7 +2777,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2834,7 +2807,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException(); if (affected < 0) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2860,7 +2833,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException(); if (affected < 0) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2913,7 +2886,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0) throw new DbStateException(); if (affected < 0) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2936,7 +2909,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2955,7 +2928,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected < 0 || affected > 1) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -2991,8 +2964,8 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs, LOG, WARNING);
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }
@@ -3059,7 +3032,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (rows < 0 || rows > 1) throw new DbStateException(); if (rows < 0 || rows > 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps, LOG, WARNING);
throw new DbException(e); throw new DbException(e);
} }
} }

View File

@@ -0,0 +1,42 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class JdbcUtils {
static void tryToClose(@Nullable ResultSet rs, Logger logger, Level level) {
try {
if (rs != null) rs.close();
} catch (SQLException e) {
logException(logger, level, e);
}
}
static void tryToClose(@Nullable Statement s, Logger logger, Level level) {
try {
if (s != null) s.close();
} catch (SQLException e) {
logException(logger, level, e);
}
}
static void tryToClose(@Nullable Connection c, Logger logger, Level level) {
try {
if (c != null) c.close();
} catch (SQLException e) {
logException(logger, level, e);
}
}
}

View File

@@ -7,10 +7,8 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration38_39 implements Migration<Connection> { class Migration38_39 implements Migration<Connection> {
@@ -40,16 +38,8 @@ class Migration38_39 implements Migration<Connection> {
+ " ALTER COLUMN contactId" + " ALTER COLUMN contactId"
+ " SET NOT NULL"); + " SET NOT NULL");
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(s); tryToClose(s, LOG, WARNING);
throw new DbException(e); 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

@@ -7,10 +7,8 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration39_40 implements Migration<Connection> { class Migration39_40 implements Migration<Connection> {
@@ -39,16 +37,8 @@ class Migration39_40 implements Migration<Connection> {
+ " ALTER COLUMN eta" + " ALTER COLUMN eta"
+ " SET NOT NULL"); + " SET NOT NULL");
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(s); tryToClose(s, LOG, WARNING);
throw new DbException(e); 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

@@ -7,11 +7,9 @@ import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration40_41 implements Migration<Connection> { class Migration40_41 implements Migration<Connection> {
@@ -41,16 +39,8 @@ class Migration40_41 implements Migration<Connection> {
s.execute("ALTER TABLE contacts" s.execute("ALTER TABLE contacts"
+ dbTypes.replaceTypes(" ADD alias _STRING")); + dbTypes.replaceTypes(" ADD alias _STRING"));
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(s); tryToClose(s, LOG, WARNING);
throw new DbException(e); 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

@@ -4,12 +4,11 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.logging.Logger; import java.util.logging.Logger;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.IoUtils.tryToClose;
@NotNullByDefault @NotNullByDefault
class FileTransportReader implements TransportConnectionReader { class FileTransportReader implements TransportConnectionReader {
@@ -34,11 +33,7 @@ class FileTransportReader implements TransportConnectionReader {
@Override @Override
public void dispose(boolean exception, boolean recognised) { public void dispose(boolean exception, boolean recognised) {
try { tryToClose(in, LOG, WARNING);
in.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
plugin.readerFinished(file, exception, recognised); plugin.readerFinished(file, exception, recognised);
} }
} }

View File

@@ -4,12 +4,11 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.logging.Logger; import java.util.logging.Logger;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.IoUtils.tryToClose;
@NotNullByDefault @NotNullByDefault
class FileTransportWriter implements TransportConnectionWriter { class FileTransportWriter implements TransportConnectionWriter {
@@ -44,11 +43,7 @@ class FileTransportWriter implements TransportConnectionWriter {
@Override @Override
public void dispose(boolean exception) { public void dispose(boolean exception) {
try { tryToClose(out, LOG, WARNING);
out.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
plugin.writerFinished(file, exception); plugin.writerFinished(file, exception);
} }
} }

View File

@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import java.io.IOException; import java.io.IOException;
@@ -35,7 +36,6 @@ import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_IP_PORTS; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_IP_PORTS;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED; import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress; import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
@NotNullByDefault @NotNullByDefault
@@ -293,11 +293,7 @@ class LanTcpPlugin extends TcpPlugin {
@Override @Override
public void close() { public void close() {
try { IoUtils.tryToClose(ss, LOG, WARNING);
ss.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
} }
} }

View File

@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import java.io.IOException; import java.io.IOException;
@@ -153,13 +154,8 @@ abstract class TcpPlugin implements DuplexPlugin {
} }
protected void tryToClose(@Nullable ServerSocket ss) { protected void tryToClose(@Nullable ServerSocket ss) {
try { IoUtils.tryToClose(ss, LOG, WARNING);
if (ss != null) ss.close(); callback.transportDisabled();
} catch (IOException e) {
logException(LOG, WARNING, e);
} finally {
callback.transportDisabled();
}
} }
String getIpPortString(InetSocketAddress a) { String getIpPortString(InetSocketAddress a) {

View File

@@ -31,7 +31,6 @@ import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider; import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.util.IoUtils; import org.briarproject.bramble.util.IoUtils;
import java.io.Closeable;
import java.io.EOFException; import java.io.EOFException;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@@ -303,8 +302,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
IoUtils.copyAndClose(in, out); IoUtils.copyAndClose(in, out);
doneFile.createNewFile(); doneFile.createNewFile();
} catch (IOException e) { } catch (IOException e) {
tryToClose(in); IoUtils.tryToClose(in, LOG, WARNING);
tryToClose(out); IoUtils.tryToClose(out, LOG, WARNING);
throw new PluginException(e); throw new PluginException(e);
} }
} }
@@ -341,22 +340,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
return getClass().getClassLoader().getResourceAsStream("torrc"); return getClass().getClassLoader().getResourceAsStream("torrc");
} }
private void tryToClose(@Nullable Closeable c) {
try {
if (c != null) c.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private void tryToClose(@Nullable Socket s) {
try {
if (s != null) s.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private void listFiles(File f) { private void listFiles(File f) {
if (f.isDirectory()) { if (f.isDirectory()) {
File[] children = f.listFiles(); File[] children = f.listFiles();
@@ -378,7 +361,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
return b; return b;
} finally { } finally {
tryToClose(in); IoUtils.tryToClose(in, LOG, WARNING);
} }
} }
@@ -418,13 +401,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
private void tryToClose(@Nullable ServerSocket ss) { private void tryToClose(@Nullable ServerSocket ss) {
try { IoUtils.tryToClose(ss, LOG, WARNING);
if (ss != null) ss.close(); callback.transportDisabled();
} catch (IOException e) {
logException(LOG, WARNING, e);
} finally {
callback.transportDisabled();
}
} }
private void publishHiddenService(String port) { private void publishHiddenService(String port) {
@@ -593,7 +571,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
LOG.info("Could not connect to " + scrubOnion(bestOnion) LOG.info("Could not connect to " + scrubOnion(bestOnion)
+ ": " + e.toString()); + ": " + e.toString());
} }
tryToClose(s); IoUtils.tryToClose(s, LOG, WARNING);
return null; return null;
} }
} }

View File

@@ -12,7 +12,6 @@ import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.util.IoUtils; import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
@@ -26,13 +25,12 @@ import java.net.Socket;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import javax.net.SocketFactory; import javax.net.SocketFactory;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.IoUtils.tryToClose;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -66,7 +64,7 @@ class DevReporterImpl implements DevReporter, EventListener {
s.setSoTimeout(SOCKET_TIMEOUT); s.setSoTimeout(SOCKET_TIMEOUT);
return s; return s;
} catch (IOException e) { } catch (IOException e) {
tryToClose(s); tryToClose(s, LOG, WARNING);
throw e; throw e;
} }
} }
@@ -88,8 +86,7 @@ class DevReporterImpl implements DevReporter, EventListener {
writer.append(armoured); writer.append(armoured);
writer.flush(); writer.flush();
} finally { } finally {
if (writer != null) tryToClose(writer, LOG, WARNING);
writer.close();
} }
} }
@@ -121,27 +118,11 @@ class DevReporterImpl implements DevReporter, EventListener {
f.delete(); f.delete();
} catch (IOException e) { } catch (IOException e) {
LOG.log(WARNING, "Failed to send reports", e); LOG.log(WARNING, "Failed to send reports", e);
tryToClose(out); tryToClose(out, LOG, WARNING);
tryToClose(in); tryToClose(in, LOG, WARNING);
return; return;
} }
} }
LOG.info("Reports sent"); LOG.info("Reports sent");
} }
private void tryToClose(@Nullable Closeable c) {
try {
if (c != null) c.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private void tryToClose(@Nullable Socket s) {
try {
if (s != null) s.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
} }

View File

@@ -7,6 +7,7 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageFactory; import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.tree.TreeHash;
import org.briarproject.bramble.util.ByteUtils; import org.briarproject.bramble.util.ByteUtils;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
@@ -39,13 +40,19 @@ class MessageFactoryImpl implements MessageFactory {
if (body.length == 0) throw new IllegalArgumentException(); if (body.length == 0) throw new IllegalArgumentException();
if (body.length > MAX_MESSAGE_BODY_LENGTH) if (body.length > MAX_MESSAGE_BODY_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
MessageId id = getMessageId(g, timestamp, body); MessageId id = getMessageIdFromBody(g, timestamp, body);
return new Message(id, g, timestamp, body); return new Message(id, g, timestamp, body);
} }
private MessageId getMessageId(GroupId g, long timestamp, byte[] body) { private MessageId getMessageIdFromBody(GroupId g, long timestamp,
byte[] body) {
// There's only one block, so the root hash is the hash of the block // There's only one block, so the root hash is the hash of the block
byte[] rootHash = crypto.hash(BLOCK_LABEL, FORMAT_VERSION_BYTES, body); byte[] rootHash = crypto.hash(BLOCK_LABEL, FORMAT_VERSION_BYTES, body);
return getMessageIdFromRootHash(g, timestamp, rootHash);
}
private MessageId getMessageIdFromRootHash(GroupId g, long timestamp,
byte[] rootHash) {
byte[] timeBytes = new byte[INT_64_BYTES]; byte[] timeBytes = new byte[INT_64_BYTES];
ByteUtils.writeUint64(timestamp, timeBytes, 0); ByteUtils.writeUint64(timestamp, timeBytes, 0);
byte[] idHash = crypto.hash(ID_LABEL, FORMAT_VERSION_BYTES, byte[] idHash = crypto.hash(ID_LABEL, FORMAT_VERSION_BYTES,
@@ -65,7 +72,7 @@ class MessageFactoryImpl implements MessageFactory {
long timestamp = ByteUtils.readUint64(raw, UniqueId.LENGTH); long timestamp = ByteUtils.readUint64(raw, UniqueId.LENGTH);
byte[] body = new byte[raw.length - MESSAGE_HEADER_LENGTH]; byte[] body = new byte[raw.length - MESSAGE_HEADER_LENGTH];
System.arraycopy(raw, MESSAGE_HEADER_LENGTH, body, 0, body.length); System.arraycopy(raw, MESSAGE_HEADER_LENGTH, body, 0, body.length);
MessageId id = getMessageId(g, timestamp, body); MessageId id = getMessageIdFromBody(g, timestamp, body);
return new Message(id, g, timestamp, body); return new Message(id, g, timestamp, body);
} }
@@ -78,4 +85,10 @@ class MessageFactoryImpl implements MessageFactory {
System.arraycopy(body, 0, raw, MESSAGE_HEADER_LENGTH, body.length); System.arraycopy(body, 0, raw, MESSAGE_HEADER_LENGTH, body.length);
return raw; return raw;
} }
@Override
public MessageId getMessageId(GroupId g, long timestamp,
TreeHash rootHash) {
return getMessageIdFromRootHash(g, timestamp, rootHash.getBytes());
}
} }

View File

@@ -0,0 +1,16 @@
package org.briarproject.bramble.sync.tree;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.tree.LeafNode;
import org.briarproject.bramble.api.sync.tree.TreeNode;
import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
@NotNullByDefault
interface HashTree {
void addLeaf(LeafNode leaf);
TreeNode getRoot();
}

View File

@@ -0,0 +1,46 @@
package org.briarproject.bramble.sync.tree;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.tree.LeafNode;
import org.briarproject.bramble.api.sync.tree.TreeHasher;
import org.briarproject.bramble.api.sync.tree.TreeNode;
import java.util.Deque;
import java.util.LinkedList;
import javax.inject.Inject;
@NotNullByDefault
class HashTreeImpl implements HashTree {
private final TreeHasher treeHasher;
private final Deque<TreeNode> nodes = new LinkedList<>();
@Inject
HashTreeImpl(TreeHasher treeHasher) {
this.treeHasher = treeHasher;
}
@Override
public void addLeaf(LeafNode leaf) {
TreeNode add = leaf;
int height = leaf.getHeight();
TreeNode last = nodes.peekLast();
while (last != null && last.getHeight() == height) {
add = treeHasher.mergeTrees(last, add);
height = add.getHeight();
nodes.removeLast();
last = nodes.peekLast();
}
nodes.addLast(add);
}
@Override
public TreeNode getRoot() {
TreeNode root = nodes.removeLast();
while (!nodes.isEmpty()) {
root = treeHasher.mergeTrees(nodes.removeLast(), root);
}
return root;
}
}

View File

@@ -0,0 +1,85 @@
package org.briarproject.bramble.sync.tree;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.io.BlockSink;
import org.briarproject.bramble.api.io.HashingId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.tree.StreamHasher;
import org.briarproject.bramble.api.sync.tree.TreeHash;
import org.briarproject.bramble.api.sync.tree.TreeHasher;
import org.briarproject.bramble.api.sync.tree.TreeNode;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.inject.Provider;
import static java.util.Arrays.copyOfRange;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_BLOCK_LENGTH;
@Immutable
@NotNullByDefault
class StreamHasherImpl implements StreamHasher {
private final TreeHasher treeHasher;
private final Provider<HashTree> hashTreeProvider;
@Inject
StreamHasherImpl(TreeHasher treeHasher,
Provider<HashTree> hashTreeProvider) {
this.treeHasher = treeHasher;
this.hashTreeProvider = hashTreeProvider;
}
@Override
public TreeNode hash(InputStream in, BlockSink sink, HashingId h)
throws IOException, DbException {
HashTree tree = hashTreeProvider.get();
byte[] block = new byte[MAX_BLOCK_LENGTH];
int read;
for (int blockNumber = 0; (read = read(in, block)) > 0; blockNumber++) {
byte[] data;
if (read == block.length) data = block;
else data = copyOfRange(block, 0, read);
sink.putBlock(h, blockNumber, data);
tree.addLeaf(treeHasher.hashBlock(blockNumber, data));
}
TreeNode root = tree.getRoot();
setPaths(sink, h, root, new LinkedList<>());
return root;
}
/**
* Reads a block from the given input stream and returns the number of
* bytes read, or 0 if no bytes were read before reaching the end of the
* stream.
*/
private int read(InputStream in, byte[] block) throws IOException {
int offset = 0;
while (offset < block.length) {
int read = in.read(block, offset, block.length - offset);
if (read == -1) return offset;
offset += read;
}
return offset;
}
private void setPaths(BlockSink sink, HashingId h, TreeNode node,
LinkedList<TreeHash> path) throws DbException {
if (node.getHeight() == 0) {
// We've reached a leaf - store the path
sink.setPath(h, node.getFirstBlockNumber(), path);
} else {
// Add the right child's hash to the path and traverse the left
path.addFirst(node.getRightChild().getHash());
setPaths(sink, h, node.getLeftChild(), path);
// Add the left child's hash to the path and traverse the right
path.removeFirst();
path.addFirst(node.getLeftChild().getHash());
setPaths(sink, h, node.getRightChild(), path);
}
}
}

View File

@@ -0,0 +1,44 @@
package org.briarproject.bramble.sync.tree;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.tree.LeafNode;
import org.briarproject.bramble.api.sync.tree.ParentNode;
import org.briarproject.bramble.api.sync.tree.TreeHash;
import org.briarproject.bramble.api.sync.tree.TreeHasher;
import org.briarproject.bramble.api.sync.tree.TreeNode;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.sync.Message.FORMAT_VERSION;
import static org.briarproject.bramble.api.sync.MessageId.BLOCK_LABEL;
import static org.briarproject.bramble.api.sync.MessageId.TREE_LABEL;
@Immutable
@NotNullByDefault
class TreeHasherImpl implements TreeHasher {
private static final byte[] FORMAT_VERSION_BYTES =
new byte[] {FORMAT_VERSION};
private final CryptoComponent crypto;
@Inject
TreeHasherImpl(CryptoComponent crypto) {
this.crypto = crypto;
}
@Override
public LeafNode hashBlock(int blockNumber, byte[] data) {
byte[] hash = crypto.hash(BLOCK_LABEL, FORMAT_VERSION_BYTES, data);
return new LeafNode(new TreeHash(hash), blockNumber);
}
@Override
public ParentNode mergeTrees(TreeNode left, TreeNode right) {
byte[] hash = crypto.hash(TREE_LABEL, FORMAT_VERSION_BYTES,
left.getHash().getBytes(), right.getHash().getBytes());
return new ParentNode(new TreeHash(hash), left, right);
}
}

View File

@@ -13,32 +13,33 @@ import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class LinuxSecureRandomProvider extends AbstractSecureRandomProvider { class UnixSecureRandomProvider extends AbstractSecureRandomProvider {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(LinuxSecureRandomProvider.class.getName()); getLogger(UnixSecureRandomProvider.class.getName());
private static final File RANDOM_DEVICE = new File("/dev/urandom"); private static final File RANDOM_DEVICE = new File("/dev/urandom");
private final AtomicBoolean seeded = new AtomicBoolean(false); private final AtomicBoolean seeded = new AtomicBoolean(false);
private final File outputDevice; private final File outputDevice;
LinuxSecureRandomProvider() { UnixSecureRandomProvider() {
this(RANDOM_DEVICE); this(RANDOM_DEVICE);
} }
LinuxSecureRandomProvider(File outputDevice) { UnixSecureRandomProvider(File outputDevice) {
this.outputDevice = outputDevice; this.outputDevice = outputDevice;
} }
@Override @Override
public Provider getProvider() { public Provider getProvider() {
if (!seeded.getAndSet(true)) writeSeed(); if (!seeded.getAndSet(true)) writeSeed();
return new LinuxProvider(); return new UnixProvider();
} }
protected void writeSeed() { protected void writeSeed() {
@@ -55,15 +56,15 @@ class LinuxSecureRandomProvider extends AbstractSecureRandomProvider {
} }
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html // Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
private static class LinuxProvider extends Provider { private static class UnixProvider extends Provider {
private LinuxProvider() { private UnixProvider() {
super("LinuxPRNG", 1.1, "A Linux-specific PRNG using /dev/urandom"); super("UnixPRNG", 1.0, "A Unix-specific PRNG using /dev/urandom");
// Although /dev/urandom is not a SHA-1 PRNG, some callers // Although /dev/urandom is not a SHA-1 PRNG, some callers
// explicitly request a SHA1PRNG SecureRandom and we need to // explicitly request a SHA1PRNG SecureRandom and we need to
// prevent them from getting the default implementation whose // prevent them from getting the default implementation whose
// output may have low entropy. // output may have low entropy.
put("SecureRandom.SHA1PRNG", LinuxSecureRandomSpi.class.getName()); put("SecureRandom.SHA1PRNG", UnixSecureRandomSpi.class.getName());
put("SecureRandom.SHA1PRNG ImplementedIn", "Software"); put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
} }
} }

View File

@@ -10,22 +10,24 @@ import java.security.SecureRandomSpi;
import java.util.logging.Logger; import java.util.logging.Logger;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
public class LinuxSecureRandomSpi extends SecureRandomSpi { public class UnixSecureRandomSpi extends SecureRandomSpi {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(LinuxSecureRandomSpi.class.getName()); getLogger(UnixSecureRandomSpi.class.getName());
private static final File RANDOM_DEVICE = new File("/dev/urandom"); private static final File RANDOM_DEVICE = new File("/dev/urandom");
private final File inputDevice, outputDevice; private final File inputDevice, outputDevice;
public LinuxSecureRandomSpi() { @SuppressWarnings("WeakerAccess")
public UnixSecureRandomSpi() {
this(RANDOM_DEVICE, RANDOM_DEVICE); this(RANDOM_DEVICE, RANDOM_DEVICE);
} }
LinuxSecureRandomSpi(File inputDevice, File outputDevice) { UnixSecureRandomSpi(File inputDevice, File outputDevice) {
this.inputDevice = inputDevice; this.inputDevice = inputDevice;
this.outputDevice = outputDevice; this.outputDevice = outputDevice;
} }

View File

@@ -1,8 +1,6 @@
package org.briarproject.bramble.system; package org.briarproject.bramble.system;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.OsUtils;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -10,48 +8,50 @@ import org.junit.Test;
import java.io.File; import java.io.File;
import java.security.Provider; import java.security.Provider;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
import static org.briarproject.bramble.util.OsUtils.isLinux;
import static org.briarproject.bramble.util.OsUtils.isMac;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
public class LinuxSecureRandomProviderTest extends BrambleTestCase { public class UnixSecureRandomProviderTest extends BrambleTestCase {
private final File testDir = TestUtils.getTestDirectory(); private final File testDir = getTestDirectory();
@Before @Before
public void setUp() { public void setUp() {
testDir.mkdirs(); assumeTrue(isLinux() || isMac());
assertTrue(testDir.mkdirs());
} }
@Test @Test
public void testGetProviderWritesToRandomDeviceOnFirstCall() public void testGetProviderWritesToRandomDeviceOnFirstCall()
throws Exception { throws Exception {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
// Redirect the provider's output to a file // Redirect the provider's output to a file
File urandom = new File(testDir, "urandom"); File urandom = new File(testDir, "urandom");
urandom.delete(); if (urandom.exists()) assertTrue(urandom.delete());
assertTrue(urandom.createNewFile()); assertTrue(urandom.createNewFile());
assertEquals(0, urandom.length()); assertEquals(0, urandom.length());
LinuxSecureRandomProvider p = new LinuxSecureRandomProvider(urandom); UnixSecureRandomProvider p = new UnixSecureRandomProvider(urandom);
// Getting a provider should write entropy to the file // Getting a provider should write entropy to the file
Provider provider = p.getProvider(); Provider provider = p.getProvider();
assertNotNull(provider); assertNotNull(provider);
assertEquals("LinuxPRNG", provider.getName()); assertEquals("UnixPRNG", provider.getName());
// There should be at least 16 bytes from the clock, 8 from the runtime // There should be at least 16 bytes from the clock, 8 from the runtime
long length = urandom.length(); long length = urandom.length();
assertTrue(length >= 24); assertTrue(length >= 24);
// Getting another provider should not write to the file again // Getting another provider should not write to the file again
provider = p.getProvider(); provider = p.getProvider();
assertNotNull(provider); assertNotNull(provider);
assertEquals("LinuxPRNG", provider.getName()); assertEquals("UnixPRNG", provider.getName());
assertEquals(length, urandom.length()); assertEquals(length, urandom.length());
} }
@After @After
public void tearDown() { public void tearDown() {
TestUtils.deleteTestDirectory(testDir); deleteTestDirectory(testDir);
} }
} }

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.IoUtils; import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.util.OsUtils;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -15,31 +14,33 @@ import java.io.FileOutputStream;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
import static org.briarproject.bramble.util.OsUtils.isLinux;
import static org.briarproject.bramble.util.OsUtils.isMac;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
public class LinuxSecureRandomSpiTest extends BrambleTestCase { public class UnixSecureRandomSpiTest extends BrambleTestCase {
private static final File RANDOM_DEVICE = new File("/dev/urandom"); private static final File RANDOM_DEVICE = new File("/dev/urandom");
private static final int SEED_BYTES = 32; private static final int SEED_BYTES = 32;
private final File testDir = TestUtils.getTestDirectory(); private final File testDir = getTestDirectory();
@Before @Before
public void setUp() { public void setUp() {
testDir.mkdirs(); assumeTrue(isLinux() || isMac());
assertTrue(testDir.mkdirs());
} }
@Test @Test
public void testSeedsAreDistinct() { public void testSeedsAreDistinct() {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
Set<Bytes> seeds = new HashSet<>(); Set<Bytes> seeds = new HashSet<>();
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(); UnixSecureRandomSpi engine = new UnixSecureRandomSpi();
for (int i = 0; i < 1000; i++) { for (int i = 0; i < 1000; i++) {
byte[] seed = engine.engineGenerateSeed(SEED_BYTES); byte[] seed = engine.engineGenerateSeed(SEED_BYTES);
assertEquals(SEED_BYTES, seed.length); assertEquals(SEED_BYTES, seed.length);
@@ -49,19 +50,15 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
@Test @Test
public void testEngineSetSeedWritesToRandomDevice() throws Exception { public void testEngineSetSeedWritesToRandomDevice() throws Exception {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
// Redirect the engine's output to a file // Redirect the engine's output to a file
File urandom = new File(testDir, "urandom"); File urandom = new File(testDir, "urandom");
urandom.delete(); if (urandom.exists()) assertTrue(urandom.delete());
assertTrue(urandom.createNewFile()); assertTrue(urandom.createNewFile());
assertEquals(0, urandom.length()); assertEquals(0, urandom.length());
// Generate a seed // Generate a seed
byte[] seed = TestUtils.getRandomBytes(SEED_BYTES); byte[] seed = TestUtils.getRandomBytes(SEED_BYTES);
// Check that the engine writes the seed to the file // Check that the engine writes the seed to the file
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(RANDOM_DEVICE, UnixSecureRandomSpi engine = new UnixSecureRandomSpi(RANDOM_DEVICE,
urandom); urandom);
engine.engineSetSeed(seed); engine.engineSetSeed(seed);
assertEquals(SEED_BYTES, urandom.length()); assertEquals(SEED_BYTES, urandom.length());
@@ -74,15 +71,11 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
@Test @Test
public void testEngineNextBytesReadsFromRandomDevice() throws Exception { public void testEngineNextBytesReadsFromRandomDevice() throws Exception {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
// Generate some entropy // Generate some entropy
byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES); byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES);
// Write the entropy to a file // Write the entropy to a file
File urandom = new File(testDir, "urandom"); File urandom = new File(testDir, "urandom");
urandom.delete(); if (urandom.exists()) assertTrue(urandom.delete());
FileOutputStream out = new FileOutputStream(urandom); FileOutputStream out = new FileOutputStream(urandom);
out.write(entropy); out.write(entropy);
out.flush(); out.flush();
@@ -90,7 +83,7 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
assertTrue(urandom.exists()); assertTrue(urandom.exists());
assertEquals(SEED_BYTES, urandom.length()); assertEquals(SEED_BYTES, urandom.length());
// Check that the engine reads from the file // Check that the engine reads from the file
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(urandom, UnixSecureRandomSpi engine = new UnixSecureRandomSpi(urandom,
RANDOM_DEVICE); RANDOM_DEVICE);
byte[] b = new byte[SEED_BYTES]; byte[] b = new byte[SEED_BYTES];
engine.engineNextBytes(b); engine.engineNextBytes(b);
@@ -99,15 +92,11 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
@Test @Test
public void testEngineGenerateSeedReadsFromRandomDevice() throws Exception { public void testEngineGenerateSeedReadsFromRandomDevice() throws Exception {
if (!(OsUtils.isLinux())) {
System.err.println("WARNING: Skipping test, can't run on this OS");
return;
}
// Generate some entropy // Generate some entropy
byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES); byte[] entropy = TestUtils.getRandomBytes(SEED_BYTES);
// Write the entropy to a file // Write the entropy to a file
File urandom = new File(testDir, "urandom"); File urandom = new File(testDir, "urandom");
urandom.delete(); if (urandom.exists()) assertTrue(urandom.delete());
FileOutputStream out = new FileOutputStream(urandom); FileOutputStream out = new FileOutputStream(urandom);
out.write(entropy); out.write(entropy);
out.flush(); out.flush();
@@ -115,7 +104,7 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
assertTrue(urandom.exists()); assertTrue(urandom.exists());
assertEquals(SEED_BYTES, urandom.length()); assertEquals(SEED_BYTES, urandom.length());
// Check that the engine reads from the file // Check that the engine reads from the file
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(urandom, UnixSecureRandomSpi engine = new UnixSecureRandomSpi(urandom,
RANDOM_DEVICE); RANDOM_DEVICE);
byte[] b = engine.engineGenerateSeed(SEED_BYTES); byte[] b = engine.engineGenerateSeed(SEED_BYTES);
assertArrayEquals(entropy, b); assertArrayEquals(entropy, b);
@@ -123,6 +112,6 @@ public class LinuxSecureRandomSpiTest extends BrambleTestCase {
@After @After
public void tearDown() { public void tearDown() {
TestUtils.deleteTestDirectory(testDir); deleteTestDirectory(testDir);
} }
} }

View File

@@ -4,6 +4,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageFactory; import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.tree.TreeHash;
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH; import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
@@ -27,4 +29,10 @@ public class TestMessageFactory implements MessageFactory {
System.arraycopy(body, 0, raw, MESSAGE_HEADER_LENGTH, body.length); System.arraycopy(body, 0, raw, MESSAGE_HEADER_LENGTH, body.length);
return raw; return raw;
} }
@Override
public MessageId getMessageId(GroupId g, long timestamp,
TreeHash rootHash) {
throw new UnsupportedOperationException();
}
} }

View File

@@ -1,20 +1,21 @@
package org.briarproject.bramble.lifecycle; package org.briarproject.bramble.lifecycle;
import org.briarproject.bramble.api.lifecycle.ShutdownManager; import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.util.OsUtils;
import javax.inject.Singleton; import javax.inject.Singleton;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static org.briarproject.bramble.util.OsUtils.isWindows;
@Module @Module
public class DesktopLifecycleModule extends LifecycleModule { public class DesktopLifecycleModule extends LifecycleModule {
@Provides @Provides
@Singleton @Singleton
ShutdownManager provideDesktopShutdownManager() { ShutdownManager provideDesktopShutdownManager() {
if (OsUtils.isWindows()) return new WindowsShutdownManagerImpl(); if (isWindows()) return new WindowsShutdownManagerImpl();
else return new ShutdownManagerImpl(); else return new ShutdownManagerImpl();
} }
} }

View File

@@ -15,7 +15,6 @@ import com.sun.jna.win32.W32APIFunctionMapper;
import com.sun.jna.win32.W32APITypeMapper; import com.sun.jna.win32.W32APITypeMapper;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.OsUtils;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@@ -29,6 +28,7 @@ import static com.sun.jna.Library.OPTION_FUNCTION_MAPPER;
import static com.sun.jna.Library.OPTION_TYPE_MAPPER; import static com.sun.jna.Library.OPTION_TYPE_MAPPER;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.OsUtils.isWindows;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
@@ -71,7 +71,7 @@ class WindowsShutdownManagerImpl extends ShutdownManagerImpl {
// Locking: lock // Locking: lock
private void initialise() { private void initialise() {
if (OsUtils.isWindows()) { if (isWindows()) {
new EventLoop().start(); new EventLoop().start();
} else { } else {
LOG.warning("Windows shutdown manager used on non-Windows OS"); LOG.warning("Windows shutdown manager used on non-Windows OS");
@@ -111,7 +111,7 @@ class WindowsShutdownManagerImpl extends ShutdownManagerImpl {
public void run() { public void run() {
try { try {
// Load user32.dll // Load user32.dll
User32 user32 = (User32) Native.loadLibrary("user32", User32 user32 = Native.loadLibrary("user32",
User32.class, options); User32.class, options);
// Create a callback to handle the WM_QUERYENDSESSION message // Create a callback to handle the WM_QUERYENDSESSION message
WindowProc proc = (hwnd, msg, wp, lp) -> { WindowProc proc = (hwnd, msg, wp, lp) -> {

View File

@@ -10,7 +10,6 @@ import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider; import org.briarproject.bramble.api.system.ResourceProvider;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.security.CodeSource; import java.security.CodeSource;
@@ -19,9 +18,9 @@ import java.util.concurrent.Executor;
import javax.net.SocketFactory; import javax.net.SocketFactory;
@NotNullByDefault @NotNullByDefault
class LinuxTorPlugin extends TorPlugin { abstract class JavaTorPlugin extends TorPlugin {
LinuxTorPlugin(Executor ioExecutor, NetworkManager networkManager, JavaTorPlugin(Executor ioExecutor, NetworkManager networkManager,
LocationUtils locationUtils, SocketFactory torSocketFactory, LocationUtils locationUtils, SocketFactory torSocketFactory,
Clock clock, ResourceProvider resourceProvider, Clock clock, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, CircumventionProvider circumventionProvider,
@@ -34,17 +33,6 @@ class LinuxTorPlugin extends TorPlugin {
torDirectory); torDirectory);
} }
@Override
protected int getProcessId() {
try {
// Java 9: ProcessHandle.current().pid()
return Integer.parseInt(
new File("/proc/self").getCanonicalFile().getName());
} catch (IOException e) {
throw new AssertionError(e);
}
}
@Override @Override
protected long getLastUpdateTime() { protected long getLastUpdateTime() {
CodeSource codeSource = CodeSource codeSource =
@@ -58,5 +46,4 @@ class LinuxTorPlugin extends TorPlugin {
throw new AssertionError(e); throw new AssertionError(e);
} }
} }
} }

View File

@@ -0,0 +1,47 @@
package org.briarproject.bramble.plugin.tor;
import com.sun.jna.Library;
import com.sun.jna.Native;
import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import java.io.File;
import java.util.concurrent.Executor;
import javax.net.SocketFactory;
@NotNullByDefault
class UnixTorPlugin extends JavaTorPlugin {
UnixTorPlugin(Executor ioExecutor, NetworkManager networkManager,
LocationUtils locationUtils, SocketFactory torSocketFactory,
Clock clock, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Backoff backoff,
DuplexPluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, File torDirectory) {
super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager,
backoff, callback, architecture, maxLatency, maxIdleTime,
torDirectory);
}
@Override
protected int getProcessId() {
return CLibrary.INSTANCE.getpid();
}
private interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary) Native.loadLibrary("c", CLibrary.class);
int getpid();
}
}

View File

@@ -22,14 +22,15 @@ import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.net.SocketFactory; import javax.net.SocketFactory;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.OsUtils.isLinux; import static org.briarproject.bramble.util.OsUtils.isLinux;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class LinuxTorPluginFactory implements DuplexPluginFactory { public class UnixTorPluginFactory implements DuplexPluginFactory {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(LinuxTorPluginFactory.class.getName()); getLogger(UnixTorPluginFactory.class.getName());
private static final int MAX_LATENCY = 30 * 1000; // 30 seconds private static final int MAX_LATENCY = 30 * 1000; // 30 seconds
private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds private static final int MAX_IDLE_TIME = 30 * 1000; // 30 seconds
@@ -49,7 +50,7 @@ public class LinuxTorPluginFactory implements DuplexPluginFactory {
private final Clock clock; private final Clock clock;
private final File torDirectory; private final File torDirectory;
public LinuxTorPluginFactory(Executor ioExecutor, public UnixTorPluginFactory(Executor ioExecutor,
NetworkManager networkManager, LocationUtils locationUtils, NetworkManager networkManager, LocationUtils locationUtils,
EventBus eventBus, SocketFactory torSocketFactory, EventBus eventBus, SocketFactory torSocketFactory,
BackoffFactory backoffFactory, ResourceProvider resourceProvider, BackoffFactory backoffFactory, ResourceProvider resourceProvider,
@@ -95,7 +96,7 @@ public class LinuxTorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
LinuxTorPlugin plugin = new LinuxTorPlugin(ioExecutor, networkManager, UnixTorPlugin plugin = new UnixTorPlugin(ioExecutor, networkManager,
locationUtils, torSocketFactory, clock, resourceProvider, locationUtils, torSocketFactory, clock, resourceProvider,
circumventionProvider, batteryManager, backoff, callback, circumventionProvider, batteryManager, backoff, callback,
architecture, MAX_LATENCY, MAX_IDLE_TIME, torDirectory); architecture, MAX_LATENCY, MAX_IDLE_TIME, torDirectory);

View File

@@ -1,19 +1,24 @@
package org.briarproject.bramble.system; package org.briarproject.bramble.system;
import org.briarproject.bramble.api.system.SecureRandomProvider; import org.briarproject.bramble.api.system.SecureRandomProvider;
import org.briarproject.bramble.util.OsUtils;
import javax.inject.Singleton; import javax.inject.Singleton;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static org.briarproject.bramble.util.OsUtils.isLinux;
import static org.briarproject.bramble.util.OsUtils.isMac;
@Module @Module
public class DesktopSecureRandomModule { public class DesktopSecureRandomModule {
@Provides @Provides
@Singleton @Singleton
SecureRandomProvider provideSecureRandomProvider() { SecureRandomProvider provideSecureRandomProvider() {
return OsUtils.isLinux() ? new LinuxSecureRandomProvider() : null; if (isLinux() || isMac())
return new UnixSecureRandomProvider();
// TODO: Create a secure random provider for Windows
throw new UnsupportedOperationException();
} }
} }

View File

@@ -70,7 +70,7 @@ public class BridgeTest extends BrambleTestCase {
private final File torDir = getTestDirectory(); private final File torDir = getTestDirectory();
private final String bridge; private final String bridge;
private LinuxTorPluginFactory factory; private UnixTorPluginFactory factory;
public BridgeTest(String bridge) { public BridgeTest(String bridge) {
this.bridge = bridge; this.bridge = bridge;
@@ -108,7 +108,7 @@ public class BridgeTest extends BrambleTestCase {
return singletonList(bridge); return singletonList(bridge);
} }
}; };
factory = new LinuxTorPluginFactory(ioExecutor, networkManager, factory = new UnixTorPluginFactory(ioExecutor, networkManager,
locationUtils, eventBus, torSocketFactory, backoffFactory, locationUtils, eventBus, torSocketFactory, backoffFactory,
resourceProvider, bridgeProvider, batteryManager, clock, resourceProvider, bridgeProvider, batteryManager, clock,
torDir); torDir);
@@ -124,7 +124,7 @@ public class BridgeTest extends BrambleTestCase {
DuplexPlugin duplexPlugin = DuplexPlugin duplexPlugin =
factory.createPlugin(new TorPluginCallBack()); factory.createPlugin(new TorPluginCallBack());
assertNotNull(duplexPlugin); assertNotNull(duplexPlugin);
LinuxTorPlugin plugin = (LinuxTorPlugin) duplexPlugin; UnixTorPlugin plugin = (UnixTorPlugin) duplexPlugin;
LOG.warning("Testing " + bridge); LOG.warning("Testing " + bridge);
try { try {

View File

@@ -104,6 +104,7 @@ dependencies {
} }
implementation "com.android.support:cardview-v7:$supportVersion" implementation "com.android.support:cardview-v7:$supportVersion"
implementation "com.android.support:support-annotations:$supportVersion" implementation "com.android.support:support-annotations:$supportVersion"
implementation "com.android.support:exifinterface:$supportVersion"
implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation "android.arch.lifecycle:extensions:1.1.1" implementation "android.arch.lifecycle:extensions:1.1.1"
@@ -117,8 +118,15 @@ dependencies {
implementation 'com.google.zxing:core:3.3.3' implementation 'com.google.zxing:core:3.3.3'
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.12.4' implementation 'uk.co.samuelwall:material-tap-target-prompt:2.12.4'
implementation 'com.vanniktech:emoji-google:0.5.1' implementation 'com.vanniktech:emoji-google:0.5.1'
def glideVersion = '4.8.0'
implementation("com.github.bumptech.glide:glide:$glideVersion") {
exclude group: 'com.android.support'
exclude module: 'disklrucache' // when there's no disk cache, we can't accidentally use it
}
implementation 'com.github.chrisbanes:PhotoView:2.1.4' // later versions already use androidx
annotationProcessor 'com.google.dagger:dagger-compiler:2.19' annotationProcessor 'com.google.dagger:dagger-compiler:2.19'
annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"
compileOnly 'javax.annotation:jsr250-api:1.0' compileOnly 'javax.annotation:jsr250-api:1.0'

View File

@@ -30,3 +30,6 @@
# Emoji # Emoji
-keep class com.vanniktech.emoji.** -keep class com.vanniktech.emoji.**
# Glide
-dontwarn com.bumptech.glide.load.engine.cache.DiskLruCacheWrapper

View File

@@ -113,6 +113,15 @@
/> />
</activity> </activity>
<activity
android:name=".android.conversation.ImageActivity"
android:parentActivityName="org.briarproject.briar.android.conversation.ConversationActivity"
android:theme="@style/BriarTheme.Transparent.NoActionBar">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.conversation.ConversationActivity"/>
</activity>
<activity <activity
android:name="org.briarproject.briar.android.privategroup.creation.CreateGroupActivity" android:name="org.briarproject.briar.android.privategroup.creation.CreateGroupActivity"
android:label="@string/groups_create_group_title" android:label="@string/groups_create_group_title"

View File

@@ -29,6 +29,7 @@ import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.plugin.tor.CircumventionProvider; import org.briarproject.bramble.plugin.tor.CircumventionProvider;
import org.briarproject.briar.BriarCoreEagerSingletons; import org.briarproject.briar.BriarCoreEagerSingletons;
import org.briarproject.briar.BriarCoreModule; import org.briarproject.briar.BriarCoreModule;
import org.briarproject.briar.android.conversation.glide.BriarModelLoader;
import org.briarproject.briar.android.login.SignInReminderReceiver; import org.briarproject.briar.android.login.SignInReminderReceiver;
import org.briarproject.briar.android.reporting.BriarReportSender; import org.briarproject.briar.android.reporting.BriarReportSender;
import org.briarproject.briar.android.view.TextInputView; import org.briarproject.briar.android.view.TextInputView;
@@ -170,6 +171,8 @@ public interface AndroidComponent
void inject(TextInputView textInputView); void inject(TextInputView textInputView);
void inject(BriarModelLoader briarModelLoader);
// Eager singleton load // Eager singleton load
void inject(AppModule.EagerSingletons init); void inject(AppModule.EagerSingletons init);
} }

View File

@@ -15,10 +15,11 @@ import org.briarproject.briar.android.blog.ReblogFragment;
import org.briarproject.briar.android.blog.RssFeedImportActivity; import org.briarproject.briar.android.blog.RssFeedImportActivity;
import org.briarproject.briar.android.blog.RssFeedManageActivity; import org.briarproject.briar.android.blog.RssFeedManageActivity;
import org.briarproject.briar.android.blog.WriteBlogPostActivity; import org.briarproject.briar.android.blog.WriteBlogPostActivity;
import org.briarproject.briar.android.conversation.AliasDialogFragment;
import org.briarproject.briar.android.contact.ContactListFragment; import org.briarproject.briar.android.contact.ContactListFragment;
import org.briarproject.briar.android.contact.ContactModule; import org.briarproject.briar.android.contact.ContactModule;
import org.briarproject.briar.android.conversation.AliasDialogFragment;
import org.briarproject.briar.android.conversation.ConversationActivity; import org.briarproject.briar.android.conversation.ConversationActivity;
import org.briarproject.briar.android.conversation.ImageActivity;
import org.briarproject.briar.android.forum.CreateForumActivity; import org.briarproject.briar.android.forum.CreateForumActivity;
import org.briarproject.briar.android.forum.ForumActivity; import org.briarproject.briar.android.forum.ForumActivity;
import org.briarproject.briar.android.forum.ForumListFragment; import org.briarproject.briar.android.forum.ForumListFragment;
@@ -110,6 +111,8 @@ public interface ActivityComponent {
void inject(ConversationActivity activity); void inject(ConversationActivity activity);
void inject(ImageActivity activity);
void inject(ForumInvitationActivity activity); void inject(ForumInvitationActivity activity);
void inject(BlogInvitationActivity activity); void inject(BlogInvitationActivity activity);

View File

@@ -2,12 +2,11 @@ package org.briarproject.briar.android.activity;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Intent; import android.content.Intent;
import android.support.annotation.RequiresApi;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.transition.Slide;
import android.transition.Transition; import android.transition.Transition;
import android.view.Gravity;
import android.view.Window; import android.view.Window;
import android.widget.CheckBox; import android.widget.CheckBox;
@@ -33,6 +32,7 @@ import static android.os.Build.VERSION.SDK_INT;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_UNLOCK; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_UNLOCK;
import static org.briarproject.briar.android.util.UiUtils.excludeSystemUi;
import static org.briarproject.briar.android.util.UiUtils.getDozeWhitelistingIntent; import static org.briarproject.briar.android.util.UiUtils.getDozeWhitelistingIntent;
import static org.briarproject.briar.android.util.UiUtils.isSamsung7; import static org.briarproject.briar.android.util.UiUtils.isSamsung7;
@@ -111,21 +111,28 @@ public abstract class BriarActivity extends BaseActivity {
lockManager.onActivityStop(); lockManager.onActivityStop();
} }
public void setSceneTransitionAnimation() { /**
if (SDK_INT < 21) return; * Sets the transition animations.
* @param enterTransition used to move views into initial positions
* @param exitTransition used to move views out when starting a <b>new</b> activity.
* @param returnTransition used when window is closing, because the activity is finishing.
*/
@RequiresApi(api = 21)
public void setSceneTransitionAnimation(
@Nullable Transition enterTransition,
@Nullable Transition exitTransition,
@Nullable Transition returnTransition) {
// workaround for #1007 // workaround for #1007
if (isSamsung7()) { if (isSamsung7()) {
return; return;
} }
Transition slide = new Slide(Gravity.RIGHT); if (enterTransition != null) excludeSystemUi(enterTransition);
slide.excludeTarget(android.R.id.statusBarBackground, true); if (exitTransition != null) excludeSystemUi(exitTransition);
slide.excludeTarget(android.R.id.navigationBarBackground, true); if (returnTransition != null) excludeSystemUi(returnTransition);
Window window = getWindow(); Window window = getWindow();
window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); window.setEnterTransition(enterTransition);
window.setExitTransition(slide); window.setExitTransition(exitTransition);
window.setEnterTransition(slide); window.setReturnTransition(returnTransition);
window.setTransitionBackgroundFadeDuration(getResources()
.getInteger(android.R.integer.config_longAnimTime));
} }
/** /**

View File

@@ -18,7 +18,6 @@ public class ReblogActivity extends BriarActivity implements
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
setSceneTransitionAnimation();
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
Intent intent = getIntent(); Intent intent = getIntent();

View File

@@ -0,0 +1,209 @@
package org.briarproject.briar.android.conversation;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.support.annotation.Nullable;
import android.support.media.ExifInterface;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R;
import org.briarproject.briar.api.messaging.Attachment;
import org.briarproject.briar.api.messaging.AttachmentHeader;
import org.briarproject.briar.api.messaging.MessagingManager;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import static android.support.media.ExifInterface.ORIENTATION_ROTATE_270;
import static android.support.media.ExifInterface.ORIENTATION_ROTATE_90;
import static android.support.media.ExifInterface.ORIENTATION_TRANSPOSE;
import static android.support.media.ExifInterface.ORIENTATION_TRANSVERSE;
import static android.support.media.ExifInterface.TAG_IMAGE_LENGTH;
import static android.support.media.ExifInterface.TAG_IMAGE_WIDTH;
import static android.support.media.ExifInterface.TAG_ORIENTATION;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
@NotNullByDefault
class AttachmentController {
private static final Logger LOG =
getLogger(AttachmentController.class.getName());
private final MessagingManager messagingManager;
private final int defaultSize;
private final int minWidth, maxWidth;
private final int minHeight, maxHeight;
private final Map<MessageId, List<AttachmentItem>> attachmentCache =
new ConcurrentHashMap<>();
AttachmentController(MessagingManager messagingManager, Resources res) {
this.messagingManager = messagingManager;
defaultSize =
res.getDimensionPixelSize(R.dimen.message_bubble_image_default);
minWidth = res.getDimensionPixelSize(
R.dimen.message_bubble_image_min_width);
maxWidth = res.getDimensionPixelSize(
R.dimen.message_bubble_image_max_width);
minHeight = res.getDimensionPixelSize(
R.dimen.message_bubble_image_min_height);
maxHeight = res.getDimensionPixelSize(
R.dimen.message_bubble_image_max_height);
}
void put(MessageId messageId, List<AttachmentItem> attachments) {
attachmentCache.put(messageId, attachments);
}
@Nullable
List<AttachmentItem> get(MessageId messageId) {
return attachmentCache.get(messageId);
}
@DatabaseExecutor
List<Pair<AttachmentHeader, Attachment>> getMessageAttachments(
List<AttachmentHeader> headers) throws DbException {
long start = now();
List<Pair<AttachmentHeader, Attachment>> attachments =
new ArrayList<>(headers.size());
for (AttachmentHeader h : headers) {
Attachment a =
messagingManager.getAttachment(h.getMessageId());
attachments.add(new Pair<>(h, a));
}
logDuration(LOG, "Loading attachment", start);
return attachments;
}
List<AttachmentItem> getAttachmentItems(
List<Pair<AttachmentHeader, Attachment>> attachments) {
List<AttachmentItem> items = new ArrayList<>(attachments.size());
for (Pair<AttachmentHeader, Attachment> a : attachments) {
AttachmentItem item =
getAttachmentItem(a.getFirst(), a.getSecond());
items.add(item);
}
return items;
}
private AttachmentItem getAttachmentItem(AttachmentHeader h, Attachment a) {
MessageId messageId = h.getMessageId();
Size size = new Size();
InputStream is = a.getStream();
is.mark(Integer.MAX_VALUE);
try {
// use exif to get size
if (h.getContentType().equals("image/jpeg")) {
size = getSizeFromExif(is);
}
} catch (IOException e) {
logException(LOG, WARNING, e);
}
try {
// use BitmapFactory to get size
if (size.error) {
is.reset();
size = getSizeFromBitmap(is);
}
} catch (IOException e) {
logException(LOG, WARNING, e);
} finally {
tryToClose(is, LOG, WARNING);
}
// calculate thumbnail size
Size thumbnailSize = new Size(defaultSize, defaultSize);
if (!size.error) {
thumbnailSize = getThumbnailSize(size.width, size.height);
}
return new AttachmentItem(messageId, size.width, size.height,
thumbnailSize.width, thumbnailSize.height, size.error);
}
/**
* Gets the size of a JPEG {@link InputStream} if EXIF info is available.
*/
private static Size getSizeFromExif(InputStream is)
throws IOException {
ExifInterface exif = new ExifInterface(is);
// these can return 0 independent of default value
int width = exif.getAttributeInt(TAG_IMAGE_WIDTH, 0);
int height = exif.getAttributeInt(TAG_IMAGE_LENGTH, 0);
if (width == 0 || height == 0) return new Size();
int orientation = exif.getAttributeInt(TAG_ORIENTATION, 0);
if (orientation == ORIENTATION_ROTATE_90 ||
orientation == ORIENTATION_ROTATE_270 ||
orientation == ORIENTATION_TRANSVERSE ||
orientation == ORIENTATION_TRANSPOSE) {
//noinspection SuspiciousNameCombination
return new Size(height, width);
}
return new Size(width, height);
}
/**
* Gets the size of any image {@link InputStream}.
*/
private static Size getSizeFromBitmap(InputStream is) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, options);
if (options.outWidth < 1 || options.outHeight < 1)
return new Size();
return new Size(options.outWidth, options.outHeight);
}
private Size getThumbnailSize(int width, int height) {
float widthPercentage = maxWidth / (float) width;
float heightPercentage = maxHeight / (float) height;
float scaleFactor = Math.min(widthPercentage, heightPercentage);
if (scaleFactor > 1) scaleFactor = 1f;
int thumbnailWidth = (int) (width * scaleFactor);
int thumbnailHeight = (int) (height * scaleFactor);
if (thumbnailWidth < minWidth || thumbnailHeight < minHeight) {
widthPercentage = minWidth / (float) width;
heightPercentage = minHeight / (float) height;
scaleFactor = Math.max(widthPercentage, heightPercentage);
thumbnailWidth = (int) (width * scaleFactor);
thumbnailHeight = (int) (height * scaleFactor);
if (thumbnailWidth > maxWidth) thumbnailWidth = maxWidth;
if (thumbnailHeight > maxHeight) thumbnailHeight = maxHeight;
}
return new Size(thumbnailWidth, thumbnailHeight);
}
private static class Size {
private final int width;
private final int height;
private final boolean error;
private Size(int width, int height) {
this.width = width;
this.height = height;
this.error = false;
}
private Size() {
this.width = 0;
this.height = 0;
this.error = true;
}
}
}

View File

@@ -0,0 +1,98 @@
package org.briarproject.briar.android.conversation;
import android.os.Parcel;
import android.os.Parcelable;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class AttachmentItem implements Parcelable {
private final MessageId messageId;
private final int width, height;
private final int thumbnailWidth, thumbnailHeight;
private final boolean hasError;
public static final Creator<AttachmentItem> CREATOR =
new Creator<AttachmentItem>() {
@Override
public AttachmentItem createFromParcel(Parcel in) {
return new AttachmentItem(in);
}
@Override
public AttachmentItem[] newArray(int size) {
return new AttachmentItem[size];
}
};
AttachmentItem(MessageId messageId, int width, int height,
int thumbnailWidth, int thumbnailHeight, boolean hasError) {
this.messageId = messageId;
this.width = width;
this.height = height;
this.thumbnailWidth = thumbnailWidth;
this.thumbnailHeight = thumbnailHeight;
this.hasError = hasError;
}
protected AttachmentItem(Parcel in) {
byte[] messageIdByte = new byte[MessageId.LENGTH];
in.readByteArray(messageIdByte);
messageId = new MessageId(messageIdByte);
width = in.readInt();
height = in.readInt();
thumbnailWidth = in.readInt();
thumbnailHeight = in.readInt();
hasError = in.readByte() != 0;
}
public MessageId getMessageId() {
return messageId;
}
int getWidth() {
return width;
}
int getHeight() {
return height;
}
int getThumbnailWidth() {
return thumbnailWidth;
}
int getThumbnailHeight() {
return thumbnailHeight;
}
boolean hasError() {
return hasError;
}
// TODO use counter instead, because in theory one attachment can appear in more than one messages
String getTransitionName() {
return String.valueOf(messageId.hashCode());
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeByteArray(messageId.getBytes());
dest.writeInt(width);
dest.writeInt(height);
dest.writeInt(thumbnailWidth);
dest.writeInt(thumbnailHeight);
dest.writeByte((byte) (hasError ? 1 : 0));
}
}

View File

@@ -9,11 +9,15 @@ import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.widget.ActionMenuView; import android.support.v7.widget.ActionMenuView;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.transition.Slide;
import android.transition.Transition;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@@ -24,6 +28,7 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
@@ -51,6 +56,7 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity; import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.blog.BlogActivity; import org.briarproject.briar.android.blog.BlogActivity;
import org.briarproject.briar.android.conversation.ConversationVisitor.AttachmentCache;
import org.briarproject.briar.android.conversation.ConversationVisitor.TextCache; import org.briarproject.briar.android.conversation.ConversationVisitor.TextCache;
import org.briarproject.briar.android.forum.ForumActivity; import org.briarproject.briar.android.forum.ForumActivity;
import org.briarproject.briar.android.introduction.IntroductionActivity; import org.briarproject.briar.android.introduction.IntroductionActivity;
@@ -69,6 +75,8 @@ import org.briarproject.briar.api.conversation.ConversationResponse;
import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent; import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent;
import org.briarproject.briar.api.forum.ForumSharingManager; import org.briarproject.briar.api.forum.ForumSharingManager;
import org.briarproject.briar.api.introduction.IntroductionManager; import org.briarproject.briar.api.introduction.IntroductionManager;
import org.briarproject.briar.api.messaging.Attachment;
import org.briarproject.briar.api.messaging.AttachmentHeader;
import org.briarproject.briar.api.messaging.MessagingManager; import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessage; import org.briarproject.briar.api.messaging.PrivateMessage;
import org.briarproject.briar.api.messaging.PrivateMessageFactory; import org.briarproject.briar.api.messaging.PrivateMessageFactory;
@@ -92,8 +100,11 @@ import im.delight.android.identicons.IdenticonDrawable;
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt; import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt;
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.PromptStateChangeListener; import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.PromptStateChangeListener;
import static android.os.Build.VERSION.SDK_INT;
import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAnimation;
import static android.support.v4.view.ViewCompat.setTransitionName; import static android.support.v4.view.ViewCompat.setTransitionName;
import static android.support.v7.util.SortedList.INVALID_POSITION; import static android.support.v7.util.SortedList.INVALID_POSITION;
import static android.view.Gravity.END;
import static android.widget.Toast.LENGTH_SHORT; import static android.widget.Toast.LENGTH_SHORT;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.sort; import static java.util.Collections.sort;
@@ -104,6 +115,9 @@ import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_INTRODUCTION; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_INTRODUCTION;
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT;
import static org.briarproject.briar.android.conversation.ImageActivity.DATE;
import static org.briarproject.briar.android.conversation.ImageActivity.NAME;
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE; import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName; import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName; import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName;
@@ -116,7 +130,7 @@ import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.S
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class ConversationActivity extends BriarActivity public class ConversationActivity extends BriarActivity
implements EventListener, ConversationListener, TextInputListener, implements EventListener, ConversationListener, TextInputListener,
TextCache { TextCache, AttachmentCache {
public static final String CONTACT_ID = "briar.CONTACT_ID"; public static final String CONTACT_ID = "briar.CONTACT_ID";
@@ -134,6 +148,7 @@ public class ConversationActivity extends BriarActivity
Executor cryptoExecutor; Executor cryptoExecutor;
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>(); private final Map<MessageId, String> textCache = new ConcurrentHashMap<>();
private AttachmentController attachmentController;
private ConversationViewModel viewModel; private ConversationViewModel viewModel;
private ConversationVisitor visitor; private ConversationVisitor visitor;
@@ -143,6 +158,7 @@ public class ConversationActivity extends BriarActivity
private ImageView toolbarStatus; private ImageView toolbarStatus;
private TextView toolbarTitle; private TextView toolbarTitle;
private BriarRecyclerView list; private BriarRecyclerView list;
private LinearLayoutManager layoutManager;
private TextInputView textInputView; private TextInputView textInputView;
// Fields that are accessed from background threads must be volatile // Fields that are accessed from background threads must be volatile
@@ -180,7 +196,10 @@ public class ConversationActivity extends BriarActivity
@Override @Override
public void onCreate(@Nullable Bundle state) { public void onCreate(@Nullable Bundle state) {
setSceneTransitionAnimation(); if (SDK_INT >= 21) {
Transition slide = new Slide(END);
setSceneTransitionAnimation(slide, null, slide);
}
super.onCreate(state); super.onCreate(state);
Intent i = getIntent(); Intent i = getIntent();
@@ -191,6 +210,7 @@ public class ConversationActivity extends BriarActivity
viewModel = ViewModelProviders.of(this, viewModelFactory) viewModel = ViewModelProviders.of(this, viewModelFactory)
.get(ConversationViewModel.class); .get(ConversationViewModel.class);
viewModel.setContactId(contactId); viewModel.setContactId(contactId);
attachmentController = viewModel.getAttachmentController();
setContentView(R.layout.activity_conversation); setContentView(R.layout.activity_conversation);
@@ -217,11 +237,12 @@ public class ConversationActivity extends BriarActivity
setTransitionName(toolbarAvatar, getAvatarTransitionName(contactId)); setTransitionName(toolbarAvatar, getAvatarTransitionName(contactId));
setTransitionName(toolbarStatus, getBulbTransitionName(contactId)); setTransitionName(toolbarStatus, getBulbTransitionName(contactId));
visitor = new ConversationVisitor(this, this, visitor = new ConversationVisitor(this, this, this,
viewModel.getContactDisplayName()); viewModel.getContactDisplayName());
adapter = new ConversationAdapter(this, this); adapter = new ConversationAdapter(this, this);
list = findViewById(R.id.conversationView); list = findViewById(R.id.conversationView);
list.setLayoutManager(new LinearLayoutManager(this)); layoutManager = new LinearLayoutManager(this);
list.setLayoutManager(layoutManager);
list.setAdapter(adapter); list.setAdapter(adapter);
list.setEmptyText(getString(R.string.no_private_messages)); list.setEmptyText(getString(R.string.no_private_messages));
@@ -340,14 +361,30 @@ public class ConversationActivity extends BriarActivity
// If the latest header is a private message, eagerly load // If the latest header is a private message, eagerly load
// its text so we can set the scroll position correctly // its text so we can set the scroll position correctly
ConversationMessageHeader latest = sorted.get(0); ConversationMessageHeader latest = sorted.get(0);
if (latest instanceof PrivateMessageHeader && if (latest instanceof PrivateMessageHeader) {
((PrivateMessageHeader) latest).hasText()) {
MessageId id = latest.getId(); MessageId id = latest.getId();
String text = textCache.get(id); PrivateMessageHeader h = (PrivateMessageHeader) latest;
if (text == null) { if (h.hasText()) {
LOG.info("Eagerly loading text of latest message"); String text = textCache.get(id);
text = messagingManager.getMessageText(id); if (text == null) {
textCache.put(id, text); LOG.info(
"Eagerly loading text of latest message");
text = messagingManager.getMessageText(id);
textCache.put(id, text);
}
}
if (!h.getAttachmentHeaders().isEmpty()) {
List<AttachmentItem> items =
attachmentController.get(id);
if (items == null) {
LOG.info(
"Eagerly loading image size for latest message");
items = attachmentController.getAttachmentItems(
attachmentController
.getMessageAttachments(
h.getAttachmentHeaders()));
attachmentController.put(id, items);
}
} }
} }
} }
@@ -408,16 +445,44 @@ public class ConversationActivity extends BriarActivity
private void displayMessageText(MessageId m, String text) { private void displayMessageText(MessageId m, String text) {
runOnUiThreadUnlessDestroyed(() -> { runOnUiThreadUnlessDestroyed(() -> {
textCache.put(m, text); textCache.put(m, text);
SparseArray<ConversationMessageItem> messages = Pair<Integer, ConversationMessageItem> pair =
adapter.getMessageItems(); adapter.getMessageItem(m);
for (int i = 0; i < messages.size(); i++) { if (pair != null) {
ConversationItem item = messages.valueAt(i); pair.getSecond().setText(text);
if (item.getId().equals(m)) { boolean bottom = adapter.isScrolledToBottom(layoutManager);
item.setText(text); adapter.notifyItemChanged(pair.getFirst());
adapter.notifyItemChanged(messages.keyAt(i)); if (bottom) list.scrollToPosition(adapter.getItemCount() - 1);
list.scrollToPosition(adapter.getItemCount() - 1); }
return; });
} }
private void loadMessageAttachments(MessageId messageId,
List<AttachmentHeader> headers) {
runOnDbThread(() -> {
try {
List<Pair<AttachmentHeader, Attachment>> attachments =
attachmentController.getMessageAttachments(headers);
// TODO move getting the items off to the IoExecutor
List<AttachmentItem> items =
attachmentController.getAttachmentItems(attachments);
displayMessageAttachments(messageId, items);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
private void displayMessageAttachments(MessageId m,
List<AttachmentItem> items) {
runOnUiThreadUnlessDestroyed(() -> {
attachmentController.put(m, items);
Pair<Integer, ConversationMessageItem> pair =
adapter.getMessageItem(m);
if (pair != null) {
pair.getSecond().setAttachments(items);
boolean bottom = adapter.isScrolledToBottom(layoutManager);
adapter.notifyItemChanged(pair.getFirst());
if (bottom) list.scrollToPosition(adapter.getItemCount() - 1);
} }
}); });
} }
@@ -466,10 +531,10 @@ public class ConversationActivity extends BriarActivity
private void addConversationItem(ConversationItem item) { private void addConversationItem(ConversationItem item) {
runOnUiThreadUnlessDestroyed(() -> { runOnUiThreadUnlessDestroyed(() -> {
boolean bottom = adapter.isScrolledToBottom(layoutManager);
adapter.incrementRevision(); adapter.incrementRevision();
adapter.add(item); adapter.add(item);
// Scroll to the bottom if (bottom) list.scrollToPosition(adapter.getItemCount() - 1);
list.scrollToPosition(adapter.getItemCount() - 1);
}); });
} }
@@ -750,6 +815,31 @@ public class ConversationActivity extends BriarActivity
startActivity(i); startActivity(i);
} }
@Override
public void onAttachmentClicked(View view,
ConversationMessageItem messageItem, AttachmentItem item) {
String name;
if (messageItem.isIncoming()) {
// must be available when items are being displayed
name = viewModel.getContactDisplayName().getValue();
} else {
name = getString(R.string.you);
}
Intent i = new Intent(this, ImageActivity.class);
i.putExtra(ATTACHMENT, item);
i.putExtra(NAME, name);
i.putExtra(DATE, messageItem.getTime());
if (SDK_INT >= 23) {
String transitionName = item.getTransitionName();
ActivityOptionsCompat options =
makeSceneTransitionAnimation(this, view, transitionName);
ActivityCompat.startActivity(this, i, options.toBundle());
} else {
// work-around for android bug #224270
startActivity(i);
}
}
@DatabaseExecutor @DatabaseExecutor
private void respondToIntroductionRequest(SessionId sessionId, private void respondToIntroductionRequest(SessionId sessionId,
boolean accept, long time) throws DbException { boolean accept, long time) throws DbException {
@@ -782,4 +872,16 @@ public class ConversationActivity extends BriarActivity
if (text == null) loadMessageText(m); if (text == null) loadMessageText(m);
return text; return text;
} }
@Override
public List<AttachmentItem> getAttachmentItems(MessageId m,
List<AttachmentHeader> headers) {
List<AttachmentItem> attachments = attachmentController.get(m);
if (attachments == null) {
loadMessageAttachments(m, headers);
return emptyList();
}
return attachments;
}
} }

View File

@@ -2,12 +2,15 @@ package org.briarproject.briar.android.conversation;
import android.content.Context; import android.content.Context;
import android.support.annotation.LayoutRes; import android.support.annotation.LayoutRes;
import android.support.v7.widget.LinearLayoutManager;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.util.BriarAdapter; import org.briarproject.briar.android.util.BriarAdapter;
@@ -98,16 +101,20 @@ class ConversationAdapter
return messages; return messages;
} }
SparseArray<ConversationMessageItem> getMessageItems() { @Nullable
SparseArray<ConversationMessageItem> messages = new SparseArray<>(); Pair<Integer, ConversationMessageItem> getMessageItem(MessageId messageId) {
for (int i = 0; i < items.size(); i++) { for (int i = 0; i < items.size(); i++) {
ConversationItem item = items.get(i); ConversationItem item = items.get(i);
if (item instanceof ConversationMessageItem) { if (item instanceof ConversationMessageItem &&
messages.put(i, (ConversationMessageItem) item); item.getId().equals(messageId)) {
return new Pair<>(i, (ConversationMessageItem) item);
} }
} }
return messages; return null;
}
boolean isScrolledToBottom(LinearLayoutManager layoutManager) {
return layoutManager.findLastVisibleItemPosition() == items.size() - 1;
} }
} }

View File

@@ -3,9 +3,9 @@ package org.briarproject.briar.android.conversation;
import android.support.annotation.CallSuper; import android.support.annotation.CallSuper;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.constraint.ConstraintLayout;
import android.support.v7.widget.RecyclerView.ViewHolder; import android.support.v7.widget.RecyclerView.ViewHolder;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -18,11 +18,11 @@ import static org.briarproject.briar.android.util.UiUtils.formatDate;
@NotNullByDefault @NotNullByDefault
abstract class ConversationItemViewHolder extends ViewHolder { abstract class ConversationItemViewHolder extends ViewHolder {
protected final ViewGroup layout; protected final ConstraintLayout layout;
@Nullable @Nullable
private final OutItemViewHolder outViewHolder; private final OutItemViewHolder outViewHolder;
private final TextView text; private final TextView text;
private final TextView time; protected final TextView time;
ConversationItemViewHolder(View v, boolean isIncoming) { ConversationItemViewHolder(View v, boolean isIncoming) {
super(v); super(v);

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android.conversation; package org.briarproject.briar.android.conversation;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.view.View;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -14,4 +15,7 @@ interface ConversationListener {
void openRequestedShareable(ConversationRequestItem item); void openRequestedShareable(ConversationRequestItem item);
void onAttachmentClicked(View view, ConversationMessageItem messageItem,
AttachmentItem attachmentItem);
} }

View File

@@ -3,7 +3,6 @@ package org.briarproject.briar.android.conversation;
import android.support.annotation.LayoutRes; import android.support.annotation.LayoutRes;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.messaging.AttachmentHeader;
import org.briarproject.briar.api.messaging.PrivateMessageHeader; import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import java.util.List; import java.util.List;
@@ -14,15 +13,20 @@ import javax.annotation.concurrent.NotThreadSafe;
@NotNullByDefault @NotNullByDefault
class ConversationMessageItem extends ConversationItem { class ConversationMessageItem extends ConversationItem {
private final List<AttachmentHeader> attachments; private List<AttachmentItem> attachments;
ConversationMessageItem(@LayoutRes int layoutRes, PrivateMessageHeader h) { ConversationMessageItem(@LayoutRes int layoutRes, PrivateMessageHeader h,
List<AttachmentItem> attachments) {
super(layoutRes, h); super(layoutRes, h);
this.attachments = h.getAttachmentHeaders(); this.attachments = attachments;
} }
List<AttachmentHeader> getAttachments() { List<AttachmentItem> getAttachments() {
return attachments; return attachments;
} }
void setAttachments(List<AttachmentItem> attachments) {
this.attachments = attachments;
}
} }

View File

@@ -1,18 +1,161 @@
package org.briarproject.briar.android.conversation; package org.briarproject.briar.android.conversation;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.support.annotation.DrawableRes;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.constraint.ConstraintSet;
import android.support.v4.content.ContextCompat;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.bumptech.glide.load.Transformation;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.conversation.glide.BriarImageTransformation;
import org.briarproject.briar.android.conversation.glide.GlideApp;
import static android.os.Build.VERSION.SDK_INT;
import static android.support.v4.view.ViewCompat.LAYOUT_DIRECTION_RTL;
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
class ConversationMessageViewHolder extends ConversationItemViewHolder { class ConversationMessageViewHolder extends ConversationItemViewHolder {
// image support will be added here (#1242) @DrawableRes
private static final int ERROR_RES = R.drawable.ic_image_broken;
private final ImageView imageView;
private final ViewGroup statusLayout;
private final int timeColor, timeColorBubble;
private final int radiusBig, radiusSmall;
private final boolean isRtl;
private final ConstraintSet textConstraints = new ConstraintSet();
private final ConstraintSet imageConstraints = new ConstraintSet();
private final ConstraintSet imageTextConstraints = new ConstraintSet();
ConversationMessageViewHolder(View v, boolean isIncoming) { ConversationMessageViewHolder(View v, boolean isIncoming) {
super(v, isIncoming); super(v, isIncoming);
imageView = v.findViewById(R.id.imageView);
statusLayout = v.findViewById(R.id.statusLayout);
radiusBig = v.getContext().getResources()
.getDimensionPixelSize(R.dimen.message_bubble_radius_big);
radiusSmall = v.getContext().getResources()
.getDimensionPixelSize(R.dimen.message_bubble_radius_small);
// remember original status text color
timeColor = time.getCurrentTextColor();
timeColorBubble =
ContextCompat.getColor(v.getContext(), R.color.briar_white);
// find out if we are showing a RTL language, Use the configuration,
// because getting the layout direction of views is not reliable
Configuration config =
imageView.getContext().getResources().getConfiguration();
isRtl = SDK_INT >= 17 &&
config.getLayoutDirection() == LAYOUT_DIRECTION_RTL;
// clone constraint sets from layout files
textConstraints
.clone(v.getContext(), R.layout.list_item_conversation_msg_in);
imageConstraints.clone(v.getContext(),
R.layout.list_item_conversation_msg_image);
imageTextConstraints.clone(v.getContext(),
R.layout.list_item_conversation_msg_image_text);
// in/out are different layouts, so we need to do this only once
textConstraints
.setHorizontalBias(R.id.statusLayout, isIncoming() ? 1 : 0);
imageConstraints
.setHorizontalBias(R.id.statusLayout, isIncoming() ? 1 : 0);
imageTextConstraints
.setHorizontalBias(R.id.statusLayout, isIncoming() ? 1 : 0);
}
@Override
void bind(ConversationItem conversationItem,
ConversationListener listener) {
super.bind(conversationItem, listener);
ConversationMessageItem item =
(ConversationMessageItem) conversationItem;
if (item.getAttachments().isEmpty()) {
bindTextItem();
} else {
bindImageItem(item, listener);
}
}
private void bindTextItem() {
clearImage();
statusLayout.setBackgroundResource(0);
// also reset padding (the background drawable defines some)
statusLayout.setPadding(0, 0, 0, 0);
time.setTextColor(timeColor);
textConstraints.applyTo(layout);
}
private void bindImageItem(ConversationMessageItem item,
ConversationListener listener) {
// TODO show more than just the first image
AttachmentItem attachment = item.getAttachments().get(0);
ConstraintSet constraintSet;
if (item.getText() == null) {
statusLayout
.setBackgroundResource(R.drawable.msg_status_bubble);
time.setTextColor(timeColorBubble);
constraintSet = imageConstraints;
} else {
statusLayout.setBackgroundResource(0);
// also reset padding (the background drawable defines some)
statusLayout.setPadding(0, 0, 0, 0);
time.setTextColor(timeColor);
constraintSet = imageTextConstraints;
}
// apply image size constraints, so glides picks them up for scaling
int width = attachment.getThumbnailWidth();
int height = attachment.getThumbnailHeight();
constraintSet.constrainWidth(R.id.imageView, width);
constraintSet.constrainHeight(R.id.imageView, height);
constraintSet.applyTo(layout);
if (attachment.hasError()) {
clearImage();
imageView.setImageResource(ERROR_RES);
} else {
loadImage(item, attachment, listener);
}
}
private void clearImage() {
GlideApp.with(imageView)
.clear(imageView);
imageView.setOnClickListener(null);
}
private void loadImage(ConversationMessageItem item,
AttachmentItem attachment, ConversationListener listener) {
boolean leftCornerSmall =
(isIncoming() && !isRtl) || (!isIncoming() && isRtl);
boolean bottomRound = item.getText() == null;
Transformation<Bitmap> transformation = new BriarImageTransformation(
radiusSmall, radiusBig, leftCornerSmall, bottomRound);
GlideApp.with(imageView)
.load(attachment)
.diskCacheStrategy(NONE)
.error(ERROR_RES)
.transform(transformation)
.transition(withCrossFade())
.into(imageView)
.waitForLayout();
imageView.setOnClickListener(
view -> listener.onAttachmentClicked(view, item, attachment));
} }
} }

View File

@@ -0,0 +1,19 @@
package org.briarproject.briar.android.conversation;
import org.briarproject.briar.android.activity.ActivityScope;
import org.briarproject.briar.android.conversation.glide.BriarDataFetcherFactory;
import dagger.Module;
import dagger.Provides;
@Module
public class ConversationModule {
@ActivityScope
@Provides
BriarDataFetcherFactory provideBriarDataFetcherFactory(
BriarDataFetcherFactory dataFetcherFactory) {
return dataFetcherFactory;
}
}

View File

@@ -16,6 +16,7 @@ import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.util.UiUtils; import org.briarproject.briar.android.util.UiUtils;
import org.briarproject.briar.api.messaging.MessagingManager;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -38,6 +39,7 @@ public class ConversationViewModel extends AndroidViewModel {
@DatabaseExecutor @DatabaseExecutor
private final Executor dbExecutor; private final Executor dbExecutor;
private final ContactManager contactManager; private final ContactManager contactManager;
private final AttachmentController attachmentController;
@Nullable @Nullable
private ContactId contactId = null; private ContactId contactId = null;
@@ -52,10 +54,12 @@ public class ConversationViewModel extends AndroidViewModel {
@Inject @Inject
ConversationViewModel(Application application, ConversationViewModel(Application application,
@DatabaseExecutor Executor dbExecutor, @DatabaseExecutor Executor dbExecutor,
ContactManager contactManager) { ContactManager contactManager, MessagingManager messagingManager) {
super(application); super(application);
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.contactManager = contactManager; this.contactManager = contactManager;
this.attachmentController = new AttachmentController(messagingManager,
application.getResources());
contactDeleted.setValue(false); contactDeleted.setValue(false);
} }
@@ -96,6 +100,10 @@ public class ConversationViewModel extends AndroidViewModel {
}); });
} }
AttachmentController getAttachmentController() {
return attachmentController;
}
LiveData<Contact> getContact() { LiveData<Contact> getContact() {
return contact; return contact;
} }

View File

@@ -14,12 +14,16 @@ import org.briarproject.briar.api.forum.ForumInvitationRequest;
import org.briarproject.briar.api.forum.ForumInvitationResponse; import org.briarproject.briar.api.forum.ForumInvitationResponse;
import org.briarproject.briar.api.introduction.IntroductionRequest; import org.briarproject.briar.api.introduction.IntroductionRequest;
import org.briarproject.briar.api.introduction.IntroductionResponse; import org.briarproject.briar.api.introduction.IntroductionResponse;
import org.briarproject.briar.api.messaging.AttachmentHeader;
import org.briarproject.briar.api.messaging.PrivateMessageHeader; import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest; import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse; import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse;
import java.util.List;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static java.util.Collections.emptyList;
import static org.briarproject.briar.android.conversation.ConversationRequestItem.RequestType.BLOG; import static org.briarproject.briar.android.conversation.ConversationRequestItem.RequestType.BLOG;
import static org.briarproject.briar.android.conversation.ConversationRequestItem.RequestType.FORUM; import static org.briarproject.briar.android.conversation.ConversationRequestItem.RequestType.FORUM;
import static org.briarproject.briar.android.conversation.ConversationRequestItem.RequestType.GROUP; import static org.briarproject.briar.android.conversation.ConversationRequestItem.RequestType.GROUP;
@@ -33,24 +37,33 @@ class ConversationVisitor implements
private final Context ctx; private final Context ctx;
private final TextCache textCache; private final TextCache textCache;
private final AttachmentCache attachmentCache;
private final LiveData<String> contactName; private final LiveData<String> contactName;
ConversationVisitor(Context ctx, TextCache textCache, ConversationVisitor(Context ctx, TextCache textCache,
LiveData<String> contactName) { AttachmentCache attachmentCache, LiveData<String> contactName) {
this.ctx = ctx; this.ctx = ctx;
this.textCache = textCache; this.textCache = textCache;
this.attachmentCache = attachmentCache;
this.contactName = contactName; this.contactName = contactName;
} }
@Override @Override
public ConversationItem visitPrivateMessageHeader(PrivateMessageHeader h) { public ConversationItem visitPrivateMessageHeader(PrivateMessageHeader h) {
ConversationItem item; ConversationItem item;
List<AttachmentItem> attachments;
if (h.getAttachmentHeaders().isEmpty()) {
attachments = emptyList();
} else {
attachments = attachmentCache
.getAttachmentItems(h.getId(), h.getAttachmentHeaders());
}
if (h.isLocal()) { if (h.isLocal()) {
item = new ConversationMessageItem( item = new ConversationMessageItem(
R.layout.list_item_conversation_msg_out, h); R.layout.list_item_conversation_msg_out, h, attachments);
} else { } else {
item = new ConversationMessageItem( item = new ConversationMessageItem(
R.layout.list_item_conversation_msg_in, h); R.layout.list_item_conversation_msg_in, h, attachments);
} }
if (h.hasText()) { if (h.hasText()) {
String text = textCache.getText(h.getId()); String text = textCache.getText(h.getId());
@@ -279,4 +292,9 @@ class ConversationVisitor implements
@Nullable @Nullable
String getText(MessageId m); String getText(MessageId m);
} }
interface AttachmentCache {
List<AttachmentItem> getAttachmentItems(MessageId m,
List<AttachmentHeader> headers);
}
} }

View File

@@ -0,0 +1,233 @@
package org.briarproject.briar.android.conversation;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.design.widget.AppBarLayout;
import android.support.v7.widget.Toolbar;
import android.transition.Fade;
import android.transition.Transition;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.TextView;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.github.chrisbanes.photoview.PhotoView;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.conversation.glide.GlideApp;
import org.briarproject.briar.android.view.PullDownLayout;
import static android.graphics.Color.TRANSPARENT;
import static android.os.Build.VERSION.SDK_INT;
import static android.view.View.GONE;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.widget.ImageView.ScaleType.FIT_START;
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
import static java.util.Objects.requireNonNull;
import static org.briarproject.briar.android.util.UiUtils.formatDateAbsolute;
public class ImageActivity extends BriarActivity
implements PullDownLayout.Callback {
final static String ATTACHMENT = "attachment";
final static String NAME = "name";
final static String DATE = "date";
private PullDownLayout layout;
private AppBarLayout appBarLayout;
private PhotoView photoView;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
// Transitions
supportPostponeEnterTransition();
Window window = getWindow();
if (SDK_INT >= 21) {
Transition transition = new Fade();
setSceneTransitionAnimation(transition, null, transition);
}
// inflate layout
setContentView(R.layout.activity_image);
layout = findViewById(R.id.layout);
layout.getBackground().setAlpha(255);
layout.setCallback(this);
// Status Bar
if (SDK_INT >= 21) {
window.setStatusBarColor(TRANSPARENT);
} else if (SDK_INT >= 19) {
// we can't make the status bar transparent, but translucent
window.addFlags(FLAG_TRANSLUCENT_STATUS);
}
// Toolbar
appBarLayout = findViewById(R.id.appBarLayout);
Toolbar toolbar = requireNonNull(setUpCustomToolbar(true));
TextView contactName = toolbar.findViewById(R.id.contactName);
TextView dateView = toolbar.findViewById(R.id.dateView);
// Intent Extras
AttachmentItem attachment = getIntent().getParcelableExtra(ATTACHMENT);
String name = getIntent().getStringExtra(NAME);
long time = getIntent().getLongExtra(DATE, 0);
String date = formatDateAbsolute(this, time);
contactName.setText(name);
dateView.setText(date);
// Image View
photoView = findViewById(R.id.photoView);
if (SDK_INT >= 16) {
photoView.setOnClickListener(view -> toggleSystemUi());
window.getDecorView().setSystemUiVisibility(
SYSTEM_UI_FLAG_LAYOUT_STABLE |
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
// Request Listener
RequestListener<Drawable> listener = new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e,
Object model, Target<Drawable> target,
boolean isFirstResource) {
supportStartPostponedEnterTransition();
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model,
Target<Drawable> target, DataSource dataSource,
boolean isFirstResource) {
if (SDK_INT >= 21 && !(resource instanceof Animatable)) {
// set transition name only when not animatable,
// because the animation won't start otherwise
photoView.setTransitionName(
attachment.getTransitionName());
}
// Move image to the top if overlapping toolbar
if (isOverlappingToolbar(resource)) {
photoView.setScaleType(FIT_START);
}
supportStartPostponedEnterTransition();
return false;
}
};
// Load Image
GlideApp.with(this)
.load(attachment)
.diskCacheStrategy(NONE)
.error(R.drawable.ic_image_broken)
.dontTransform()
.addListener(listener)
.into(photoView);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onPullStart() {
appBarLayout.animate()
.alpha(0f)
.start();
}
@Override
public void onPull(float progress) {
layout.getBackground().setAlpha(Math.round((1 - progress) * 255));
}
@Override
public void onPullCancel() {
appBarLayout.animate()
.alpha(1f)
.start();
}
@Override
public void onPullComplete() {
supportFinishAfterTransition();
}
@RequiresApi(api = 16)
private void toggleSystemUi() {
View decorView = getWindow().getDecorView();
if (appBarLayout.getVisibility() == VISIBLE) {
hideSystemUi(decorView);
} else {
showSystemUi(decorView);
}
}
@RequiresApi(api = 16)
private void hideSystemUi(View decorView) {
decorView.setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_STABLE
| SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| SYSTEM_UI_FLAG_FULLSCREEN
);
appBarLayout.animate()
.translationYBy(-1 * appBarLayout.getHeight())
.alpha(0f)
.withEndAction(() -> appBarLayout.setVisibility(GONE))
.start();
}
@RequiresApi(api = 16)
private void showSystemUi(View decorView) {
decorView.setSystemUiVisibility(
SYSTEM_UI_FLAG_LAYOUT_STABLE
| SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
);
appBarLayout.animate()
.translationYBy(appBarLayout.getHeight())
.alpha(1f)
.withStartAction(() -> appBarLayout.setVisibility(VISIBLE))
.start();
}
private boolean isOverlappingToolbar(Drawable drawable) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
float widthPercentage = photoView.getWidth() / (float) width;
float heightPercentage = photoView.getHeight() / (float) height;
float scaleFactor = Math.min(widthPercentage, heightPercentage);
int realWidth = (int) (width * scaleFactor);
int realHeight = (int) (height * scaleFactor);
// return if photo doesn't use the full width,
// because it will be moved to the right otherwise
if (realWidth < photoView.getWidth()) return false;
int drawableTop = (photoView.getHeight() - realHeight) / 2;
return drawableTop < appBarLayout.getBottom() &&
drawableTop != appBarLayout.getTop();
}
}

View File

@@ -0,0 +1,85 @@
package org.briarproject.briar.android.conversation.glide;
import android.support.annotation.Nullable;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.data.DataFetcher;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.android.conversation.AttachmentItem;
import org.briarproject.briar.api.messaging.MessagingManager;
import java.io.InputStream;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
import static com.bumptech.glide.load.DataSource.LOCAL;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
@NotNullByDefault
class BriarDataFetcher implements DataFetcher<InputStream> {
private final static Logger LOG =
getLogger(BriarDataFetcher.class.getName());
private final MessagingManager messagingManager;
@DatabaseExecutor
private final Executor dbExecutor;
private final AttachmentItem attachment;
@Nullable
private volatile InputStream inputStream;
private volatile boolean cancel = false;
@Inject
public BriarDataFetcher(MessagingManager messagingManager,
@DatabaseExecutor Executor dbExecutor, AttachmentItem attachment) {
this.messagingManager = messagingManager;
this.dbExecutor = dbExecutor;
this.attachment = attachment;
}
@Override
public void loadData(Priority priority,
DataCallback<? super InputStream> callback) {
MessageId id = attachment.getMessageId();
dbExecutor.execute(() -> {
if (cancel) return;
try {
inputStream = messagingManager.getAttachment(id).getStream();
callback.onDataReady(inputStream);
} catch (DbException e) {
callback.onLoadFailed(e);
}
});
}
@Override
public void cleanup() {
tryToClose(inputStream, LOG, WARNING);
}
@Override
public void cancel() {
cancel = true;
}
@Override
public Class<InputStream> getDataClass() {
return InputStream.class;
}
@Override
public DataSource getDataSource() {
return LOCAL;
}
}

View File

@@ -0,0 +1,30 @@
package org.briarproject.briar.android.conversation.glide;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.conversation.AttachmentItem;
import org.briarproject.briar.api.messaging.MessagingManager;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@NotNullByDefault
public class BriarDataFetcherFactory {
private final MessagingManager messagingManager;
@DatabaseExecutor
private final Executor dbExecutor;
@Inject
public BriarDataFetcherFactory(MessagingManager messagingManager,
@DatabaseExecutor Executor dbExecutor) {
this.messagingManager = messagingManager;
this.dbExecutor = dbExecutor;
}
BriarDataFetcher createBriarDataFetcher(AttachmentItem model) {
return new BriarDataFetcher(messagingManager, dbExecutor, model);
}
}

View File

@@ -0,0 +1,44 @@
package org.briarproject.briar.android.conversation.glide;
import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.Registry;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.BriarApplication;
import org.briarproject.briar.android.conversation.AttachmentItem;
import java.io.InputStream;
import static android.util.Log.DEBUG;
import static android.util.Log.WARN;
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
@GlideModule
@NotNullByDefault
public final class BriarGlideModule extends AppGlideModule {
@Override
public void registerComponents(Context context, Glide glide,
Registry registry) {
BriarApplication app =
(BriarApplication) context.getApplicationContext();
BriarModelLoaderFactory factory = new BriarModelLoaderFactory(app);
registry.prepend(AttachmentItem.class, InputStream.class, factory);
}
@Override
public void applyOptions(Context context, GlideBuilder builder) {
builder.setLogLevel(IS_DEBUG_BUILD ? DEBUG : WARN);
}
@Override
public boolean isManifestParsingEnabled() {
return false;
}
}

View File

@@ -0,0 +1,16 @@
package org.briarproject.briar.android.conversation.glide;
import android.graphics.Bitmap;
import com.bumptech.glide.load.MultiTransformation;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
public class BriarImageTransformation extends MultiTransformation<Bitmap> {
public BriarImageTransformation(int smallRadius, int radius,
boolean leftCornerSmall, boolean bottomRound) {
super(new CenterCrop(), new ImageCornerTransformation(
smallRadius, radius, leftCornerSmall, bottomRound));
}
}

View File

@@ -0,0 +1,42 @@
package org.briarproject.briar.android.conversation.glide;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.signature.ObjectKey;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.android.BriarApplication;
import org.briarproject.briar.android.conversation.AttachmentItem;
import java.io.InputStream;
import javax.inject.Inject;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public final class BriarModelLoader
implements ModelLoader<AttachmentItem, InputStream> {
@Inject
BriarDataFetcherFactory dataFetcherFactory;
public BriarModelLoader(BriarApplication app) {
app.getApplicationComponent().inject(this);
}
@Override
public LoadData<InputStream> buildLoadData(AttachmentItem model, int width,
int height, Options options) {
ObjectKey key = new ObjectKey(model.getMessageId());
BriarDataFetcher dataFetcher =
dataFetcherFactory.createBriarDataFetcher(model);
return new LoadData<>(key, dataFetcher);
}
@Override
public boolean handles(AttachmentItem model) {
return true;
}
}

View File

@@ -0,0 +1,34 @@
package org.briarproject.briar.android.conversation.glide;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.BriarApplication;
import org.briarproject.briar.android.conversation.AttachmentItem;
import java.io.InputStream;
@NotNullByDefault
class BriarModelLoaderFactory
implements ModelLoaderFactory<AttachmentItem, InputStream> {
private final BriarApplication app;
public BriarModelLoaderFactory(BriarApplication app) {
this.app = app;
}
@Override
public ModelLoader<AttachmentItem, InputStream> build(
MultiModelLoaderFactory multiFactory) {
return new BriarModelLoader(app);
}
@Override
public void teardown() {
// noop
}
}

View File

@@ -0,0 +1,111 @@
package org.briarproject.briar.android.conversation.glide;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.annotation.NonNull;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.security.MessageDigest;
import javax.annotation.concurrent.Immutable;
import static android.graphics.Bitmap.Config.ARGB_8888;
import static android.graphics.Shader.TileMode.CLAMP;
@Immutable
@NotNullByDefault
class ImageCornerTransformation extends BitmapTransformation {
private static final String ID = ImageCornerTransformation.class.getName();
private final int smallRadius, radius;
private final boolean leftCornerSmall, bottomRound;
ImageCornerTransformation(int smallRadius, int radius,
boolean leftCornerSmall, boolean bottomRound) {
this.smallRadius = smallRadius;
this.radius = radius;
this.leftCornerSmall = leftCornerSmall;
this.bottomRound = bottomRound;
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform,
int outWidth, int outHeight) {
int width = toTransform.getWidth();
int height = toTransform.getHeight();
Bitmap bitmap = pool.get(width, height, ARGB_8888);
bitmap.setHasAlpha(true);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(toTransform, CLAMP, CLAMP));
drawRect(canvas, paint, width, height);
return bitmap;
}
private void drawRect(Canvas canvas, Paint paint, float width,
float height) {
drawSmallCorner(canvas, paint, width);
drawBigCorners(canvas, paint, width, height);
}
private void drawSmallCorner(Canvas canvas, Paint paint, float width) {
float left = leftCornerSmall ? 0 : width - radius;
float right = leftCornerSmall ? radius : width;
canvas.drawRoundRect(new RectF(left, 0, right, radius),
smallRadius, smallRadius, paint);
}
private void drawBigCorners(Canvas canvas, Paint paint, float width,
float height) {
float top = bottomRound ? 0 : radius;
RectF rect = new RectF(0, top, width, height);
if (bottomRound) {
canvas.drawRoundRect(rect, radius, radius, paint);
} else {
canvas.drawRect(rect, paint);
canvas.drawRoundRect(new RectF(0, 0, width, radius * 2),
radius, radius, paint);
}
}
@Override
public String toString() {
return "ImageCornerTransformation(smallRadius=" + smallRadius +
", radius=" + radius + ", leftCornerSmall=" + leftCornerSmall +
", bottomRound=" + bottomRound + ")";
}
@Override
public boolean equals(Object o) {
return o instanceof ImageCornerTransformation &&
((ImageCornerTransformation) o).smallRadius == smallRadius &&
((ImageCornerTransformation) o).radius == radius &&
((ImageCornerTransformation) o).leftCornerSmall ==
leftCornerSmall &&
((ImageCornerTransformation) o).bottomRound == bottomRound;
}
@Override
public int hashCode() {
return ID.hashCode() + (smallRadius << 16) ^ (radius << 2) ^
(leftCornerSmall ? 2 : 0) ^ (bottomRound ? 1 : 0);
}
@Override
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
messageDigest.update((ID + '|' + smallRadius + '|' + radius + '|' +
leftCornerSmall + '|' + bottomRound).getBytes(CHARSET));
}
}

View File

@@ -16,6 +16,7 @@ import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes; import android.support.annotation.ColorRes;
import android.support.annotation.MainThread; import android.support.annotation.MainThread;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.design.widget.TextInputLayout; import android.support.design.widget.TextInputLayout;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
@@ -31,6 +32,7 @@ import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan; import android.text.style.ClickableSpan;
import android.text.style.ForegroundColorSpan; import android.text.style.ForegroundColorSpan;
import android.text.style.URLSpan; import android.text.style.URLSpan;
import android.transition.Transition;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View; import android.view.View;
@@ -60,12 +62,16 @@ import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_NO;
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_YES; import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_YES;
import static android.support.v7.app.AppCompatDelegate.setDefaultNightMode; import static android.support.v7.app.AppCompatDelegate.setDefaultNightMode;
import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.FORMAT_ABBREV_ALL;
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE; import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE;
import static android.text.format.DateUtils.FORMAT_ABBREV_TIME; import static android.text.format.DateUtils.FORMAT_ABBREV_TIME;
import static android.text.format.DateUtils.FORMAT_SHOW_DATE; import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
import static android.text.format.DateUtils.FORMAT_SHOW_TIME;
import static android.text.format.DateUtils.FORMAT_SHOW_YEAR;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static android.text.format.DateUtils.YEAR_IN_MILLIS;
import static android.view.KeyEvent.ACTION_DOWN; import static android.view.KeyEvent.ACTION_DOWN;
import static android.view.KeyEvent.KEYCODE_ENTER; import static android.view.KeyEvent.KEYCODE_ENTER;
import static android.view.inputmethod.EditorInfo.IME_NULL; import static android.view.inputmethod.EditorInfo.IME_NULL;
@@ -117,6 +123,13 @@ public class UiUtils {
MIN_DATE_RESOLUTION, flags).toString(); MIN_DATE_RESOLUTION, flags).toString();
} }
public static String formatDateAbsolute(Context ctx, long time) {
int flags = FORMAT_SHOW_TIME | FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL;
long diff = System.currentTimeMillis() - time;
if (diff >= YEAR_IN_MILLIS) flags |= FORMAT_SHOW_YEAR;
return DateUtils.formatDateTime(ctx, time, flags);
}
public static int getDaysUntilExpiry() { public static int getDaysUntilExpiry() {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
long daysBeforeExpiry = (EXPIRY_DATE - now) / 1000 / 60 / 60 / 24; long daysBeforeExpiry = (EXPIRY_DATE - now) / 1000 / 60 / 60 / 24;
@@ -318,6 +331,12 @@ public class UiUtils {
keyEvent.getKeyCode() == KEYCODE_ENTER; keyEvent.getKeyCode() == KEYCODE_ENTER;
} }
@RequiresApi(api = 21)
public static void excludeSystemUi(Transition transition) {
transition.excludeTarget(android.R.id.statusBarBackground, true);
transition.excludeTarget(android.R.id.navigationBarBackground, true);
}
/** /**
* Observes the given {@link LiveData} until the first change. * Observes the given {@link LiveData} until the first change.
* If the LiveData's value is available, the {@link Observer} will be * If the LiveData's value is available, the {@link Observer} will be

View File

@@ -0,0 +1,161 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 XiNGRZ <xxx@oxo.ooo>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.briarproject.briar.android.view;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public class PullDownLayout extends FrameLayout {
private final ViewDragHelper dragger;
private final int minimumFlingVelocity;
@Nullable
private Callback callback;
public PullDownLayout(Context context) {
this(context, null);
}
public PullDownLayout(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public PullDownLayout(Context context, @Nullable AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
dragger = ViewDragHelper.create(this, 1f / 8f, new ViewDragCallback());
minimumFlingVelocity =
ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
}
public void setCallback(@Nullable Callback callback) {
this.callback = callback;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return dragger.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
dragger.processTouchEvent(event);
return true;
}
@Override
public void computeScroll() {
if (dragger.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
public interface Callback {
void onPullStart();
void onPull(float progress);
void onPullCancel();
void onPullComplete();
}
private class ViewDragCallback extends ViewDragHelper.Callback {
@Override
public boolean tryCaptureView(View child, int pointerId) {
return true;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return 0;
}
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return Math.max(0, top);
}
@Override
public int getViewHorizontalDragRange(View child) {
return 0;
}
@Override
public int getViewVerticalDragRange(View child) {
return getHeight();
}
@Override
public void onViewCaptured(View capturedChild, int activePointerId) {
if (callback != null) {
callback.onPullStart();
}
}
@Override
public void onViewPositionChanged(View changedView, int left, int top,
int dx, int dy) {
if (callback != null) {
callback.onPull((float) top / (float) getHeight());
}
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
int slop = yvel > minimumFlingVelocity ? getHeight() / 6 :
getHeight() / 3;
if (releasedChild.getTop() > slop) {
if (callback != null) {
callback.onPullComplete();
}
} else {
if (callback != null) {
callback.onPullCancel();
}
dragger.settleCapturedViewAt(0, 0);
invalidate();
}
}
}
}

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#808080"
android:pathData="M21,5v6.59l-3,-3.01 -4,4.01 -4,-4 -4,4 -3,-3.01L3,5c0,-1.1 0.9,-2 2,-2h14c1.1,0 2,0.9 2,2zM18,11.42l3,3.01L21,19c0,1.1 -0.9,2 -2,2L5,21c-1.1,0 -2,-0.9 -2,-2v-6.58l3,2.99 4,-4 4,4 4,-3.99z"/>
</vector>

View File

@@ -8,10 +8,10 @@
android:topLeftRadius="@dimen/message_bubble_radius_top_inner" android:topLeftRadius="@dimen/message_bubble_radius_top_inner"
android:topRightRadius="@dimen/message_bubble_radius_top_outer"/> android:topRightRadius="@dimen/message_bubble_radius_top_outer"/>
<padding <padding
android:bottom="@dimen/message_bubble_padding_bottom" android:bottom="@dimen/message_bubble_border"
android:left="@dimen/message_bubble_padding_sides" android:left="@dimen/message_bubble_border"
android:right="@dimen/message_bubble_padding_sides" android:right="@dimen/message_bubble_border"
android:top="@dimen/message_bubble_padding_top"/> android:top="@dimen/message_bubble_border"/>
<solid <solid
android:color="@color/msg_in"/> android:color="@color/msg_in"/>
<stroke <stroke

View File

@@ -8,10 +8,10 @@
android:topLeftRadius="@dimen/message_bubble_radius_top_outer" android:topLeftRadius="@dimen/message_bubble_radius_top_outer"
android:topRightRadius="@dimen/message_bubble_radius_top_inner"/> android:topRightRadius="@dimen/message_bubble_radius_top_inner"/>
<padding <padding
android:bottom="@dimen/message_bubble_padding_bottom" android:bottom="@dimen/message_bubble_border"
android:left="@dimen/message_bubble_padding_sides" android:left="@dimen/message_bubble_border"
android:right="@dimen/message_bubble_padding_sides" android:right="@dimen/message_bubble_border"
android:top="@dimen/message_bubble_padding_top"/> android:top="@dimen/message_bubble_border"/>
<solid <solid
android:color="@color/msg_out"/> android:color="@color/msg_out"/>
<stroke <stroke

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:radius="@dimen/message_bubble_radius_big"/>
<padding
android:bottom="3dp"
android:left="7dp"
android:right="7dp"
android:top="2dp"/>
<solid
android:color="@color/msg_status_bubble_background"/>
</shape>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<org.briarproject.briar.android.view.PullDownLayout
android:id="@+id/layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/briar_black"
tools:context=".android.conversation.ImageActivity">
<com.github.chrisbanes.photoview.PhotoView
android:id="@+id/photoView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="ContentDescription"
tools:srcCompat="@tools:sample/backgrounds/scenic"/>
<android.support.design.widget.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/msg_status_bubble_background">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
style="@style/BriarToolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/msg_status_bubble_background"
android:fitsSystemWindows="true">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/contactName"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/action_bar_text"
tools:text="Contact Name of someone who chose a long name"/>
<TextView
android:id="@+id/dateView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/action_bar_text"
tools:text="date"/>
</LinearLayout>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
</org.briarproject.briar.android.view.PullDownLayout>

View File

@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
android:id="@+id/layout"
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="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/message_bubble_margin"
android:layout_marginEnd="@dimen/message_bubble_margin_non_tail"
android:layout_marginLeft="@dimen/message_bubble_margin_tail"
android:layout_marginRight="@dimen/message_bubble_margin_non_tail"
android:layout_marginStart="@dimen/message_bubble_margin_tail"
android:layout_marginTop="@dimen/message_bubble_margin"
android:background="@drawable/msg_in"
android:elevation="@dimen/message_bubble_elevation">
<ImageView
android:id="@+id/imageView"
android:layout_width="@dimen/message_bubble_image_default"
android:layout_height="@dimen/message_bubble_image_default"
app:layout_constraintBottom_toTopOf="@+id/text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription"
tools:src="@drawable/alerts_and_states_error"/>
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/text"
style="@style/TextMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/message_bubble_padding_bottom_inner"
android:layout_marginLeft="@dimen/message_bubble_padding_sides_inner"
android:layout_marginRight="@dimen/message_bubble_padding_sides_inner"
android:layout_marginTop="@dimen/message_bubble_padding_top_inner"
android:textColor="?android:attr/textColorPrimary"
android:visibility="gone"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@+id/statusLayout"
app:layout_constraintEnd_toEndOf="@+id/imageView"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
tools:text="The text of a message which can sometimes be a bit longer as well"/>
<LinearLayout
android:id="@+id/statusLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/message_bubble_padding_top"
android:layout_marginLeft="@dimen/message_bubble_padding_sides_inner"
android:layout_marginRight="@dimen/message_bubble_padding_sides_inner"
android:background="@drawable/msg_status_bubble"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="@+id/imageView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent">
<TextView
android:id="@+id/time"
style="@style/TextMessage.Timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Dec 24, 13:37"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout>

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
android:id="@+id/layout"
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="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/message_bubble_margin"
android:layout_marginEnd="@dimen/message_bubble_margin_non_tail"
android:layout_marginLeft="@dimen/message_bubble_margin_tail"
android:layout_marginRight="@dimen/message_bubble_margin_non_tail"
android:layout_marginStart="@dimen/message_bubble_margin_tail"
android:layout_marginTop="@dimen/message_bubble_margin"
android:background="@drawable/msg_in"
android:elevation="@dimen/message_bubble_elevation">
<ImageView
android:id="@+id/imageView"
android:layout_width="@dimen/message_bubble_image_default"
android:layout_height="@dimen/message_bubble_image_default"
app:layout_constraintBottom_toTopOf="@+id/text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription"
tools:src="@drawable/alerts_and_states_error"/>
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/text"
style="@style/TextMessage"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/message_bubble_padding_bottom_inner"
android:layout_marginLeft="@dimen/message_bubble_padding_sides_inner"
android:layout_marginRight="@dimen/message_bubble_padding_sides_inner"
android:layout_marginTop="@dimen/message_bubble_padding_top_inner"
android:textColor="?android:attr/textColorPrimary"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@+id/statusLayout"
app:layout_constraintEnd_toEndOf="@+id/imageView"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
tools:text="The text of a message which can sometimes be a bit longer as well"/>
<LinearLayout
android:id="@+id/statusLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/message_bubble_padding_bottom_inner"
android:layout_marginLeft="@dimen/message_bubble_padding_sides_inner"
android:layout_marginRight="@dimen/message_bubble_padding_sides_inner"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text">
<TextView
android:id="@+id/time"
style="@style/TextMessage.Timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Dec 24, 13:37"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout>

View File

@@ -15,29 +15,55 @@
android:background="@drawable/msg_in" android:background="@drawable/msg_in"
android:elevation="@dimen/message_bubble_elevation"> android:elevation="@dimen/message_bubble_elevation">
<ImageView
android:id="@+id/imageView"
android:layout_width="@dimen/message_bubble_image_default"
android:layout_height="@dimen/message_bubble_image_default"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription"/>
<com.vanniktech.emoji.EmojiTextView <com.vanniktech.emoji.EmojiTextView
android:id="@+id/text" android:id="@+id/text"
style="@style/TextMessage" style="@style/TextMessage"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/message_bubble_padding_bottom_inner"
android:layout_marginLeft="@dimen/message_bubble_padding_sides_inner"
android:layout_marginRight="@dimen/message_bubble_padding_sides_inner"
android:layout_marginTop="@dimen/message_bubble_padding_top_inner"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
app:layout_constraintBottom_toTopOf="@+id/time" app:layout_constrainedWidth="true"
app:layout_constraintEnd_toEndOf="@+id/time" app:layout_constraintBottom_toTopOf="@+id/statusLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toBottomOf="@+id/imageView"
tools:text="Short message"/> tools:text="The text of a message which can sometimes be a bit longer as well"/>
<TextView <LinearLayout
android:id="@+id/time" android:id="@+id/statusLayout"
style="@style/TextMessage.Timestamp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/message_bubble_padding_bottom_inner"
android:layout_marginLeft="@dimen/message_bubble_padding_sides_inner"
android:layout_marginRight="@dimen/message_bubble_padding_sides_inner"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0" app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text" app:layout_constraintTop_toBottomOf="@+id/text">
tools:text="Dec 24, 13:37"/>
<TextView
android:id="@+id/time"
style="@style/TextMessage.Timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Dec 24, 13:37"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>

View File

@@ -21,39 +21,68 @@
android:background="@drawable/msg_out" android:background="@drawable/msg_out"
android:elevation="@dimen/message_bubble_elevation"> android:elevation="@dimen/message_bubble_elevation">
<ImageView
android:id="@+id/imageView"
android:layout_width="@dimen/message_bubble_image_default"
android:layout_height="@dimen/message_bubble_image_default"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/text"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription"
tools:src="@drawable/alerts_and_states_error"/>
<com.vanniktech.emoji.EmojiTextView <com.vanniktech.emoji.EmojiTextView
android:id="@+id/text" android:id="@+id/text"
style="@style/TextMessage" style="@style/TextMessage"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/message_bubble_padding_bottom_inner"
android:layout_marginLeft="@dimen/message_bubble_padding_sides_inner"
android:layout_marginRight="@dimen/message_bubble_padding_sides_inner"
android:layout_marginTop="@dimen/message_bubble_padding_top_inner"
android:textColor="@color/briar_text_primary_inverse" android:textColor="@color/briar_text_primary_inverse"
app:layout_constraintBottom_toTopOf="@+id/time" app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@+id/statusLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toBottomOf="@+id/imageView"
tools:text="This is a long long long message that spans over several lines.\n\nIt ends here."/> tools:text="This is a long long long message that spans over several lines.\n\nIt ends here."/>
<TextView <LinearLayout
android:id="@+id/time" android:id="@+id/statusLayout"
style="@style/TextMessage.Timestamp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/message_bubble_timestamp_margin" android:layout_marginBottom="@dimen/message_bubble_padding_bottom_inner"
android:textColor="@color/private_message_date_inverse" android:layout_marginLeft="@dimen/message_bubble_padding_sides_inner"
android:layout_marginRight="@dimen/message_bubble_padding_sides_inner"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text" app:layout_constraintTop_toBottomOf="@+id/text"
tools:text="Dec 24, 13:37"/> tools:ignore="UseCompoundDrawables">
<ImageView <TextView
android:id="@+id/status" android:id="@+id/time"
android:layout_width="wrap_content" style="@style/TextMessage.Timestamp"
android:layout_height="wrap_content" android:layout_width="wrap_content"
android:layout_marginLeft="@dimen/margin_medium" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_medium" android:layout_marginEnd="6dp"
app:layout_constraintBottom_toBottomOf="@+id/time" android:layout_marginRight="6dp"
app:layout_constraintStart_toEndOf="@+id/time" android:textColor="@color/private_message_date_inverse"
app:layout_constraintTop_toTopOf="@+id/time" tools:text="Dec 24, 13:37"/>
tools:ignore="ContentDescription"
tools:src="@drawable/message_delivered"/> <ImageView
android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="ContentDescription"
tools:src="@drawable/message_delivered"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>

View File

@@ -40,6 +40,7 @@
<color name="msg_stroke_light">#cbcbcb</color> <color name="msg_stroke_light">#cbcbcb</color>
<color name="msg_stroke_dark">#333333</color> <color name="msg_stroke_dark">#333333</color>
<color name="msg_stroke">@color/msg_stroke_light</color> <color name="msg_stroke">@color/msg_stroke_light</color>
<color name="msg_status_bubble_background">#66000000</color>
<!-- text colors --> <!-- text colors -->
<color name="briar_text_link">@color/briar_blue_light</color> <color name="briar_text_link">@color/briar_blue_light</color>

View File

@@ -43,9 +43,18 @@
<dimen name="message_bubble_radius_top_inner">@dimen/message_bubble_radius_small</dimen> <dimen name="message_bubble_radius_top_inner">@dimen/message_bubble_radius_small</dimen>
<dimen name="message_bubble_radius_top_outer">@dimen/message_bubble_radius_big</dimen> <dimen name="message_bubble_radius_top_outer">@dimen/message_bubble_radius_big</dimen>
<dimen name="message_bubble_margin">6dp</dimen> <dimen name="message_bubble_margin">6dp</dimen>
<dimen name="message_bubble_image_default">210dp</dimen>
<dimen name="message_bubble_image_min_width">150dp</dimen>
<dimen name="message_bubble_image_max_width">240dp</dimen>
<dimen name="message_bubble_image_min_height">100dp</dimen>
<dimen name="message_bubble_image_max_height">320dp</dimen>
<dimen name="message_bubble_border">2dp</dimen>
<dimen name="message_bubble_padding_sides">12dp</dimen> <dimen name="message_bubble_padding_sides">12dp</dimen>
<dimen name="message_bubble_padding_sides_inner">10dp</dimen>
<dimen name="message_bubble_padding_top">6dp</dimen> <dimen name="message_bubble_padding_top">6dp</dimen>
<dimen name="message_bubble_padding_top_inner">4dp</dimen>
<dimen name="message_bubble_padding_bottom">4dp</dimen> <dimen name="message_bubble_padding_bottom">4dp</dimen>
<dimen name="message_bubble_padding_bottom_inner">2dp</dimen>
<dimen name="message_bubble_timestamp_margin">4dp</dimen> <dimen name="message_bubble_timestamp_margin">4dp</dimen>
<dimen name="message_bubble_elevation">2dp</dimen> <dimen name="message_bubble_elevation">2dp</dimen>
<dimen name="message_bubble_margin_tail">8dp</dimen> <dimen name="message_bubble_margin_tail">8dp</dimen>

View File

@@ -134,6 +134,8 @@
<string name="dialog_title_delete_contact">Confirm Contact Deletion</string> <string name="dialog_title_delete_contact">Confirm Contact Deletion</string>
<string name="dialog_message_delete_contact">Are you sure that you want to remove this contact and all messages exchanged with this contact?</string> <string name="dialog_message_delete_contact">Are you sure that you want to remove this contact and all messages exchanged with this contact?</string>
<string name="contact_deleted_toast">Contact deleted</string> <string name="contact_deleted_toast">Contact deleted</string>
<!-- This is shown in the action bar when opening an image in fullscreen that the user sent -->
<string name="you">You</string>
<!-- Adding Contacts --> <!-- Adding Contacts -->
<string name="add_contact_title">Add a Contact</string> <string name="add_contact_title">Add a Contact</string>

View File

@@ -18,6 +18,12 @@
<item name="toolbarStyle">@style/BriarToolbar</item> <item name="toolbarStyle">@style/BriarToolbar</item>
</style> </style>
<style name="BriarTheme.Transparent.NoActionBar" parent="BriarTheme.NoActionBar">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowActionBarOverlay">true</item>
</style>
<style name="ActivityAnimation" parent="@android:style/Animation.Activity"> <style name="ActivityAnimation" parent="@android:style/Animation.Activity">
<item name="android:activityOpenEnterAnimation">@anim/screen_new_in</item> <item name="android:activityOpenEnterAnimation">@anim/screen_new_in</item>
<item name="android:activityOpenExitAnimation">@anim/screen_old_out</item> <item name="android:activityOpenExitAnimation">@anim/screen_old_out</item>

View File

@@ -36,6 +36,7 @@ dependencyVerification {
'com.android.support:design:28.0.0:design-28.0.0.aar:7874ad1904eedc74aa41cffffb7f759d8990056f3bbbc9264911651c67c42f5f', 'com.android.support:design:28.0.0:design-28.0.0.aar:7874ad1904eedc74aa41cffffb7f759d8990056f3bbbc9264911651c67c42f5f',
'com.android.support:documentfile:28.0.0:documentfile-28.0.0.aar:47cdcd3e9302b7b064923f05487a5c03babbd9bbda4726b71e97791fab5d4779', 'com.android.support:documentfile:28.0.0:documentfile-28.0.0.aar:47cdcd3e9302b7b064923f05487a5c03babbd9bbda4726b71e97791fab5d4779',
'com.android.support:drawerlayout:28.0.0:drawerlayout-28.0.0.aar:8f6809afae4793550c37461c9810e954ae6a23dbb4d23e5333bf18148df1150a', 'com.android.support:drawerlayout:28.0.0:drawerlayout-28.0.0.aar:8f6809afae4793550c37461c9810e954ae6a23dbb4d23e5333bf18148df1150a',
'com.android.support:exifinterface:28.0.0:exifinterface-28.0.0.aar:bbf44e519edd6333a24a3285aa21fd00181b920b81ca8aa89a8899f03ab4d6b0',
'com.android.support:interpolator:28.0.0:interpolator-28.0.0.aar:7bc7ee86a0db39a4b51956f3e89842d2bd962118d57d779eb6ed6b34ba0677ea', 'com.android.support:interpolator:28.0.0:interpolator-28.0.0.aar:7bc7ee86a0db39a4b51956f3e89842d2bd962118d57d779eb6ed6b34ba0677ea',
'com.android.support:loader:28.0.0:loader-28.0.0.aar:920b85efd72dc33e915b0f88a883fe73b88483c6df8751a741e17611f2460341', 'com.android.support:loader:28.0.0:loader-28.0.0.aar:920b85efd72dc33e915b0f88a883fe73b88483c6df8751a741e17611f2460341',
'com.android.support:localbroadcastmanager:28.0.0:localbroadcastmanager-28.0.0.aar:d287c823af5fdde72c099fcfc5f630efe9687af7a914343ae6fd92de32c8a806', 'com.android.support:localbroadcastmanager:28.0.0:localbroadcastmanager-28.0.0.aar:d287c823af5fdde72c099fcfc5f630efe9687af7a914343ae6fd92de32c8a806',
@@ -84,6 +85,11 @@ dependencyVerification {
'com.android.tools:repository:26.2.1:repository-26.2.1.jar:fa74dae09103faef703df38550ad8fa244c5b6d1bf90d6198be932292b3d9cc1', '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: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.android.tools:sdklib:26.2.1:sdklib-26.2.1.jar:248df7ad5eac4aeb6f96c394c76760de4b7b89ac056e54d0c21a739368b91b45',
'com.github.bumptech.glide:annotations:4.8.0:annotations-4.8.0.jar:4ea82e59874673105165820336c6ac268fc46149892486aad8e7a131a4449446',
'com.github.bumptech.glide:compiler:4.8.0:compiler-4.8.0.jar:1fa93dd0cf7ef0b8b98a59a67a1ee84915416c2d677d83a771ea3e32ad15e6bf',
'com.github.bumptech.glide:gifdecoder:4.8.0:gifdecoder-4.8.0.aar:b00c5454a023a9488ea49603930d9c25e09192e5ceaadf64977aa52946b3c1b4',
'com.github.bumptech.glide:glide:4.8.0:glide-4.8.0.aar:5ddf08b12cc43332e812988f16c2c39e7fce49d1c4d94b7948dcde7f00bf49d6',
'com.github.chrisbanes:PhotoView:2.1.4:PhotoView-2.1.4.aar:04cb397fcb3df0757c8aed6927ebdd247930b5c78ee9acc59cd07dccdaaf3460',
'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.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.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', 'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',

View File

@@ -12,7 +12,6 @@ import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataParser; import org.briarproject.bramble.api.data.MetadataParser;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.MessageDeletedException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
@@ -201,8 +200,6 @@ class IntroductionManagerImpl extends ConversationClientImpl
} }
// Store the updated session // Store the updated session
storeSession(txn, storageId, session); storeSession(txn, storageId, session);
// FIXME
db.deleteMessage(txn, m.getId());
return false; return false;
} }
@@ -423,22 +420,15 @@ class IntroductionManagerImpl extends ConversationClientImpl
StoredSession ss = getSession(txn, meta.getSessionId()); StoredSession ss = getSession(txn, meta.getSessionId());
if (ss == null) throw new AssertionError(); if (ss == null) throw new AssertionError();
MessageType type = meta.getMessageType(); MessageType type = meta.getMessageType();
try { if (type == REQUEST) {
if (type == REQUEST) { messages.add(parseInvitationRequest(txn, contactGroupId, m,
messages.add(parseInvitationRequest(txn, meta, status, meta.getSessionId(), authorInfos));
contactGroupId, m, meta, status, } else if (type == ACCEPT) {
meta.getSessionId(), authorInfos)); messages.add(parseInvitationResponse(txn, contactGroupId, m,
} else if (type == ACCEPT) { meta, status, ss.bdfSession, authorInfos, true));
messages.add(parseInvitationResponse(txn, } else if (type == DECLINE) {
contactGroupId, m, meta, status, messages.add(parseInvitationResponse(txn, contactGroupId, m,
ss.bdfSession, authorInfos, true)); meta, status, ss.bdfSession, authorInfos, false));
} else if (type == DECLINE) {
messages.add(parseInvitationResponse(txn,
contactGroupId, m, meta, status,
ss.bdfSession, authorInfos, false));
}
} catch (MessageDeletedException ex) {
// FIXME
} }
} }
return messages; return messages;

View File

@@ -127,8 +127,7 @@ class MessagingManagerImpl extends ConversationClientImpl
new PrivateMessageReceivedEvent(header, contactId); new PrivateMessageReceivedEvent(header, contactId);
txn.attach(event); txn.attach(event);
messageTracker.trackIncomingMessage(txn, m); messageTracker.trackIncomingMessage(txn, m);
// FIXME
db.deleteMessage(txn, m.getId());
// don't share message // don't share message
return false; return false;
} }

View File

@@ -32,10 +32,6 @@ import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT_ID; import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT_ID;
import static org.briarproject.briar.api.privategroup.PrivateGroupManager.MAJOR_VERSION; import static org.briarproject.briar.api.privategroup.PrivateGroupManager.MAJOR_VERSION;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.GROUP_KEY_CONTACT_ID; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_DESCRIPTOR;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_PRIVATE_GROUP_ID;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_SIGNATURE;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_TIMESTAMP;
import static org.briarproject.briar.privategroup.invitation.MessageType.ABORT; import static org.briarproject.briar.privategroup.invitation.MessageType.ABORT;
import static org.briarproject.briar.privategroup.invitation.MessageType.INVITE; import static org.briarproject.briar.privategroup.invitation.MessageType.INVITE;
import static org.briarproject.briar.privategroup.invitation.MessageType.JOIN; import static org.briarproject.briar.privategroup.invitation.MessageType.JOIN;
@@ -200,29 +196,20 @@ abstract class AbstractProtocolEngine<S extends Session>
void subscribeToPrivateGroup(Transaction txn, MessageId inviteId) void subscribeToPrivateGroup(Transaction txn, MessageId inviteId)
throws DbException, FormatException { throws DbException, FormatException {
BdfDictionary meta = InviteMessage invite = messageParser.getInviteMessage(txn, inviteId);
clientHelper.getMessageMetadataAsDictionary(txn, inviteId); PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup(
GroupId groupId = new GroupId(meta.getRaw(MSG_KEY_PRIVATE_GROUP_ID)); invite.getGroupName(), invite.getCreator(), invite.getSalt());
byte[] descriptor = meta.getRaw(MSG_KEY_DESCRIPTOR);
byte[] signature = meta.getRaw(MSG_KEY_SIGNATURE);
long inviteTimestamp = meta.getLong(MSG_KEY_TIMESTAMP);
Group g = getPrivateGroup(groupId, descriptor);
PrivateGroup privateGroup = privateGroupFactory.parsePrivateGroup(g);
long timestamp = long timestamp =
Math.max(clock.currentTimeMillis(), inviteTimestamp + 1); Math.max(clock.currentTimeMillis(), invite.getTimestamp() + 1);
// TODO: Create the join message on the crypto executor // TODO: Create the join message on the crypto executor
LocalAuthor member = identityManager.getLocalAuthor(txn); LocalAuthor member = identityManager.getLocalAuthor(txn);
GroupMessage joinMessage = groupMessageFactory.createJoinMessage( GroupMessage joinMessage = groupMessageFactory.createJoinMessage(
privateGroup.getId(), timestamp, member, inviteTimestamp, privateGroup.getId(), timestamp, member, invite.getTimestamp(),
signature); invite.getSignature());
privateGroupManager privateGroupManager
.addPrivateGroup(txn, privateGroup, joinMessage, false); .addPrivateGroup(txn, privateGroup, joinMessage, false);
} }
private Group getPrivateGroup(GroupId id, byte[] descriptor) {
return new Group(id, CLIENT_ID, MAJOR_VERSION, descriptor);
}
long getLocalTimestamp(S session) { long getLocalTimestamp(S session) {
return Math.max(clock.currentTimeMillis(), return Math.max(clock.currentTimeMillis(),
Math.max(session.getLocalTimestamp(), Math.max(session.getLocalTimestamp(),

View File

@@ -8,8 +8,6 @@ interface GroupInvitationConstants {
// Message metadata keys // Message metadata keys
String MSG_KEY_MESSAGE_TYPE = "messageType"; String MSG_KEY_MESSAGE_TYPE = "messageType";
String MSG_KEY_PRIVATE_GROUP_ID = "privateGroupId"; String MSG_KEY_PRIVATE_GROUP_ID = "privateGroupId";
String MSG_KEY_DESCRIPTOR = "descriptor";
String MSG_KEY_SIGNATURE = "signature";
String MSG_KEY_TIMESTAMP = "timestamp"; String MSG_KEY_TIMESTAMP = "timestamp";
String MSG_KEY_LOCAL = "local"; String MSG_KEY_LOCAL = "local";
String MSG_KEY_VISIBLE_IN_UI = "visibleInUi"; String MSG_KEY_VISIBLE_IN_UI = "visibleInUi";

View File

@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataParser; import org.briarproject.bramble.api.data.MetadataParser;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.MessageDeletedException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
@@ -168,8 +167,6 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
} }
// Store the updated session // Store the updated session
storeSession(txn, storageId, session); storeSession(txn, storageId, session);
// FIXME
db.deleteMessage(txn, m.getId());
return false; return false;
} }
@@ -387,19 +384,15 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
messageParser.parseMetadata(e.getValue()); messageParser.parseMetadata(e.getValue());
MessageStatus status = db.getMessageStatus(txn, c, m); MessageStatus status = db.getMessageStatus(txn, c, m);
MessageType type = meta.getMessageType(); MessageType type = meta.getMessageType();
try { if (type == INVITE) {
if (type == INVITE) { messages.add(parseInvitationRequest(txn, contactGroupId, m,
messages.add(parseInvitationRequest(txn, meta, status));
contactGroupId, m, meta, status)); } else if (type == JOIN) {
} else if (type == JOIN) { messages.add(parseInvitationResponse(contactGroupId, m,
messages.add(parseInvitationResponse(contactGroupId, m, meta, status, true));
meta, status, true)); } else if (type == LEAVE) {
} else if (type == LEAVE) { messages.add(parseInvitationResponse(contactGroupId, m,
messages.add(parseInvitationResponse(contactGroupId, m, meta, status, false));
meta, status, false));
}
} catch (MessageDeletedException ex) {
// FIXME
} }
} }
return messages; return messages;
@@ -448,13 +441,8 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
Map<MessageId, BdfDictionary> results = Map<MessageId, BdfDictionary> results =
clientHelper.getMessageMetadataAsDictionary(txn, clientHelper.getMessageMetadataAsDictionary(txn,
contactGroupId, query); contactGroupId, query);
for (MessageId m : results.keySet()) { for (MessageId m : results.keySet())
try { items.add(parseGroupInvitationItem(txn, c, m));
items.add(parseGroupInvitationItem(txn, c, m));
} catch (MessageDeletedException e) {
// FIXME
}
}
} }
db.commitTransaction(txn); db.commitTransaction(txn);
} catch (FormatException e) { } catch (FormatException e) {

View File

@@ -31,6 +31,7 @@ import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH; import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH;
import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory.SIGNING_LABEL_INVITE; import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory.SIGNING_LABEL_INVITE;
import static org.briarproject.briar.privategroup.invitation.MessageType.ABORT; import static org.briarproject.briar.privategroup.invitation.MessageType.ABORT;
import static org.briarproject.briar.privategroup.invitation.MessageType.INVITE;
import static org.briarproject.briar.privategroup.invitation.MessageType.JOIN; import static org.briarproject.briar.privategroup.invitation.MessageType.JOIN;
import static org.briarproject.briar.privategroup.invitation.MessageType.LEAVE; import static org.briarproject.briar.privategroup.invitation.MessageType.LEAVE;
@@ -99,9 +100,9 @@ class GroupInvitationValidator extends BdfMessageValidator {
throw new FormatException(); throw new FormatException();
} }
// Create the metadata // Create the metadata
BdfDictionary meta = messageEncoder.encodeInviteMetadata( BdfDictionary meta = messageEncoder.encodeMetadata(INVITE,
privateGroup.getId(), privateGroup.getGroup().getDescriptor(), privateGroup.getId(), m.getTimestamp(), false, false, false,
signature, m.getTimestamp(), false, false, false, false, false); false, false);
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} }

View File

@@ -12,10 +12,6 @@ import javax.annotation.Nullable;
@NotNullByDefault @NotNullByDefault
interface MessageEncoder { interface MessageEncoder {
BdfDictionary encodeInviteMetadata(GroupId privateGroupId,
byte[] descriptor, byte[] signature, long timestamp, boolean local,
boolean read, boolean visible, boolean available, boolean accepted);
BdfDictionary encodeMetadata(MessageType type, GroupId privateGroupId, BdfDictionary encodeMetadata(MessageType type, GroupId privateGroupId,
long timestamp, boolean local, boolean read, boolean visible, long timestamp, boolean local, boolean read, boolean visible,
boolean available, boolean accepted); boolean available, boolean accepted);

View File

@@ -17,12 +17,10 @@ import javax.inject.Inject;
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ; import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_AVAILABLE_TO_ANSWER; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_AVAILABLE_TO_ANSWER;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_DESCRIPTOR;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_INVITATION_ACCEPTED; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_INVITATION_ACCEPTED;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_LOCAL; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_MESSAGE_TYPE; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_MESSAGE_TYPE;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_PRIVATE_GROUP_ID; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_PRIVATE_GROUP_ID;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_SIGNATURE;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_TIMESTAMP; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_TIMESTAMP;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_VISIBLE_IN_UI; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.MSG_KEY_VISIBLE_IN_UI;
import static org.briarproject.briar.privategroup.invitation.MessageType.ABORT; import static org.briarproject.briar.privategroup.invitation.MessageType.ABORT;
@@ -44,25 +42,6 @@ class MessageEncoderImpl implements MessageEncoder {
this.messageFactory = messageFactory; this.messageFactory = messageFactory;
} }
@Override
public BdfDictionary encodeInviteMetadata(GroupId privateGroupId,
byte[] descriptor, byte[] signature, long timestamp, boolean local,
boolean read, boolean visible, boolean available,
boolean accepted) {
BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_MESSAGE_TYPE, INVITE.getValue());
meta.put(MSG_KEY_PRIVATE_GROUP_ID, privateGroupId);
meta.put(MSG_KEY_DESCRIPTOR, descriptor);
meta.put(MSG_KEY_SIGNATURE, signature);
meta.put(MSG_KEY_TIMESTAMP, timestamp);
meta.put(MSG_KEY_LOCAL, local);
meta.put(MSG_KEY_READ, read);
meta.put(MSG_KEY_VISIBLE_IN_UI, visible);
meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, available);
meta.put(MSG_KEY_INVITATION_ACCEPTED, accepted);
return meta;
}
@Override @Override
public BdfDictionary encodeMetadata(MessageType type, public BdfDictionary encodeMetadata(MessageType type,
GroupId privateGroupId, long timestamp, boolean local, boolean read, GroupId privateGroupId, long timestamp, boolean local, boolean read,

View File

@@ -3,8 +3,6 @@ package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
@@ -27,7 +25,6 @@ import javax.inject.Inject;
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID; import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
import static org.briarproject.briar.api.blog.BlogManager.MAJOR_VERSION; import static org.briarproject.briar.api.blog.BlogManager.MAJOR_VERSION;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_DESCRIPTOR;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -87,10 +84,9 @@ class BlogProtocolEngineImpl extends ProtocolEngineImpl<Blog> {
@Override @Override
protected void addShareable(Transaction txn, MessageId inviteId) protected void addShareable(Transaction txn, MessageId inviteId)
throws DbException, FormatException { throws DbException, FormatException {
BdfDictionary meta = InviteMessage<Blog> invite =
clientHelper.getMessageMetadataAsDictionary(txn, inviteId); messageParser.getInviteMessage(txn, inviteId);
BdfList descriptor = meta.getList(MSG_KEY_DESCRIPTOR); blogManager.addBlog(txn, invite.getShareable());
blogManager.addBlog(txn, messageParser.createShareable(descriptor));
} }
} }

View File

@@ -3,8 +3,6 @@ package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
@@ -15,19 +13,18 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.conversation.ConversationRequest;
import org.briarproject.briar.api.forum.Forum; import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumInvitationResponse; import org.briarproject.briar.api.forum.ForumInvitationResponse;
import org.briarproject.briar.api.forum.ForumManager; import org.briarproject.briar.api.forum.ForumManager;
import org.briarproject.briar.api.forum.event.ForumInvitationRequestReceivedEvent; import org.briarproject.briar.api.forum.event.ForumInvitationRequestReceivedEvent;
import org.briarproject.briar.api.forum.event.ForumInvitationResponseReceivedEvent; import org.briarproject.briar.api.forum.event.ForumInvitationResponseReceivedEvent;
import org.briarproject.briar.api.conversation.ConversationRequest;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.briar.api.forum.ForumManager.CLIENT_ID; import static org.briarproject.briar.api.forum.ForumManager.CLIENT_ID;
import static org.briarproject.briar.api.forum.ForumManager.MAJOR_VERSION; import static org.briarproject.briar.api.forum.ForumManager.MAJOR_VERSION;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_DESCRIPTOR;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -88,10 +85,9 @@ class ForumProtocolEngineImpl extends ProtocolEngineImpl<Forum> {
@Override @Override
protected void addShareable(Transaction txn, MessageId inviteId) protected void addShareable(Transaction txn, MessageId inviteId)
throws DbException, FormatException { throws DbException, FormatException {
BdfDictionary meta = InviteMessage<Forum> invite =
clientHelper.getMessageMetadataAsDictionary(txn, inviteId); messageParser.getInviteMessage(txn, inviteId);
BdfList descriptor = meta.getList(MSG_KEY_DESCRIPTOR); forumManager.addForum(txn, invite.getShareable());
forumManager.addForum(txn, messageParser.createShareable(descriptor));
} }
} }

View File

@@ -12,10 +12,6 @@ import javax.annotation.Nullable;
@NotNullByDefault @NotNullByDefault
interface MessageEncoder { interface MessageEncoder {
BdfDictionary encodeInviteMetadata(GroupId shareableId, BdfList descriptor,
long timestamp, boolean local, boolean read, boolean visible,
boolean available, boolean accepted);
BdfDictionary encodeMetadata(MessageType type, GroupId shareableId, BdfDictionary encodeMetadata(MessageType type, GroupId shareableId,
long timestamp, boolean local, boolean read, boolean visible, long timestamp, boolean local, boolean read, boolean visible,
boolean available, boolean accepted); boolean available, boolean accepted);

View File

@@ -20,7 +20,6 @@ import static org.briarproject.briar.sharing.MessageType.DECLINE;
import static org.briarproject.briar.sharing.MessageType.INVITE; import static org.briarproject.briar.sharing.MessageType.INVITE;
import static org.briarproject.briar.sharing.MessageType.LEAVE; import static org.briarproject.briar.sharing.MessageType.LEAVE;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_AVAILABLE_TO_ANSWER; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_AVAILABLE_TO_ANSWER;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_DESCRIPTOR;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_INVITATION_ACCEPTED; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_INVITATION_ACCEPTED;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_LOCAL; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_MESSAGE_TYPE; import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_MESSAGE_TYPE;
@@ -43,23 +42,6 @@ class MessageEncoderImpl implements MessageEncoder {
this.messageFactory = messageFactory; this.messageFactory = messageFactory;
} }
@Override
public BdfDictionary encodeInviteMetadata(GroupId shareableId,
BdfList descriptor, long timestamp, boolean local, boolean read,
boolean visible, boolean available, boolean accepted) {
BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_MESSAGE_TYPE, INVITE.getValue());
meta.put(MSG_KEY_SHAREABLE_ID, shareableId);
meta.put(MSG_KEY_DESCRIPTOR, descriptor);
meta.put(MSG_KEY_TIMESTAMP, timestamp);
meta.put(MSG_KEY_LOCAL, local);
meta.put(MSG_KEY_READ, read);
meta.put(MSG_KEY_VISIBLE_IN_UI, visible);
meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, available);
meta.put(MSG_KEY_INVITATION_ACCEPTED, accepted);
return meta;
}
@Override @Override
public BdfDictionary encodeMetadata(MessageType type, public BdfDictionary encodeMetadata(MessageType type,
GroupId shareableId, long timestamp, boolean local, boolean read, GroupId shareableId, long timestamp, boolean local, boolean read,

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