mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Compare commits
2 Commits
block-inpu
...
test-immed
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33bfef12e4 | ||
|
|
92163d6a99 |
@@ -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
|
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom test --continue
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
# these file change every time but should not be cached
|
# these file change every time but should not be cached
|
||||||
|
|||||||
@@ -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,6 +51,7 @@ 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
|
||||||
@@ -160,7 +161,11 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
void tryToClose(@Nullable BluetoothServerSocket ss) {
|
void tryToClose(@Nullable BluetoothServerSocket ss) {
|
||||||
IoUtils.tryToClose(ss, LOG, WARNING);
|
try {
|
||||||
|
if (ss != null) ss.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -190,7 +195,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
|||||||
s.connect();
|
s.connect();
|
||||||
return wrapSocket(s);
|
return wrapSocket(s);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
IoUtils.tryToClose(s, LOG, WARNING);
|
tryToClose(s);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,6 +268,14 @@ 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
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
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;
|
||||||
@@ -27,7 +26,7 @@ import static android.provider.Settings.Secure.ANDROID_ID;
|
|||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
|
||||||
|
|
||||||
private static final int SEED_LENGTH = 32;
|
private static final int SEED_LENGTH = 32;
|
||||||
|
|
||||||
@@ -38,7 +37,6 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
|||||||
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);
|
||||||
@@ -51,14 +49,12 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
|||||||
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) appContext.getApplicationContext()
|
WifiManager wm =
|
||||||
.getSystemService(WIFI_SERVICE);
|
(WifiManager) appContext.getSystemService(WIFI_SERVICE);
|
||||||
if (wm != null) {
|
List<WifiConfiguration> configs = wm.getConfiguredNetworks();
|
||||||
List<WifiConfiguration> configs = wm.getConfiguredNetworks();
|
if (configs != null) {
|
||||||
if (configs != null) {
|
for (WifiConfiguration config : configs)
|
||||||
for (WifiConfiguration config : configs)
|
parcel.writeParcelable(config, 0);
|
||||||
parcel.writeParcelable(config, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||||
if (bt != null) {
|
if (bt != null) {
|
||||||
@@ -81,13 +77,13 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
|||||||
|
|
||||||
// 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 UnixSecureRandomSpi().engineGenerateSeed(
|
byte[] seed = new LinuxSecureRandomSpi().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, (Object) seed);
|
.invoke(null, 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")
|
||||||
|
|||||||
@@ -208,24 +208,6 @@ public interface DatabaseComponent {
|
|||||||
Collection<Message> generateRequestedBatch(Transaction txn, ContactId c,
|
Collection<Message> generateRequestedBatch(Transaction txn, ContactId c,
|
||||||
int maxLength, int maxLatency) throws DbException;
|
int maxLength, int maxLatency) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of blocks in the given message.
|
|
||||||
* <p>
|
|
||||||
* Read-only.
|
|
||||||
*/
|
|
||||||
int getBlockCount(Transaction txn, MessageId m) throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the given block of the given message.
|
|
||||||
* <p>
|
|
||||||
* Read-only.
|
|
||||||
*
|
|
||||||
* @throws NoSuchBlockException if 'blockNumber' is greater than or equal
|
|
||||||
* to the number of blocks in the message
|
|
||||||
*/
|
|
||||||
byte[] getBlock(Transaction txn, MessageId m, int blockNumber)
|
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the contact with the given ID.
|
* Returns the contact with the given ID.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.db;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown when a database operation is attempted for a block that is not in
|
|
||||||
* the database. This exception may occur due to concurrent updates and does
|
|
||||||
* not indicate a database error.
|
|
||||||
*/
|
|
||||||
public class NoSuchBlockException extends DbException {
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.db.NoSuchBlockException;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
|
||||||
|
|
||||||
public interface BlockSource {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of blocks in the given message.
|
|
||||||
*/
|
|
||||||
int getBlockCount(MessageId m) throws DbException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the given block of the given message.
|
|
||||||
*
|
|
||||||
* @throws NoSuchBlockException if 'blockNumber' is greater than or equal
|
|
||||||
* to the number of blocks in the message
|
|
||||||
*/
|
|
||||||
byte[] getBlock(MessageId m, int blockNumber) throws DbException;
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package org.briarproject.bramble.api.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
public interface MessageInputStreamFactory {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an {@link InputStream} for reading the given message from the
|
|
||||||
* database. This method returns immediately. If the message is not in the
|
|
||||||
* database or cannot be read, reading from the stream will throw an
|
|
||||||
* {@link IOException};
|
|
||||||
*/
|
|
||||||
InputStream getMessageInputStream(MessageId m);
|
|
||||||
}
|
|
||||||
@@ -35,9 +35,4 @@ 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
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,15 +8,12 @@ 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 {
|
||||||
@@ -57,35 +54,16 @@ public class IoUtils {
|
|||||||
out.flush();
|
out.flush();
|
||||||
out.close();
|
out.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
tryToClose(in, LOG, WARNING);
|
tryToClose(in);
|
||||||
tryToClose(out, LOG, WARNING);
|
tryToClose(out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void tryToClose(@Nullable Closeable c, Logger logger,
|
private static void tryToClose(@Nullable Closeable c) {
|
||||||
Level level) {
|
|
||||||
try {
|
try {
|
||||||
if (c != null) c.close();
|
if (c != null) c.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logException(logger, level, e);
|
// We did our best
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ 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() {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import org.briarproject.bramble.db.DatabaseExecutorModule;
|
|||||||
import org.briarproject.bramble.db.DatabaseModule;
|
import org.briarproject.bramble.db.DatabaseModule;
|
||||||
import org.briarproject.bramble.event.EventModule;
|
import org.briarproject.bramble.event.EventModule;
|
||||||
import org.briarproject.bramble.identity.IdentityModule;
|
import org.briarproject.bramble.identity.IdentityModule;
|
||||||
import org.briarproject.bramble.io.IoModule;
|
|
||||||
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
import org.briarproject.bramble.keyagreement.KeyAgreementModule;
|
||||||
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
import org.briarproject.bramble.lifecycle.LifecycleModule;
|
||||||
import org.briarproject.bramble.plugin.PluginModule;
|
import org.briarproject.bramble.plugin.PluginModule;
|
||||||
@@ -37,7 +36,6 @@ import dagger.Module;
|
|||||||
DatabaseExecutorModule.class,
|
DatabaseExecutorModule.class,
|
||||||
EventModule.class,
|
EventModule.class,
|
||||||
IdentityModule.class,
|
IdentityModule.class,
|
||||||
IoModule.class,
|
|
||||||
KeyAgreementModule.class,
|
KeyAgreementModule.class,
|
||||||
LifecycleModule.class,
|
LifecycleModule.class,
|
||||||
PluginModule.class,
|
PluginModule.class,
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
package org.briarproject.bramble.db;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.io.BlockSource;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
class BlockSourceImpl implements BlockSource {
|
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
BlockSourceImpl(DatabaseComponent db) {
|
|
||||||
this.db = db;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBlockCount(MessageId m) throws DbException {
|
|
||||||
return db.transactionWithResult(true, txn -> db.getBlockCount(txn, m));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getBlock(MessageId m, int blockNumber) throws DbException {
|
|
||||||
return db.transactionWithResult(true, txn ->
|
|
||||||
db.getBlock(txn, m, blockNumber));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,6 @@ import org.briarproject.bramble.api.db.DbException;
|
|||||||
import org.briarproject.bramble.api.db.DbRunnable;
|
import org.briarproject.bramble.api.db.DbRunnable;
|
||||||
import org.briarproject.bramble.api.db.Metadata;
|
import org.briarproject.bramble.api.db.Metadata;
|
||||||
import org.briarproject.bramble.api.db.MigrationListener;
|
import org.briarproject.bramble.api.db.MigrationListener;
|
||||||
import org.briarproject.bramble.api.db.NoSuchBlockException;
|
|
||||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||||
import org.briarproject.bramble.api.db.NoSuchGroupException;
|
import org.briarproject.bramble.api.db.NoSuchGroupException;
|
||||||
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
|
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
|
||||||
@@ -421,26 +420,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBlockCount(Transaction transaction, MessageId m)
|
|
||||||
throws DbException {
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsMessage(txn, m))
|
|
||||||
throw new NoSuchMessageException();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getBlock(Transaction transaction, MessageId m,
|
|
||||||
int blockNumber) throws DbException {
|
|
||||||
T txn = unbox(transaction);
|
|
||||||
if (!db.containsMessage(txn, m))
|
|
||||||
throw new NoSuchMessageException();
|
|
||||||
if (blockNumber != 0)
|
|
||||||
throw new NoSuchBlockException();
|
|
||||||
return db.getMessage(txn, m).getBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Contact getContact(Transaction transaction, ContactId c)
|
public Contact getContact(Transaction transaction, ContactId c)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.db;
|
|||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.io.BlockSource;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
|
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
|
||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
@@ -32,9 +31,4 @@ public class DatabaseModule {
|
|||||||
return new DatabaseComponentImpl<>(db, Connection.class, eventBus,
|
return new DatabaseComponentImpl<>(db, Connection.class, eventBus,
|
||||||
shutdown);
|
shutdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
BlockSource provideBlockSource(BlockSourceImpl blockSource) {
|
|
||||||
return blockSource;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,23 +15,16 @@ 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";
|
||||||
@@ -128,8 +121,8 @@ class H2Database extends JdbcDatabase {
|
|||||||
s.close();
|
s.close();
|
||||||
c.close();
|
c.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s, LOG, WARNING);
|
tryToClose(s);
|
||||||
tryToClose(c, LOG, WARNING);
|
tryToClose(c);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,24 +14,16 @@ 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";
|
||||||
@@ -80,8 +72,8 @@ class HyperSqlDatabase extends JdbcDatabase {
|
|||||||
s.close();
|
s.close();
|
||||||
c.close();
|
c.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s, LOG, WARNING);
|
tryToClose(s);
|
||||||
tryToClose(c, LOG, WARNING);
|
tryToClose(c);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,8 +122,8 @@ class HyperSqlDatabase extends JdbcDatabase {
|
|||||||
s.close();
|
s.close();
|
||||||
c.close();
|
c.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(s, LOG, WARNING);
|
tryToClose(s);
|
||||||
tryToClose(c, LOG, WARNING);
|
tryToClose(c);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ 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;
|
||||||
@@ -459,6 +458,30 @@ 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 {
|
||||||
@@ -479,7 +502,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, LOG, WARNING);
|
tryToClose(s);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -496,7 +519,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, LOG, WARNING);
|
tryToClose(s);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -543,7 +566,11 @@ 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);
|
||||||
tryToClose(txn, LOG, WARNING);
|
try {
|
||||||
|
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 {
|
||||||
@@ -632,8 +659,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return c;
|
return c;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -654,7 +681,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -677,7 +704,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -708,8 +735,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -734,7 +761,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -783,7 +810,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -813,8 +840,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, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -846,7 +873,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -888,8 +915,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, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -908,7 +935,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -988,8 +1015,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return keySetId;
|
return keySetId;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1012,8 +1039,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return found;
|
return found;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1034,8 +1061,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return found;
|
return found;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1056,8 +1083,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return found;
|
return found;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1078,8 +1105,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return found;
|
return found;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1100,8 +1127,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return found;
|
return found;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1122,8 +1149,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return found;
|
return found;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1147,8 +1174,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return found;
|
return found;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1171,8 +1198,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return count;
|
return count;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1196,7 +1223,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1213,7 +1240,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1246,8 +1273,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, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1283,8 +1310,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return contacts;
|
return contacts;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1306,8 +1333,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1344,8 +1371,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return contacts;
|
return contacts;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1368,8 +1395,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, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1396,8 +1423,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return groups;
|
return groups;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1422,8 +1449,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return v;
|
return v;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1446,8 +1473,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return visible;
|
return visible;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1478,8 +1505,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return localAuthor;
|
return localAuthor;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1510,8 +1537,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return authors;
|
return authors;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1539,8 +1566,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, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1563,8 +1590,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1601,8 +1628,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, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1634,8 +1661,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return all;
|
return all;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1669,8 +1696,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return metadata;
|
return metadata;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1693,8 +1720,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return metadata;
|
return metadata;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1719,8 +1746,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return metadata;
|
return metadata;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1749,8 +1776,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return statuses;
|
return statuses;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1780,8 +1807,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return status;
|
return status;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1810,8 +1837,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return dependencies;
|
return dependencies;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1841,8 +1868,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return dependents;
|
return dependents;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1864,8 +1891,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return state;
|
return state;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1889,8 +1916,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1923,8 +1950,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1948,8 +1975,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1987,8 +2014,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2021,8 +2048,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2049,8 +2076,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2079,8 +2106,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return nextSendTime;
|
return nextSendTime;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2118,8 +2145,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return ids;
|
return ids;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2141,8 +2168,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return s;
|
return s;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2207,8 +2234,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
ps.close();
|
ps.close();
|
||||||
return keys;
|
return keys;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(rs, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2227,7 +2254,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2254,7 +2281,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2281,7 +2308,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
ps.close();
|
ps.close();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2311,7 +2338,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2356,8 +2383,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, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2423,7 +2450,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
return added;
|
return added;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2470,7 +2497,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2489,7 +2516,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2508,7 +2535,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2527,7 +2554,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2544,7 +2571,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2560,7 +2587,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2588,7 +2615,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2605,7 +2632,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2621,7 +2648,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2640,7 +2667,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2665,7 +2692,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2682,7 +2709,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2703,7 +2730,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2722,7 +2749,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2740,7 +2767,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2758,7 +2785,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2777,7 +2804,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2807,7 +2834,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2833,7 +2860,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2886,7 +2913,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2909,7 +2936,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2928,7 +2955,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2964,8 +2991,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, LOG, WARNING);
|
tryToClose(rs);
|
||||||
tryToClose(ps, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3032,7 +3059,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, LOG, WARNING);
|
tryToClose(ps);
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,8 +7,10 @@ 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.db.JdbcUtils.tryToClose;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
class Migration38_39 implements Migration<Connection> {
|
class Migration38_39 implements Migration<Connection> {
|
||||||
|
|
||||||
@@ -38,8 +40,16 @@ 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, LOG, WARNING);
|
tryToClose(s);
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ 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.db.JdbcUtils.tryToClose;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
class Migration39_40 implements Migration<Connection> {
|
class Migration39_40 implements Migration<Connection> {
|
||||||
|
|
||||||
@@ -37,8 +39,16 @@ 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, LOG, WARNING);
|
tryToClose(s);
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ 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.db.JdbcUtils.tryToClose;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
class Migration40_41 implements Migration<Connection> {
|
class Migration40_41 implements Migration<Connection> {
|
||||||
|
|
||||||
@@ -39,8 +41,16 @@ 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, LOG, WARNING);
|
tryToClose(s);
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,155 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InterruptedIOException;
|
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.GuardedBy;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
import static java.lang.System.arraycopy;
|
|
||||||
import static java.lang.Thread.currentThread;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An {@link InputStream} that asynchronously fetches blocks of data on demand.
|
|
||||||
*/
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
abstract class BlockInputStream extends InputStream {
|
|
||||||
|
|
||||||
private final int minBufferBytes;
|
|
||||||
private final BlockingQueue<Buffer> queue = new ArrayBlockingQueue<>(1);
|
|
||||||
private final Object lock = new Object();
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
@Nullable
|
|
||||||
private Buffer buffer = null;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private int offset = 0;
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private boolean fetchingBlock = false;
|
|
||||||
|
|
||||||
abstract void fetchBlockAsync(int blockNumber);
|
|
||||||
|
|
||||||
BlockInputStream(int minBufferBytes) {
|
|
||||||
this.minBufferBytes = minBufferBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read() throws IOException {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (!prepareRead()) return -1;
|
|
||||||
if (buffer == null) throw new AssertionError();
|
|
||||||
return buffer.data[offset++] & 0xFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read(byte[] b) throws IOException {
|
|
||||||
return read(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read(byte[] b, int off, int len) throws IOException {
|
|
||||||
if (off < 0 || len < 0 || off + len > b.length)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
synchronized (lock) {
|
|
||||||
if (!prepareRead()) return -1;
|
|
||||||
if (buffer == null) throw new AssertionError();
|
|
||||||
len = Math.min(len, buffer.length - offset);
|
|
||||||
if (len < 0) throw new AssertionError();
|
|
||||||
arraycopy(buffer.data, offset, b, off, len);
|
|
||||||
offset += len;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean prepareRead() throws IOException {
|
|
||||||
throwExceptionIfNecessary();
|
|
||||||
if (isEndOfStream()) return false;
|
|
||||||
if (shouldFetchBlock()) fetchBlockAsync();
|
|
||||||
waitForBlock();
|
|
||||||
if (buffer == null) throw new AssertionError();
|
|
||||||
return offset < buffer.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private void throwExceptionIfNecessary() throws IOException {
|
|
||||||
if (buffer != null && buffer.exception != null)
|
|
||||||
throw new IOException(buffer.exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private boolean isEndOfStream() {
|
|
||||||
return buffer != null && offset == buffer.length && !fetchingBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private boolean shouldFetchBlock() {
|
|
||||||
if (fetchingBlock) return false;
|
|
||||||
if (buffer == null) return true;
|
|
||||||
if (buffer.length == 0) return false;
|
|
||||||
return buffer.length - offset < minBufferBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private void fetchBlockAsync() {
|
|
||||||
if (buffer == null) fetchBlockAsync(0);
|
|
||||||
else fetchBlockAsync(buffer.blockNumber + 1);
|
|
||||||
fetchingBlock = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GuardedBy("lock")
|
|
||||||
private void waitForBlock() throws IOException {
|
|
||||||
if (buffer != null && offset < buffer.length) return;
|
|
||||||
try {
|
|
||||||
buffer = queue.take();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
currentThread().interrupt();
|
|
||||||
throw new InterruptedIOException();
|
|
||||||
}
|
|
||||||
fetchingBlock = false;
|
|
||||||
offset = 0;
|
|
||||||
throwExceptionIfNecessary();
|
|
||||||
}
|
|
||||||
|
|
||||||
void fetchSucceeded(int blockNumber, byte[] data, int length) {
|
|
||||||
queue.add(new Buffer(blockNumber, data, length));
|
|
||||||
}
|
|
||||||
|
|
||||||
void fetchFailed(int blockNumber, Exception exception) {
|
|
||||||
queue.add(new Buffer(blockNumber, exception));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Buffer {
|
|
||||||
|
|
||||||
private final int blockNumber;
|
|
||||||
private final byte[] data;
|
|
||||||
private final int length;
|
|
||||||
@Nullable
|
|
||||||
private final Exception exception;
|
|
||||||
|
|
||||||
private Buffer(int blockNumber, byte[] data, int length) {
|
|
||||||
if (length < 0 || length > data.length)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
this.blockNumber = blockNumber;
|
|
||||||
this.data = data;
|
|
||||||
this.length = length;
|
|
||||||
exception = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Buffer(int blockNumber, Exception exception) {
|
|
||||||
this.blockNumber = blockNumber;
|
|
||||||
this.exception = exception;
|
|
||||||
data = new byte[0];
|
|
||||||
length = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.io.BlockSource;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link BlockInputStream} that fetches data from a {@link BlockSource}.
|
|
||||||
*/
|
|
||||||
@ThreadSafe
|
|
||||||
@NotNullByDefault
|
|
||||||
class BlockSourceInputStream extends BlockInputStream {
|
|
||||||
|
|
||||||
private final Executor executor;
|
|
||||||
private final BlockSource blockSource;
|
|
||||||
private final MessageId messageId;
|
|
||||||
|
|
||||||
private volatile int blockCount = -1;
|
|
||||||
|
|
||||||
BlockSourceInputStream(int minBufferBytes, Executor executor,
|
|
||||||
BlockSource blockSource, MessageId messageId) {
|
|
||||||
super(minBufferBytes);
|
|
||||||
this.executor = executor;
|
|
||||||
this.blockSource = blockSource;
|
|
||||||
this.messageId = messageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void fetchBlockAsync(int blockNumber) {
|
|
||||||
executor.execute(() -> {
|
|
||||||
try {
|
|
||||||
if (blockCount == -1) {
|
|
||||||
blockCount = blockSource.getBlockCount(messageId);
|
|
||||||
}
|
|
||||||
if (blockNumber > blockCount) {
|
|
||||||
fetchFailed(blockNumber, new IllegalArgumentException());
|
|
||||||
} else if (blockNumber == blockCount) {
|
|
||||||
fetchSucceeded(blockNumber, new byte[0], 0); // EOF
|
|
||||||
} else {
|
|
||||||
byte[] block = blockSource.getBlock(messageId, blockNumber);
|
|
||||||
fetchSucceeded(blockNumber, block, block.length);
|
|
||||||
}
|
|
||||||
} catch (DbException e) {
|
|
||||||
fetchFailed(blockNumber, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.io.MessageInputStreamFactory;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class IoModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
MessageInputStreamFactory provideMessageInputStreamFactory(
|
|
||||||
MessageInputStreamFactoryImpl messageInputStreamFactory) {
|
|
||||||
return messageInputStreamFactory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
|
||||||
import org.briarproject.bramble.api.io.BlockSource;
|
|
||||||
import org.briarproject.bramble.api.io.MessageInputStreamFactory;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_BLOCK_LENGTH;
|
|
||||||
|
|
||||||
class MessageInputStreamFactoryImpl implements MessageInputStreamFactory {
|
|
||||||
|
|
||||||
private final Executor dbExecutor;
|
|
||||||
private final BlockSource blockSource;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
MessageInputStreamFactoryImpl(@DatabaseExecutor Executor dbExecutor,
|
|
||||||
BlockSource blockSource) {
|
|
||||||
this.dbExecutor = dbExecutor;
|
|
||||||
this.blockSource = blockSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InputStream getMessageInputStream(MessageId m) {
|
|
||||||
return new BlockSourceInputStream(MAX_BLOCK_LENGTH, dbExecutor,
|
|
||||||
blockSource, m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,11 +4,12 @@ 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.IoUtils.tryToClose;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class FileTransportReader implements TransportConnectionReader {
|
class FileTransportReader implements TransportConnectionReader {
|
||||||
@@ -33,7 +34,11 @@ class FileTransportReader implements TransportConnectionReader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose(boolean exception, boolean recognised) {
|
public void dispose(boolean exception, boolean recognised) {
|
||||||
tryToClose(in, LOG, WARNING);
|
try {
|
||||||
|
in.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
plugin.readerFinished(file, exception, recognised);
|
plugin.readerFinished(file, exception, recognised);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ 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.IoUtils.tryToClose;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class FileTransportWriter implements TransportConnectionWriter {
|
class FileTransportWriter implements TransportConnectionWriter {
|
||||||
@@ -43,7 +44,11 @@ class FileTransportWriter implements TransportConnectionWriter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose(boolean exception) {
|
public void dispose(boolean exception) {
|
||||||
tryToClose(out, LOG, WARNING);
|
try {
|
||||||
|
out.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
plugin.writerFinished(file, exception);
|
plugin.writerFinished(file, exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ 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;
|
||||||
@@ -36,6 +35,7 @@ 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,7 +293,11 @@ class LanTcpPlugin extends TcpPlugin {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
IoUtils.tryToClose(ss, LOG, WARNING);
|
try {
|
||||||
|
ss.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ 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;
|
||||||
@@ -154,8 +153,13 @@ abstract class TcpPlugin implements DuplexPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void tryToClose(@Nullable ServerSocket ss) {
|
protected void tryToClose(@Nullable ServerSocket ss) {
|
||||||
IoUtils.tryToClose(ss, LOG, WARNING);
|
try {
|
||||||
callback.transportDisabled();
|
if (ss != null) ss.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
} finally {
|
||||||
|
callback.transportDisabled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String getIpPortString(InetSocketAddress a) {
|
String getIpPortString(InetSocketAddress a) {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ 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;
|
||||||
@@ -302,8 +303,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) {
|
||||||
IoUtils.tryToClose(in, LOG, WARNING);
|
tryToClose(in);
|
||||||
IoUtils.tryToClose(out, LOG, WARNING);
|
tryToClose(out);
|
||||||
throw new PluginException(e);
|
throw new PluginException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,6 +341,22 @@ 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();
|
||||||
@@ -361,7 +378,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
return b;
|
return b;
|
||||||
} finally {
|
} finally {
|
||||||
IoUtils.tryToClose(in, LOG, WARNING);
|
tryToClose(in);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,8 +418,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void tryToClose(@Nullable ServerSocket ss) {
|
private void tryToClose(@Nullable ServerSocket ss) {
|
||||||
IoUtils.tryToClose(ss, LOG, WARNING);
|
try {
|
||||||
callback.transportDisabled();
|
if (ss != null) ss.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
} finally {
|
||||||
|
callback.transportDisabled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void publishHiddenService(String port) {
|
private void publishHiddenService(String port) {
|
||||||
@@ -571,7 +593,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());
|
||||||
}
|
}
|
||||||
IoUtils.tryToClose(s, LOG, WARNING);
|
tryToClose(s);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ 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;
|
||||||
@@ -25,12 +26,13 @@ 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.IoUtils.tryToClose;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -64,7 +66,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, LOG, WARNING);
|
tryToClose(s);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,7 +88,8 @@ class DevReporterImpl implements DevReporter, EventListener {
|
|||||||
writer.append(armoured);
|
writer.append(armoured);
|
||||||
writer.flush();
|
writer.flush();
|
||||||
} finally {
|
} finally {
|
||||||
tryToClose(writer, LOG, WARNING);
|
if (writer != null)
|
||||||
|
writer.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,11 +121,27 @@ 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, LOG, WARNING);
|
tryToClose(out);
|
||||||
tryToClose(in, LOG, WARNING);
|
tryToClose(in);
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,33 +13,32 @@ 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 UnixSecureRandomProvider extends AbstractSecureRandomProvider {
|
class LinuxSecureRandomProvider extends AbstractSecureRandomProvider {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(UnixSecureRandomProvider.class.getName());
|
Logger.getLogger(LinuxSecureRandomProvider.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;
|
||||||
|
|
||||||
UnixSecureRandomProvider() {
|
LinuxSecureRandomProvider() {
|
||||||
this(RANDOM_DEVICE);
|
this(RANDOM_DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
UnixSecureRandomProvider(File outputDevice) {
|
LinuxSecureRandomProvider(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 UnixProvider();
|
return new LinuxProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void writeSeed() {
|
protected void writeSeed() {
|
||||||
@@ -56,15 +55,15 @@ class UnixSecureRandomProvider 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 UnixProvider extends Provider {
|
private static class LinuxProvider extends Provider {
|
||||||
|
|
||||||
private UnixProvider() {
|
private LinuxProvider() {
|
||||||
super("UnixPRNG", 1.0, "A Unix-specific PRNG using /dev/urandom");
|
super("LinuxPRNG", 1.1, "A Linux-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", UnixSecureRandomSpi.class.getName());
|
put("SecureRandom.SHA1PRNG", LinuxSecureRandomSpi.class.getName());
|
||||||
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
|
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,24 +10,22 @@ 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 UnixSecureRandomSpi extends SecureRandomSpi {
|
public class LinuxSecureRandomSpi extends SecureRandomSpi {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(UnixSecureRandomSpi.class.getName());
|
Logger.getLogger(LinuxSecureRandomSpi.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;
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
public LinuxSecureRandomSpi() {
|
||||||
public UnixSecureRandomSpi() {
|
|
||||||
this(RANDOM_DEVICE, RANDOM_DEVICE);
|
this(RANDOM_DEVICE, RANDOM_DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
UnixSecureRandomSpi(File inputDevice, File outputDevice) {
|
LinuxSecureRandomSpi(File inputDevice, File outputDevice) {
|
||||||
this.inputDevice = inputDevice;
|
this.inputDevice = inputDevice;
|
||||||
this.outputDevice = outputDevice;
|
this.outputDevice = outputDevice;
|
||||||
}
|
}
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.io.BlockSource;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
|
||||||
import org.jmock.Expectations;
|
|
||||||
import org.jmock.lib.concurrent.Synchroniser;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import static java.util.concurrent.Executors.newSingleThreadExecutor;
|
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_BLOCK_LENGTH;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNotEquals;
|
|
||||||
import static org.spongycastle.util.Arrays.copyOfRange;
|
|
||||||
|
|
||||||
public class BlockSourceInputStreamTest extends BrambleMockTestCase {
|
|
||||||
|
|
||||||
private static final int MAX_DATA_BYTES = 1_000_000;
|
|
||||||
private static final int READ_BUFFER_BYTES = 4 * 1024;
|
|
||||||
|
|
||||||
private final BlockSource blockSource;
|
|
||||||
|
|
||||||
private final Random random = new Random();
|
|
||||||
private final Executor executor = newSingleThreadExecutor();
|
|
||||||
private final MessageId messageId = new MessageId(getRandomId());
|
|
||||||
|
|
||||||
public BlockSourceInputStreamTest() {
|
|
||||||
context.setThreadingPolicy(new Synchroniser());
|
|
||||||
blockSource = context.mock(BlockSource.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadSingleBytes() throws IOException {
|
|
||||||
byte[] data = createRandomData();
|
|
||||||
BlockSource source = new ByteArrayBlockSource(data, MAX_BLOCK_LENGTH);
|
|
||||||
InputStream in = new BlockSourceInputStream(MAX_BLOCK_LENGTH, executor,
|
|
||||||
source, messageId);
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
//noinspection ForLoopReplaceableByForEach
|
|
||||||
for (int i = 0; i < data.length; i++) {
|
|
||||||
int read = in.read();
|
|
||||||
assertNotEquals(-1, read);
|
|
||||||
out.write(read);
|
|
||||||
}
|
|
||||||
assertEquals(-1, in.read());
|
|
||||||
in.close();
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
assertArrayEquals(data, out.toByteArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadByteArrays() throws IOException {
|
|
||||||
byte[] data = createRandomData();
|
|
||||||
BlockSource source = new ByteArrayBlockSource(data, MAX_BLOCK_LENGTH);
|
|
||||||
InputStream in = new BlockSourceInputStream(MAX_BLOCK_LENGTH, executor,
|
|
||||||
source, messageId);
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
byte[] buf = new byte[READ_BUFFER_BYTES];
|
|
||||||
int dataOffset = 0;
|
|
||||||
while (dataOffset < data.length) {
|
|
||||||
int length = Math.min(random.nextInt(buf.length) + 1,
|
|
||||||
data.length - dataOffset);
|
|
||||||
int bufOffset = 0;
|
|
||||||
if (length < buf.length)
|
|
||||||
bufOffset = random.nextInt(buf.length - length);
|
|
||||||
int read = in.read(buf, bufOffset, length);
|
|
||||||
assertNotEquals(-1, read);
|
|
||||||
out.write(buf, bufOffset, read);
|
|
||||||
dataOffset += read;
|
|
||||||
}
|
|
||||||
assertEquals(-1, in.read(buf, 0, 0));
|
|
||||||
in.close();
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
assertArrayEquals(data, out.toByteArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IOException.class)
|
|
||||||
public void testDbExceptionFromGetBlockCountIsRethrown() throws Exception {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(blockSource).getBlockCount(messageId);
|
|
||||||
will(throwException(new DbException()));
|
|
||||||
}});
|
|
||||||
|
|
||||||
InputStream in = new BlockSourceInputStream(MAX_BLOCK_LENGTH, executor,
|
|
||||||
blockSource, messageId);
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
in.read();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IOException.class)
|
|
||||||
public void testDbExceptionFromGetBlockIsRethrown() throws Exception {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(blockSource).getBlockCount(messageId);
|
|
||||||
will(returnValue(1));
|
|
||||||
oneOf(blockSource).getBlock(messageId, 0);
|
|
||||||
will(throwException(new DbException()));
|
|
||||||
}});
|
|
||||||
|
|
||||||
InputStream in = new BlockSourceInputStream(MAX_BLOCK_LENGTH, executor,
|
|
||||||
blockSource, messageId);
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
in.read();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadFullBlockAtEndOfMessage() throws Exception {
|
|
||||||
testReadBlockAtEndOfMessage(MAX_BLOCK_LENGTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadPartialBlockAtEndOfMessage() throws Exception {
|
|
||||||
testReadBlockAtEndOfMessage(MAX_BLOCK_LENGTH - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void testReadBlockAtEndOfMessage(int blockLength) throws Exception {
|
|
||||||
byte[] block = new byte[blockLength];
|
|
||||||
random.nextBytes(block);
|
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(blockSource).getBlockCount(messageId);
|
|
||||||
will(returnValue(1));
|
|
||||||
oneOf(blockSource).getBlock(messageId, 0);
|
|
||||||
will(returnValue(block));
|
|
||||||
}});
|
|
||||||
|
|
||||||
InputStream in = new BlockSourceInputStream(MAX_BLOCK_LENGTH, executor,
|
|
||||||
blockSource, messageId);
|
|
||||||
byte[] buf = new byte[MAX_BLOCK_LENGTH * 2];
|
|
||||||
assertEquals(block.length, in.read(buf, 0, buf.length));
|
|
||||||
assertArrayEquals(block, copyOfRange(buf, 0, block.length));
|
|
||||||
assertEquals(-1, in.read(buf, 0, buf.length));
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] createRandomData() {
|
|
||||||
int length = random.nextInt(MAX_DATA_BYTES) + 1;
|
|
||||||
byte[] data = new byte[length];
|
|
||||||
random.nextBytes(data);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package org.briarproject.bramble.io;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.io.BlockSource;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
|
||||||
|
|
||||||
import static java.lang.System.arraycopy;
|
|
||||||
|
|
||||||
class ByteArrayBlockSource implements BlockSource {
|
|
||||||
|
|
||||||
private final byte[] data;
|
|
||||||
private final int blockBytes;
|
|
||||||
|
|
||||||
ByteArrayBlockSource(byte[] data, int blockBytes) {
|
|
||||||
this.data = data;
|
|
||||||
this.blockBytes = blockBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBlockCount(MessageId m) {
|
|
||||||
return (data.length + blockBytes - 1) / blockBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getBlock(MessageId m, int blockNumber) {
|
|
||||||
int offset = blockNumber * blockBytes;
|
|
||||||
if (offset >= data.length) throw new IllegalArgumentException();
|
|
||||||
int length = Math.min(blockBytes, data.length - offset);
|
|
||||||
byte[] block = new byte[length];
|
|
||||||
arraycopy(data, offset, block, 0, length);
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
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;
|
||||||
@@ -8,50 +10,48 @@ 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 UnixSecureRandomProviderTest extends BrambleTestCase {
|
public class LinuxSecureRandomProviderTest extends BrambleTestCase {
|
||||||
|
|
||||||
private final File testDir = getTestDirectory();
|
private final File testDir = TestUtils.getTestDirectory();
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
assumeTrue(isLinux() || isMac());
|
testDir.mkdirs();
|
||||||
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");
|
||||||
if (urandom.exists()) assertTrue(urandom.delete());
|
urandom.delete();
|
||||||
assertTrue(urandom.createNewFile());
|
assertTrue(urandom.createNewFile());
|
||||||
assertEquals(0, urandom.length());
|
assertEquals(0, urandom.length());
|
||||||
UnixSecureRandomProvider p = new UnixSecureRandomProvider(urandom);
|
LinuxSecureRandomProvider p = new LinuxSecureRandomProvider(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("UnixPRNG", provider.getName());
|
assertEquals("LinuxPRNG", 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("UnixPRNG", provider.getName());
|
assertEquals("LinuxPRNG", provider.getName());
|
||||||
assertEquals(length, urandom.length());
|
assertEquals(length, urandom.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
deleteTestDirectory(testDir);
|
TestUtils.deleteTestDirectory(testDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ 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;
|
||||||
@@ -14,33 +15,31 @@ 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 UnixSecureRandomSpiTest extends BrambleTestCase {
|
public class LinuxSecureRandomSpiTest 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 = getTestDirectory();
|
private final File testDir = TestUtils.getTestDirectory();
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
assumeTrue(isLinux() || isMac());
|
testDir.mkdirs();
|
||||||
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<>();
|
||||||
UnixSecureRandomSpi engine = new UnixSecureRandomSpi();
|
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi();
|
||||||
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);
|
||||||
@@ -50,15 +49,19 @@ public class UnixSecureRandomSpiTest 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");
|
||||||
if (urandom.exists()) assertTrue(urandom.delete());
|
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
|
||||||
UnixSecureRandomSpi engine = new UnixSecureRandomSpi(RANDOM_DEVICE,
|
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(RANDOM_DEVICE,
|
||||||
urandom);
|
urandom);
|
||||||
engine.engineSetSeed(seed);
|
engine.engineSetSeed(seed);
|
||||||
assertEquals(SEED_BYTES, urandom.length());
|
assertEquals(SEED_BYTES, urandom.length());
|
||||||
@@ -71,11 +74,15 @@ public class UnixSecureRandomSpiTest 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");
|
||||||
if (urandom.exists()) assertTrue(urandom.delete());
|
urandom.delete();
|
||||||
FileOutputStream out = new FileOutputStream(urandom);
|
FileOutputStream out = new FileOutputStream(urandom);
|
||||||
out.write(entropy);
|
out.write(entropy);
|
||||||
out.flush();
|
out.flush();
|
||||||
@@ -83,7 +90,7 @@ public class UnixSecureRandomSpiTest 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
|
||||||
UnixSecureRandomSpi engine = new UnixSecureRandomSpi(urandom,
|
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(urandom,
|
||||||
RANDOM_DEVICE);
|
RANDOM_DEVICE);
|
||||||
byte[] b = new byte[SEED_BYTES];
|
byte[] b = new byte[SEED_BYTES];
|
||||||
engine.engineNextBytes(b);
|
engine.engineNextBytes(b);
|
||||||
@@ -92,11 +99,15 @@ public class UnixSecureRandomSpiTest 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");
|
||||||
if (urandom.exists()) assertTrue(urandom.delete());
|
urandom.delete();
|
||||||
FileOutputStream out = new FileOutputStream(urandom);
|
FileOutputStream out = new FileOutputStream(urandom);
|
||||||
out.write(entropy);
|
out.write(entropy);
|
||||||
out.flush();
|
out.flush();
|
||||||
@@ -104,7 +115,7 @@ public class UnixSecureRandomSpiTest 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
|
||||||
UnixSecureRandomSpi engine = new UnixSecureRandomSpi(urandom,
|
LinuxSecureRandomSpi engine = new LinuxSecureRandomSpi(urandom,
|
||||||
RANDOM_DEVICE);
|
RANDOM_DEVICE);
|
||||||
byte[] b = engine.engineGenerateSeed(SEED_BYTES);
|
byte[] b = engine.engineGenerateSeed(SEED_BYTES);
|
||||||
assertArrayEquals(entropy, b);
|
assertArrayEquals(entropy, b);
|
||||||
@@ -112,6 +123,6 @@ public class UnixSecureRandomSpiTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
deleteTestDirectory(testDir);
|
TestUtils.deleteTestDirectory(testDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,20 @@
|
|||||||
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 (isWindows()) return new WindowsShutdownManagerImpl();
|
if (OsUtils.isWindows()) return new WindowsShutdownManagerImpl();
|
||||||
else return new ShutdownManagerImpl();
|
else return new ShutdownManagerImpl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ 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;
|
||||||
@@ -28,7 +29,6 @@ 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 (isWindows()) {
|
if (OsUtils.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 = Native.loadLibrary("user32",
|
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) -> {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ 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;
|
||||||
@@ -18,9 +19,9 @@ import java.util.concurrent.Executor;
|
|||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
abstract class JavaTorPlugin extends TorPlugin {
|
class LinuxTorPlugin extends TorPlugin {
|
||||||
|
|
||||||
JavaTorPlugin(Executor ioExecutor, NetworkManager networkManager,
|
LinuxTorPlugin(Executor ioExecutor, NetworkManager networkManager,
|
||||||
LocationUtils locationUtils, SocketFactory torSocketFactory,
|
LocationUtils locationUtils, SocketFactory torSocketFactory,
|
||||||
Clock clock, ResourceProvider resourceProvider,
|
Clock clock, ResourceProvider resourceProvider,
|
||||||
CircumventionProvider circumventionProvider,
|
CircumventionProvider circumventionProvider,
|
||||||
@@ -33,6 +34,17 @@ abstract class JavaTorPlugin 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 =
|
||||||
@@ -46,4 +58,5 @@ abstract class JavaTorPlugin extends TorPlugin {
|
|||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -22,15 +22,14 @@ 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 UnixTorPluginFactory implements DuplexPluginFactory {
|
public class LinuxTorPluginFactory implements DuplexPluginFactory {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(UnixTorPluginFactory.class.getName());
|
Logger.getLogger(LinuxTorPluginFactory.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
|
||||||
@@ -50,7 +49,7 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
|
|||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
private final File torDirectory;
|
private final File torDirectory;
|
||||||
|
|
||||||
public UnixTorPluginFactory(Executor ioExecutor,
|
public LinuxTorPluginFactory(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,
|
||||||
@@ -96,7 +95,7 @@ public class UnixTorPluginFactory 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);
|
||||||
UnixTorPlugin plugin = new UnixTorPlugin(ioExecutor, networkManager,
|
LinuxTorPlugin plugin = new LinuxTorPlugin(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);
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +1,19 @@
|
|||||||
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() {
|
||||||
if (isLinux() || isMac())
|
return OsUtils.isLinux() ? new LinuxSecureRandomProvider() : null;
|
||||||
return new UnixSecureRandomProvider();
|
|
||||||
// TODO: Create a secure random provider for Windows
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 UnixTorPluginFactory factory;
|
private LinuxTorPluginFactory 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 UnixTorPluginFactory(ioExecutor, networkManager,
|
factory = new LinuxTorPluginFactory(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);
|
||||||
UnixTorPlugin plugin = (UnixTorPlugin) duplexPlugin;
|
LinuxTorPlugin plugin = (LinuxTorPlugin) duplexPlugin;
|
||||||
|
|
||||||
LOG.warning("Testing " + bridge);
|
LOG.warning("Testing " + bridge);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ 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"
|
||||||
|
|
||||||
@@ -118,15 +117,8 @@ 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'
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,3 @@
|
|||||||
|
|
||||||
# Emoji
|
# Emoji
|
||||||
-keep class com.vanniktech.emoji.**
|
-keep class com.vanniktech.emoji.**
|
||||||
|
|
||||||
# Glide
|
|
||||||
-dontwarn com.bumptech.glide.load.engine.cache.DiskLruCacheWrapper
|
|
||||||
|
|||||||
@@ -113,15 +113,6 @@
|
|||||||
/>
|
/>
|
||||||
</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"
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ 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;
|
||||||
@@ -171,8 +170,6 @@ 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,11 +15,10 @@ 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;
|
||||||
@@ -111,8 +110,6 @@ 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);
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ 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;
|
||||||
|
|
||||||
@@ -32,7 +33,6 @@ 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,28 +111,21 @@ public abstract class BriarActivity extends BaseActivity {
|
|||||||
lockManager.onActivityStop();
|
lockManager.onActivityStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void setSceneTransitionAnimation() {
|
||||||
* Sets the transition animations.
|
if (SDK_INT < 21) return;
|
||||||
* @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;
|
||||||
}
|
}
|
||||||
if (enterTransition != null) excludeSystemUi(enterTransition);
|
Transition slide = new Slide(Gravity.RIGHT);
|
||||||
if (exitTransition != null) excludeSystemUi(exitTransition);
|
slide.excludeTarget(android.R.id.statusBarBackground, true);
|
||||||
if (returnTransition != null) excludeSystemUi(returnTransition);
|
slide.excludeTarget(android.R.id.navigationBarBackground, true);
|
||||||
Window window = getWindow();
|
Window window = getWindow();
|
||||||
window.setEnterTransition(enterTransition);
|
window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
|
||||||
window.setExitTransition(exitTransition);
|
window.setExitTransition(slide);
|
||||||
window.setReturnTransition(returnTransition);
|
window.setEnterTransition(slide);
|
||||||
|
window.setTransitionBackgroundFadeDuration(getResources()
|
||||||
|
.getInteger(android.R.integer.config_longAnimTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ 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();
|
||||||
|
|||||||
@@ -1,209 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -9,15 +9,11 @@ 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;
|
||||||
@@ -28,7 +24,6 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.bramble.api.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;
|
||||||
@@ -56,7 +51,6 @@ 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;
|
||||||
@@ -75,8 +69,6 @@ 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;
|
||||||
@@ -100,11 +92,8 @@ 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;
|
||||||
@@ -115,9 +104,6 @@ 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;
|
||||||
@@ -130,7 +116,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, AttachmentCache {
|
TextCache {
|
||||||
|
|
||||||
public static final String CONTACT_ID = "briar.CONTACT_ID";
|
public static final String CONTACT_ID = "briar.CONTACT_ID";
|
||||||
|
|
||||||
@@ -148,7 +134,6 @@ 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;
|
||||||
@@ -158,7 +143,6 @@ 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
|
||||||
@@ -196,10 +180,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle state) {
|
public void onCreate(@Nullable Bundle state) {
|
||||||
if (SDK_INT >= 21) {
|
setSceneTransitionAnimation();
|
||||||
Transition slide = new Slide(END);
|
|
||||||
setSceneTransitionAnimation(slide, null, slide);
|
|
||||||
}
|
|
||||||
super.onCreate(state);
|
super.onCreate(state);
|
||||||
|
|
||||||
Intent i = getIntent();
|
Intent i = getIntent();
|
||||||
@@ -210,7 +191,6 @@ 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);
|
||||||
|
|
||||||
@@ -237,12 +217,11 @@ 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, this,
|
visitor = new ConversationVisitor(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);
|
||||||
layoutManager = new LinearLayoutManager(this);
|
list.setLayoutManager(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));
|
||||||
|
|
||||||
@@ -361,30 +340,14 @@ 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();
|
||||||
PrivateMessageHeader h = (PrivateMessageHeader) latest;
|
String text = textCache.get(id);
|
||||||
if (h.hasText()) {
|
if (text == null) {
|
||||||
String text = textCache.get(id);
|
LOG.info("Eagerly loading text of latest message");
|
||||||
if (text == null) {
|
text = messagingManager.getMessageText(id);
|
||||||
LOG.info(
|
textCache.put(id, text);
|
||||||
"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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -445,44 +408,16 @@ 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);
|
||||||
Pair<Integer, ConversationMessageItem> pair =
|
SparseArray<ConversationMessageItem> messages =
|
||||||
adapter.getMessageItem(m);
|
adapter.getMessageItems();
|
||||||
if (pair != null) {
|
for (int i = 0; i < messages.size(); i++) {
|
||||||
pair.getSecond().setText(text);
|
ConversationItem item = messages.valueAt(i);
|
||||||
boolean bottom = adapter.isScrolledToBottom(layoutManager);
|
if (item.getId().equals(m)) {
|
||||||
adapter.notifyItemChanged(pair.getFirst());
|
item.setText(text);
|
||||||
if (bottom) list.scrollToPosition(adapter.getItemCount() - 1);
|
adapter.notifyItemChanged(messages.keyAt(i));
|
||||||
}
|
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);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -531,10 +466,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);
|
||||||
if (bottom) list.scrollToPosition(adapter.getItemCount() - 1);
|
// Scroll to the bottom
|
||||||
|
list.scrollToPosition(adapter.getItemCount() - 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -815,31 +750,6 @@ 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 {
|
||||||
@@ -872,16 +782,4 @@ 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,12 @@ 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;
|
||||||
|
|
||||||
@@ -101,20 +98,16 @@ class ConversationAdapter
|
|||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
SparseArray<ConversationMessageItem> getMessageItems() {
|
||||||
Pair<Integer, ConversationMessageItem> getMessageItem(MessageId messageId) {
|
SparseArray<ConversationMessageItem> messages = new SparseArray<>();
|
||||||
|
|
||||||
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) {
|
||||||
item.getId().equals(messageId)) {
|
messages.put(i, (ConversationMessageItem) item);
|
||||||
return new Pair<>(i, (ConversationMessageItem) item);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return messages;
|
||||||
}
|
|
||||||
|
|
||||||
boolean isScrolledToBottom(LinearLayoutManager layoutManager) {
|
|
||||||
return layoutManager.findLastVisibleItemPosition() == items.size() - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 ConstraintLayout layout;
|
protected final ViewGroup layout;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final OutItemViewHolder outViewHolder;
|
private final OutItemViewHolder outViewHolder;
|
||||||
private final TextView text;
|
private final TextView text;
|
||||||
protected final TextView time;
|
private final TextView time;
|
||||||
|
|
||||||
ConversationItemViewHolder(View v, boolean isIncoming) {
|
ConversationItemViewHolder(View v, boolean isIncoming) {
|
||||||
super(v);
|
super(v);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
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;
|
||||||
|
|
||||||
@@ -15,7 +14,4 @@ interface ConversationListener {
|
|||||||
|
|
||||||
void openRequestedShareable(ConversationRequestItem item);
|
void openRequestedShareable(ConversationRequestItem item);
|
||||||
|
|
||||||
void onAttachmentClicked(View view, ConversationMessageItem messageItem,
|
|
||||||
AttachmentItem attachmentItem);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ 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;
|
||||||
@@ -13,20 +14,15 @@ import javax.annotation.concurrent.NotThreadSafe;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class ConversationMessageItem extends ConversationItem {
|
class ConversationMessageItem extends ConversationItem {
|
||||||
|
|
||||||
private List<AttachmentItem> attachments;
|
private final List<AttachmentHeader> attachments;
|
||||||
|
|
||||||
ConversationMessageItem(@LayoutRes int layoutRes, PrivateMessageHeader h,
|
ConversationMessageItem(@LayoutRes int layoutRes, PrivateMessageHeader h) {
|
||||||
List<AttachmentItem> attachments) {
|
|
||||||
super(layoutRes, h);
|
super(layoutRes, h);
|
||||||
this.attachments = attachments;
|
this.attachments = h.getAttachmentHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AttachmentItem> getAttachments() {
|
List<AttachmentHeader> getAttachments() {
|
||||||
return attachments;
|
return attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAttachments(List<AttachmentItem> attachments) {
|
|
||||||
this.attachments = attachments;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,161 +1,18 @@
|
|||||||
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 {
|
||||||
|
|
||||||
@DrawableRes
|
// image support will be added here (#1242)
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -16,7 +16,6 @@ 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;
|
||||||
@@ -39,7 +38,6 @@ 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;
|
||||||
@@ -54,12 +52,10 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
@Inject
|
@Inject
|
||||||
ConversationViewModel(Application application,
|
ConversationViewModel(Application application,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
ContactManager contactManager, MessagingManager messagingManager) {
|
ContactManager contactManager) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,10 +96,6 @@ public class ConversationViewModel extends AndroidViewModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
AttachmentController getAttachmentController() {
|
|
||||||
return attachmentController;
|
|
||||||
}
|
|
||||||
|
|
||||||
LiveData<Contact> getContact() {
|
LiveData<Contact> getContact() {
|
||||||
return contact;
|
return contact;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,16 +14,12 @@ 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;
|
||||||
@@ -37,33 +33,24 @@ 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,
|
||||||
AttachmentCache attachmentCache, LiveData<String> contactName) {
|
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, attachments);
|
R.layout.list_item_conversation_msg_out, h);
|
||||||
} else {
|
} else {
|
||||||
item = new ConversationMessageItem(
|
item = new ConversationMessageItem(
|
||||||
R.layout.list_item_conversation_msg_in, h, attachments);
|
R.layout.list_item_conversation_msg_in, h);
|
||||||
}
|
}
|
||||||
if (h.hasText()) {
|
if (h.hasText()) {
|
||||||
String text = textCache.getText(h.getId());
|
String text = textCache.getText(h.getId());
|
||||||
@@ -292,9 +279,4 @@ class ConversationVisitor implements
|
|||||||
@Nullable
|
@Nullable
|
||||||
String getText(MessageId m);
|
String getText(MessageId m);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AttachmentCache {
|
|
||||||
List<AttachmentItem> getAttachmentItems(MessageId m,
|
|
||||||
List<AttachmentHeader> headers);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,233 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -16,7 +16,6 @@ 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;
|
||||||
@@ -32,7 +31,6 @@ 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;
|
||||||
@@ -62,16 +60,12 @@ 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;
|
||||||
@@ -123,13 +117,6 @@ 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;
|
||||||
@@ -331,12 +318,6 @@ 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
|
||||||
|
|||||||
@@ -1,161 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -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_border"
|
android:bottom="@dimen/message_bubble_padding_bottom"
|
||||||
android:left="@dimen/message_bubble_border"
|
android:left="@dimen/message_bubble_padding_sides"
|
||||||
android:right="@dimen/message_bubble_border"
|
android:right="@dimen/message_bubble_padding_sides"
|
||||||
android:top="@dimen/message_bubble_border"/>
|
android:top="@dimen/message_bubble_padding_top"/>
|
||||||
<solid
|
<solid
|
||||||
android:color="@color/msg_in"/>
|
android:color="@color/msg_in"/>
|
||||||
<stroke
|
<stroke
|
||||||
|
|||||||
@@ -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_border"
|
android:bottom="@dimen/message_bubble_padding_bottom"
|
||||||
android:left="@dimen/message_bubble_border"
|
android:left="@dimen/message_bubble_padding_sides"
|
||||||
android:right="@dimen/message_bubble_border"
|
android:right="@dimen/message_bubble_padding_sides"
|
||||||
android:top="@dimen/message_bubble_border"/>
|
android:top="@dimen/message_bubble_padding_top"/>
|
||||||
<solid
|
<solid
|
||||||
android:color="@color/msg_out"/>
|
android:color="@color/msg_out"/>
|
||||||
<stroke
|
<stroke
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -15,55 +15,29 @@
|
|||||||
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_constrainedWidth="true"
|
app:layout_constraintBottom_toTopOf="@+id/time"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/statusLayout"
|
app:layout_constraintEnd_toEndOf="@+id/time"
|
||||||
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_toBottomOf="@+id/imageView"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="The text of a message which can sometimes be a bit longer as well"/>
|
tools:text="Short message"/>
|
||||||
|
|
||||||
<LinearLayout
|
<TextView
|
||||||
android:id="@+id/statusLayout"
|
android:id="@+id/time"
|
||||||
|
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>
|
||||||
|
|||||||
@@ -21,68 +21,39 @@
|
|||||||
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_constrainedWidth="true"
|
app:layout_constraintBottom_toTopOf="@+id/time"
|
||||||
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_toBottomOf="@+id/imageView"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
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."/>
|
||||||
|
|
||||||
<LinearLayout
|
<TextView
|
||||||
android:id="@+id/statusLayout"
|
android:id="@+id/time"
|
||||||
|
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_marginTop="@dimen/message_bubble_timestamp_margin"
|
||||||
android:layout_marginLeft="@dimen/message_bubble_padding_sides_inner"
|
android:textColor="@color/private_message_date_inverse"
|
||||||
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:ignore="UseCompoundDrawables">
|
tools:text="Dec 24, 13:37"/>
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:id="@+id/time"
|
android:id="@+id/status"
|
||||||
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_marginLeft="@dimen/margin_medium"
|
||||||
android:layout_marginEnd="6dp"
|
android:layout_marginStart="@dimen/margin_medium"
|
||||||
android:layout_marginRight="6dp"
|
app:layout_constraintBottom_toBottomOf="@+id/time"
|
||||||
android:textColor="@color/private_message_date_inverse"
|
app:layout_constraintStart_toEndOf="@+id/time"
|
||||||
tools:text="Dec 24, 13:37"/>
|
app:layout_constraintTop_toTopOf="@+id/time"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
<ImageView
|
tools:src="@drawable/message_delivered"/>
|
||||||
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>
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,6 @@
|
|||||||
<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>
|
||||||
|
|||||||
@@ -43,18 +43,9 @@
|
|||||||
<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>
|
||||||
|
|||||||
@@ -134,8 +134,6 @@
|
|||||||
<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>
|
||||||
|
|||||||
@@ -18,12 +18,6 @@
|
|||||||
<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>
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ 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',
|
||||||
@@ -85,11 +84,6 @@ 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',
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import org.briarproject.briar.api.messaging.PrivateMessage;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public abstract class ThreadedMessage extends PrivateMessage {
|
public abstract class ThreadedMessage extends PrivateMessage {
|
||||||
@@ -21,7 +19,7 @@ public abstract class ThreadedMessage extends PrivateMessage {
|
|||||||
|
|
||||||
public ThreadedMessage(Message message, @Nullable MessageId parent,
|
public ThreadedMessage(Message message, @Nullable MessageId parent,
|
||||||
Author author) {
|
Author author) {
|
||||||
super(message, emptyList());
|
super(message);
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.author = author;
|
this.author = author;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ package org.briarproject.briar.api.messaging;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -12,18 +10,13 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
public class PrivateMessage {
|
public class PrivateMessage {
|
||||||
|
|
||||||
private final Message message;
|
private final Message message;
|
||||||
private final List<AttachmentHeader> attachments;
|
|
||||||
|
|
||||||
public PrivateMessage(Message message, List<AttachmentHeader> attachments) {
|
public PrivateMessage(Message message) {
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.attachments = attachments;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message getMessage() {
|
public Message getMessage() {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<AttachmentHeader> getAttachmentHeaders() {
|
|
||||||
return attachments;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ 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;
|
||||||
@@ -200,6 +201,8 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,15 +423,22 @@ 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();
|
||||||
if (type == REQUEST) {
|
try {
|
||||||
messages.add(parseInvitationRequest(txn, contactGroupId, m,
|
if (type == REQUEST) {
|
||||||
meta, status, meta.getSessionId(), authorInfos));
|
messages.add(parseInvitationRequest(txn,
|
||||||
} else if (type == ACCEPT) {
|
contactGroupId, m, meta, status,
|
||||||
messages.add(parseInvitationResponse(txn, contactGroupId, m,
|
meta.getSessionId(), authorInfos));
|
||||||
meta, status, ss.bdfSession, authorInfos, true));
|
} else if (type == ACCEPT) {
|
||||||
} else if (type == DECLINE) {
|
messages.add(parseInvitationResponse(txn,
|
||||||
messages.add(parseInvitationResponse(txn, contactGroupId, m,
|
contactGroupId, m, meta, status,
|
||||||
meta, status, ss.bdfSession, authorInfos, false));
|
ss.bdfSession, authorInfos, true));
|
||||||
|
} else if (type == DECLINE) {
|
||||||
|
messages.add(parseInvitationResponse(txn,
|
||||||
|
contactGroupId, m, meta, status,
|
||||||
|
ss.bdfSession, authorInfos, false));
|
||||||
|
}
|
||||||
|
} catch (MessageDeletedException ex) {
|
||||||
|
// FIXME
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return messages;
|
return messages;
|
||||||
|
|||||||
@@ -11,16 +11,13 @@ 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.Metadata;
|
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.io.MessageInputStreamFactory;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.Client;
|
import org.briarproject.bramble.api.sync.Client;
|
||||||
import org.briarproject.bramble.api.sync.Group;
|
import org.briarproject.bramble.api.sync.Group;
|
||||||
import org.briarproject.bramble.api.sync.Group.Visibility;
|
import org.briarproject.bramble.api.sync.Group.Visibility;
|
||||||
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.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.api.sync.MessageStatus;
|
import org.briarproject.bramble.api.sync.MessageStatus;
|
||||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
||||||
@@ -35,17 +32,16 @@ import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
|||||||
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent;
|
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent;
|
||||||
import org.briarproject.briar.client.ConversationClientImpl;
|
import org.briarproject.briar.client.ConversationClientImpl;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_LENGTH;
|
|
||||||
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
|
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -55,21 +51,15 @@ class MessagingManagerImpl extends ConversationClientImpl
|
|||||||
|
|
||||||
private final ClientVersioningManager clientVersioningManager;
|
private final ClientVersioningManager clientVersioningManager;
|
||||||
private final ContactGroupFactory contactGroupFactory;
|
private final ContactGroupFactory contactGroupFactory;
|
||||||
private final MessageFactory messageFactory;
|
|
||||||
private final MessageInputStreamFactory messageInputStreamFactory;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
MessagingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
|
MessagingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
|
||||||
ClientVersioningManager clientVersioningManager,
|
ClientVersioningManager clientVersioningManager,
|
||||||
MetadataParser metadataParser, MessageTracker messageTracker,
|
MetadataParser metadataParser, MessageTracker messageTracker,
|
||||||
ContactGroupFactory contactGroupFactory,
|
ContactGroupFactory contactGroupFactory) {
|
||||||
MessageFactory messageFactory,
|
|
||||||
MessageInputStreamFactory messageInputStreamFactory) {
|
|
||||||
super(db, clientHelper, metadataParser, messageTracker);
|
super(db, clientHelper, metadataParser, messageTracker);
|
||||||
this.clientVersioningManager = clientVersioningManager;
|
this.clientVersioningManager = clientVersioningManager;
|
||||||
this.contactGroupFactory = contactGroupFactory;
|
this.contactGroupFactory = contactGroupFactory;
|
||||||
this.messageFactory = messageFactory;
|
|
||||||
this.messageInputStreamFactory = messageInputStreamFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -137,7 +127,8 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -152,11 +143,9 @@ class MessagingManagerImpl extends ConversationClientImpl
|
|||||||
meta.put("read", true);
|
meta.put("read", true);
|
||||||
clientHelper.addLocalMessage(txn, m.getMessage(), meta, true);
|
clientHelper.addLocalMessage(txn, m.getMessage(), meta, true);
|
||||||
messageTracker.trackOutgoingMessage(txn, m.getMessage());
|
messageTracker.trackOutgoingMessage(txn, m.getMessage());
|
||||||
for (AttachmentHeader h : m.getAttachmentHeaders())
|
|
||||||
db.setMessageShared(txn, h.getMessageId());
|
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new AssertionError(e);
|
throw new RuntimeException(e);
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(txn);
|
db.endTransaction(txn);
|
||||||
}
|
}
|
||||||
@@ -164,16 +153,11 @@ class MessagingManagerImpl extends ConversationClientImpl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AttachmentHeader addLocalAttachment(GroupId groupId, long timestamp,
|
public AttachmentHeader addLocalAttachment(GroupId groupId, long timestamp,
|
||||||
String contentType, ByteBuffer data) throws DbException {
|
String contentType, ByteBuffer data) {
|
||||||
// TODO: Remove this restriction when large messages are supported
|
// TODO add real implementation
|
||||||
byte[] body = data.array();
|
byte[] b = new byte[MessageId.LENGTH];
|
||||||
if (body.length > MAX_MESSAGE_LENGTH) throw new DbException();
|
new Random().nextBytes(b);
|
||||||
// TODO: Store message type and content type
|
return new AttachmentHeader(new MessageId(b), "image/png");
|
||||||
Message m = messageFactory.createMessage(groupId, timestamp, body);
|
|
||||||
Metadata meta = new Metadata();
|
|
||||||
// Attachment will be shared when private message is added
|
|
||||||
db.transaction(false, txn -> db.addLocalMessage(txn, m, meta, false));
|
|
||||||
return new AttachmentHeader(m.getId(), contentType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ContactId getContactId(Transaction txn, GroupId g)
|
private ContactId getContactId(Transaction txn, GroupId g)
|
||||||
@@ -253,9 +237,8 @@ class MessagingManagerImpl extends ConversationClientImpl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Attachment getAttachment(MessageId m) {
|
public Attachment getAttachment(MessageId m) {
|
||||||
InputStream in = messageInputStreamFactory.getMessageInputStream(m);
|
// TODO add real implementation
|
||||||
// TODO: Read message type and content type
|
throw new IllegalStateException("Not yet implemented");
|
||||||
return new Attachment(in);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,6 @@ class PrivateMessageFactoryImpl implements PrivateMessageFactory {
|
|||||||
// Serialise the message
|
// Serialise the message
|
||||||
BdfList message = BdfList.of(text);
|
BdfList message = BdfList.of(text);
|
||||||
Message m = clientHelper.createMessage(groupId, timestamp, message);
|
Message m = clientHelper.createMessage(groupId, timestamp, message);
|
||||||
return new PrivateMessage(m, attachments);
|
return new PrivateMessage(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ 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;
|
||||||
@@ -196,20 +200,29 @@ abstract class AbstractProtocolEngine<S extends Session>
|
|||||||
|
|
||||||
void subscribeToPrivateGroup(Transaction txn, MessageId inviteId)
|
void subscribeToPrivateGroup(Transaction txn, MessageId inviteId)
|
||||||
throws DbException, FormatException {
|
throws DbException, FormatException {
|
||||||
InviteMessage invite = messageParser.getInviteMessage(txn, inviteId);
|
BdfDictionary meta =
|
||||||
PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup(
|
clientHelper.getMessageMetadataAsDictionary(txn, inviteId);
|
||||||
invite.getGroupName(), invite.getCreator(), invite.getSalt());
|
GroupId groupId = new GroupId(meta.getRaw(MSG_KEY_PRIVATE_GROUP_ID));
|
||||||
|
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(), invite.getTimestamp() + 1);
|
Math.max(clock.currentTimeMillis(), inviteTimestamp + 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, invite.getTimestamp(),
|
privateGroup.getId(), timestamp, member, inviteTimestamp,
|
||||||
invite.getSignature());
|
signature);
|
||||||
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(),
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ 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";
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ 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;
|
||||||
@@ -167,6 +168,8 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,15 +387,19 @@ 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();
|
||||||
if (type == INVITE) {
|
try {
|
||||||
messages.add(parseInvitationRequest(txn, contactGroupId, m,
|
if (type == INVITE) {
|
||||||
meta, status));
|
messages.add(parseInvitationRequest(txn,
|
||||||
} else if (type == JOIN) {
|
contactGroupId, m, meta, status));
|
||||||
messages.add(parseInvitationResponse(contactGroupId, m,
|
} else if (type == JOIN) {
|
||||||
meta, status, true));
|
messages.add(parseInvitationResponse(contactGroupId, m,
|
||||||
} else if (type == LEAVE) {
|
meta, status, true));
|
||||||
messages.add(parseInvitationResponse(contactGroupId, m,
|
} else if (type == LEAVE) {
|
||||||
meta, status, false));
|
messages.add(parseInvitationResponse(contactGroupId, m,
|
||||||
|
meta, status, false));
|
||||||
|
}
|
||||||
|
} catch (MessageDeletedException ex) {
|
||||||
|
// FIXME
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return messages;
|
return messages;
|
||||||
@@ -441,8 +448,13 @@ 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()) {
|
||||||
items.add(parseGroupInvitationItem(txn, c, m));
|
try {
|
||||||
|
items.add(parseGroupInvitationItem(txn, c, m));
|
||||||
|
} catch (MessageDeletedException e) {
|
||||||
|
// FIXME
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ 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;
|
||||||
|
|
||||||
@@ -100,9 +99,9 @@ class GroupInvitationValidator extends BdfMessageValidator {
|
|||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
// Create the metadata
|
// Create the metadata
|
||||||
BdfDictionary meta = messageEncoder.encodeMetadata(INVITE,
|
BdfDictionary meta = messageEncoder.encodeInviteMetadata(
|
||||||
privateGroup.getId(), m.getTimestamp(), false, false, false,
|
privateGroup.getId(), privateGroup.getGroup().getDescriptor(),
|
||||||
false, false);
|
signature, m.getTimestamp(), false, false, false, false, false);
|
||||||
return new BdfMessageContext(meta);
|
return new BdfMessageContext(meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ 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);
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ 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;
|
||||||
@@ -42,6 +44,25 @@ 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,
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ 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;
|
||||||
@@ -25,6 +27,7 @@ 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
|
||||||
@@ -84,9 +87,10 @@ 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 {
|
||||||
InviteMessage<Blog> invite =
|
BdfDictionary meta =
|
||||||
messageParser.getInviteMessage(txn, inviteId);
|
clientHelper.getMessageMetadataAsDictionary(txn, inviteId);
|
||||||
blogManager.addBlog(txn, invite.getShareable());
|
BdfList descriptor = meta.getList(MSG_KEY_DESCRIPTOR);
|
||||||
|
blogManager.addBlog(txn, messageParser.createShareable(descriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ 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;
|
||||||
@@ -13,18 +15,19 @@ 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
|
||||||
@@ -85,9 +88,10 @@ 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 {
|
||||||
InviteMessage<Forum> invite =
|
BdfDictionary meta =
|
||||||
messageParser.getInviteMessage(txn, inviteId);
|
clientHelper.getMessageMetadataAsDictionary(txn, inviteId);
|
||||||
forumManager.addForum(txn, invite.getShareable());
|
BdfList descriptor = meta.getList(MSG_KEY_DESCRIPTOR);
|
||||||
|
forumManager.addForum(txn, messageParser.createShareable(descriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ 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);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user