itemResults =
@@ -64,12 +64,12 @@ class AttachmentCreatorImpl implements AttachmentCreator {
@Inject
AttachmentCreatorImpl(Application app, @IoExecutor Executor ioExecutor,
MessagingManager messagingManager, AttachmentRetriever retriever,
- ImageSizeCalculator imageSizeCalculator) {
+ ImageCompressor imageCompressor) {
this.app = app;
this.ioExecutor = ioExecutor;
this.messagingManager = messagingManager;
this.retriever = retriever;
- this.imageSizeCalculator = imageSizeCalculator;
+ this.imageCompressor = imageCompressor;
}
@Override
@@ -89,7 +89,7 @@ class AttachmentCreatorImpl implements AttachmentCreator {
if (id == null) throw new IllegalStateException();
boolean needsSize = uris.size() == 1;
task = new AttachmentCreationTask(messagingManager,
- app.getContentResolver(), this, imageSizeCalculator, id,
+ app.getContentResolver(), this, imageCompressor, id,
uris, needsSize);
ioExecutor.execute(() -> task.storeAttachments());
});
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentItem.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentItem.java
index 9ce231c7d..46abaf78e 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentItem.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentItem.java
@@ -4,8 +4,9 @@ import android.os.Parcel;
import android.os.Parcelable;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import javax.annotation.concurrent.Immutable;
@@ -78,6 +79,9 @@ public class AttachmentItem implements Parcelable {
}
protected AttachmentItem(Parcel in) {
+ byte[] groupIdByte = new byte[GroupId.LENGTH];
+ in.readByteArray(groupIdByte);
+ GroupId groupId = new GroupId(groupIdByte);
byte[] messageIdByte = new byte[MessageId.LENGTH];
in.readByteArray(messageIdByte);
MessageId messageId = new MessageId(messageIdByte);
@@ -88,7 +92,7 @@ public class AttachmentItem implements Parcelable {
thumbnailWidth = in.readInt();
thumbnailHeight = in.readInt();
state = State.valueOf(requireNonNull(in.readString()));
- header = new AttachmentHeader(messageId, mimeType);
+ header = new AttachmentHeader(groupId, messageId, mimeType);
}
public AttachmentHeader getHeader() {
@@ -142,6 +146,7 @@ public class AttachmentItem implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByteArray(header.getGroupId().getBytes());
dest.writeByteArray(header.getMessageId().getBytes());
dest.writeInt(width);
dest.writeInt(height);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentManager.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentManager.java
index db7f0e850..07f48d44a 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentManager.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentManager.java
@@ -3,7 +3,7 @@ package org.briarproject.briar.android.attachment;
import android.net.Uri;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import java.util.Collection;
import java.util.List;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentModule.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentModule.java
index 57ae44f56..4b3a4de51 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentModule.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentModule.java
@@ -12,16 +12,6 @@ import static org.briarproject.briar.android.attachment.AttachmentDimensions.get
@Module
public class AttachmentModule {
- @Provides
- ImageHelper provideImageHelper(ImageHelperImpl imageHelper) {
- return imageHelper;
- }
-
- @Provides
- ImageSizeCalculator provideImageSizeCalculator(ImageHelper imageHelper) {
- return new ImageSizeCalculator(imageHelper);
- }
-
@Provides
AttachmentDimensions provideAttachmentDimensions(Application app) {
return getAttachmentDimensions(app.getResources());
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentRetriever.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentRetriever.java
index 2bf6b69ea..3df13d61e 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentRetriever.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentRetriever.java
@@ -4,8 +4,8 @@ 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.api.messaging.Attachment;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
+import org.briarproject.briar.api.attachment.Attachment;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import org.briarproject.briar.api.messaging.event.AttachmentReceivedEvent;
@@ -49,10 +49,10 @@ public interface AttachmentRetriever {
* Loads an {@link AttachmentItem}
* that arrived via an {@link AttachmentReceivedEvent}
* and notifies the associated {@link LiveData}.
- *
+ *
* Note that you need to call {@link #getAttachmentItems(PrivateMessageHeader)}
* first to get the LiveData.
- *
+ *
* It is possible that no LiveData is available,
* because the message of the AttachmentItem did not arrive, yet.
* In this case, the load wil be deferred until the message arrives.
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentRetrieverImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentRetrieverImpl.java
index 1faa7dd1e..9f8c3cc15 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentRetrieverImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/AttachmentRetrieverImpl.java
@@ -6,9 +6,12 @@ import org.briarproject.bramble.api.db.NoSuchMessageException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.android.attachment.AttachmentItem.State;
-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.android.attachment.media.ImageHelper;
+import org.briarproject.briar.android.attachment.media.ImageSizeCalculator;
+import org.briarproject.briar.android.attachment.media.Size;
+import org.briarproject.briar.api.attachment.Attachment;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.briarproject.briar.api.attachment.AttachmentReader;
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import java.io.BufferedInputStream;
@@ -43,7 +46,7 @@ class AttachmentRetrieverImpl implements AttachmentRetriever {
@DatabaseExecutor
private final Executor dbExecutor;
- private final MessagingManager messagingManager;
+ private final AttachmentReader attachmentReader;
private final ImageHelper imageHelper;
private final ImageSizeCalculator imageSizeCalculator;
private final int defaultSize;
@@ -57,11 +60,10 @@ class AttachmentRetrieverImpl implements AttachmentRetriever {
@Inject
AttachmentRetrieverImpl(@DatabaseExecutor Executor dbExecutor,
- MessagingManager messagingManager,
- AttachmentDimensions dimensions, ImageHelper imageHelper,
- ImageSizeCalculator imageSizeCalculator) {
+ AttachmentReader attachmentReader, AttachmentDimensions dimensions,
+ ImageHelper imageHelper, ImageSizeCalculator imageSizeCalculator) {
this.dbExecutor = dbExecutor;
- this.messagingManager = messagingManager;
+ this.attachmentReader = attachmentReader;
this.imageHelper = imageHelper;
this.imageSizeCalculator = imageSizeCalculator;
defaultSize = dimensions.defaultSize;
@@ -75,7 +77,7 @@ class AttachmentRetrieverImpl implements AttachmentRetriever {
@DatabaseExecutor
public Attachment getMessageAttachment(AttachmentHeader h)
throws DbException {
- return messagingManager.getAttachment(h);
+ return attachmentReader.getAttachment(h);
}
@Override
@@ -86,13 +88,11 @@ class AttachmentRetrieverImpl implements AttachmentRetriever {
boolean needsSize = headers.size() == 1;
for (AttachmentHeader h : headers) {
// try cache for existing item live data
- MutableLiveData liveData;
- if (needsSize) liveData = itemsWithSize.get(h.getMessageId());
- else {
- // try items with size first, as they work as well
- liveData = itemsWithSize.get(h.getMessageId());
- if (liveData == null)
- liveData = itemsWithoutSize.get(h.getMessageId());
+ MutableLiveData liveData =
+ itemsWithSize.get(h.getMessageId());
+ if (!needsSize && liveData == null) {
+ // check cache for items that don't need the size
+ liveData = itemsWithoutSize.get(h.getMessageId());
}
// create new live data with LOADING item if cache miss
@@ -131,7 +131,7 @@ class AttachmentRetrieverImpl implements AttachmentRetriever {
// If a live data is already cached we don't need to do anything
if (itemsWithSize.containsKey(h.getMessageId())) return;
try {
- Attachment a = messagingManager.getAttachment(h);
+ Attachment a = attachmentReader.getAttachment(h);
AttachmentItem item = createAttachmentItem(a, true);
MutableLiveData liveData =
new MutableLiveData<>(item);
@@ -173,7 +173,7 @@ class AttachmentRetrieverImpl implements AttachmentRetriever {
Attachment a;
AttachmentItem item;
try {
- a = messagingManager.getAttachment(h);
+ a = attachmentReader.getAttachment(h);
item = createAttachmentItem(a, needsSize);
} catch (NoSuchMessageException e) {
LOG.info("Attachment not received yet");
@@ -210,26 +210,30 @@ class AttachmentRetrieverImpl implements AttachmentRetriever {
private AttachmentItem createAttachmentItem(AttachmentHeader h, Size size) {
// calculate thumbnail size
- Size thumbnailSize = new Size(defaultSize, defaultSize, size.mimeType);
- if (!size.error) {
+ Size thumbnailSize =
+ new Size(defaultSize, defaultSize, size.getMimeType());
+ if (!size.hasError()) {
thumbnailSize =
- getThumbnailSize(size.width, size.height, size.mimeType);
+ getThumbnailSize(size.getWidth(), size.getHeight(),
+ size.getMimeType());
}
// get file extension
- String extension = imageHelper.getExtensionFromMimeType(size.mimeType);
- boolean hasError = extension == null || size.error;
- if (!h.getContentType().equals(size.mimeType)) {
+ String extension =
+ imageHelper.getExtensionFromMimeType(size.getMimeType());
+ boolean hasError = extension == null || size.hasError();
+ if (!h.getContentType().equals(size.getMimeType())) {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Header has different mime type (" +
- h.getContentType() + ") than image (" + size.mimeType +
- ").");
+ h.getContentType() + ") than image (" +
+ size.getMimeType() + ").");
}
hasError = true;
}
if (extension == null) extension = "";
State state = hasError ? ERROR : AVAILABLE;
- return new AttachmentItem(h, size.width, size.height,
- extension, thumbnailSize.width, thumbnailSize.height, state);
+ return new AttachmentItem(h, size.getWidth(), size.getHeight(),
+ extension, thumbnailSize.getWidth(), thumbnailSize.getHeight(),
+ state);
}
private Size getThumbnailSize(int width, int height, String mimeType) {
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/Size.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/Size.java
deleted file mode 100644
index 22871e1a5..000000000
--- a/briar-android/src/main/java/org/briarproject/briar/android/attachment/Size.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.briarproject.briar.android.attachment;
-
-class Size {
-
- final int width;
- final int height;
- final String mimeType;
- final boolean error;
-
- Size(int width, int height, String mimeType) {
- this.width = width;
- this.height = height;
- this.mimeType = mimeType;
- this.error = false;
- }
-
- Size() {
- this.width = 0;
- this.height = 0;
- this.mimeType = "";
- this.error = true;
- }
-}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/UnsupportedMimeTypeException.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/UnsupportedMimeTypeException.java
new file mode 100644
index 000000000..5b8cb7b35
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/UnsupportedMimeTypeException.java
@@ -0,0 +1,24 @@
+package org.briarproject.briar.android.attachment;
+
+import android.net.Uri;
+
+import java.io.IOException;
+
+public class UnsupportedMimeTypeException extends IOException {
+
+ private final String mimeType;
+ private final Uri uri;
+
+ public UnsupportedMimeTypeException(String mimeType, Uri uri) {
+ this.mimeType = mimeType;
+ this.uri = uri;
+ }
+
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ public Uri getUri() {
+ return uri;
+ }
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageCompressor.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageCompressor.java
new file mode 100644
index 000000000..2c2d150c7
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageCompressor.java
@@ -0,0 +1,39 @@
+package org.briarproject.briar.android.attachment.media;
+
+import android.graphics.Bitmap;
+import android.net.Uri;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface ImageCompressor {
+
+ /**
+ * The MIME type of compressed images
+ */
+ String MIME_TYPE = "image/jpeg";
+
+ /**
+ * Load an image from {@code is}, compress it and return an InputStream
+ * from which the resulting image can be read. The image will be compressed
+ * as a JPEG image such that it fits into a message.
+ *
+ * @param is the stream to read the source image from
+ * @param contentType the mimetype of the source image such as "image/jpeg"
+ * as obtained by {@link android.content.ContentResolver#getType(Uri)}
+ * @return a stream from which the resulting image can be read
+ */
+ InputStream compressImage(InputStream is, String contentType)
+ throws IOException;
+
+ /**
+ * Compress an image and return an InputStream from which the resulting
+ * image can be read. The image will be compressed as a JPEG image such that
+ * it fits into a message.
+ *
+ * @param bitmap the source image
+ * @return a stream from which the resulting image can be read
+ */
+ InputStream compressImage(Bitmap bitmap) throws IOException;
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageCompressorImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageCompressorImpl.java
new file mode 100644
index 000000000..e990fbb93
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageCompressorImpl.java
@@ -0,0 +1,92 @@
+package org.briarproject.briar.android.attachment.media;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import static android.graphics.Bitmap.CompressFormat.JPEG;
+import static android.graphics.BitmapFactory.decodeStream;
+import static java.util.logging.Level.INFO;
+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.briar.api.attachment.MediaConstants.MAX_IMAGE_SIZE;
+
+class ImageCompressorImpl implements ImageCompressor {
+
+ private static final Logger LOG =
+ getLogger(ImageCompressorImpl.class.getName());
+
+ private static final int MAX_ATTACHMENT_DIMENSION = 1000;
+
+ private final ImageSizeCalculator imageSizeCalculator;
+
+ @Inject
+ ImageCompressorImpl(ImageSizeCalculator imageSizeCalculator) {
+ this.imageSizeCalculator = imageSizeCalculator;
+ }
+
+ @Override
+ public InputStream compressImage(InputStream is, String contentType)
+ throws IOException {
+ try {
+ Bitmap bitmap =
+ createBitmap(is, contentType, MAX_ATTACHMENT_DIMENSION);
+ return compressImage(bitmap);
+ } finally {
+ tryToClose(is, LOG, WARNING);
+ }
+ }
+
+ @Override
+ public InputStream compressImage(Bitmap bitmap) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ for (int quality = 100; quality >= 0; quality -= 10) {
+ if (!bitmap.compress(JPEG, quality, out))
+ throw new IOException();
+ if (out.size() <= MAX_IMAGE_SIZE) {
+ if (LOG.isLoggable(INFO)) {
+ LOG.info("Compressed image to "
+ + out.size() + " bytes, quality " + quality);
+ }
+ return new ByteArrayInputStream(out.toByteArray());
+ }
+ out.reset();
+ }
+ throw new IOException();
+ }
+
+ private Bitmap createBitmap(InputStream is, String contentType, int maxSize)
+ throws IOException {
+ is = new BufferedInputStream(is);
+ Size size = imageSizeCalculator.getSize(is, contentType);
+ if (size.hasError()) throw new IOException();
+ if (LOG.isLoggable(INFO))
+ LOG.info("Original image size: " + size.getWidth() + "x" +
+ size.getHeight());
+ int dimension = Math.max(size.getWidth(), size.getHeight());
+ int inSampleSize = 1;
+ while (dimension > maxSize) {
+ inSampleSize *= 2;
+ dimension /= 2;
+ }
+ if (LOG.isLoggable(INFO))
+ LOG.info("Scaling attachment by factor of " + inSampleSize);
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = inSampleSize;
+ if (contentType.equals("image/png"))
+ options.inPreferredConfig = Bitmap.Config.RGB_565;
+ Bitmap bitmap = decodeStream(is, null, options);
+ if (bitmap == null) throw new IOException();
+ return bitmap;
+ }
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/ImageHelper.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageHelper.java
similarity index 90%
rename from briar-android/src/main/java/org/briarproject/briar/android/attachment/ImageHelper.java
rename to briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageHelper.java
index eb16cbe01..b8ef6d4b0 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/attachment/ImageHelper.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageHelper.java
@@ -1,4 +1,4 @@
-package org.briarproject.briar.android.attachment;
+package org.briarproject.briar.android.attachment.media;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/ImageHelperImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageHelperImpl.java
similarity index 94%
rename from briar-android/src/main/java/org/briarproject/briar/android/attachment/ImageHelperImpl.java
rename to briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageHelperImpl.java
index f7f2435eb..97a596674 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/attachment/ImageHelperImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageHelperImpl.java
@@ -1,4 +1,4 @@
-package org.briarproject.briar.android.attachment;
+package org.briarproject.briar.android.attachment.media;
import android.graphics.BitmapFactory;
import android.webkit.MimeTypeMap;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageSizeCalculator.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageSizeCalculator.java
new file mode 100644
index 000000000..52d34791d
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageSizeCalculator.java
@@ -0,0 +1,18 @@
+package org.briarproject.briar.android.attachment.media;
+
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+
+import java.io.InputStream;
+
+@NotNullByDefault
+public interface ImageSizeCalculator {
+
+ /**
+ * Determine the size of the image that can be read from {@code is}.
+ *
+ * @param contentType the mime type of the image. If "image/jpeg" is passed,
+ * the implementation will try to determine the size from the Exif header
+ */
+ Size getSize(InputStream is, String contentType);
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/ImageSizeCalculator.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageSizeCalculatorImpl.java
similarity index 87%
rename from briar-android/src/main/java/org/briarproject/briar/android/attachment/ImageSizeCalculator.java
rename to briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageSizeCalculatorImpl.java
index daccea2f5..d0e6d6dae 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/attachment/ImageSizeCalculator.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/ImageSizeCalculatorImpl.java
@@ -1,9 +1,9 @@
-package org.briarproject.briar.android.attachment;
+package org.briarproject.briar.android.attachment.media;
import com.bumptech.glide.util.MarkEnforcingInputStream;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
-import org.briarproject.briar.android.attachment.ImageHelper.DecodeResult;
+import org.briarproject.briar.android.attachment.media.ImageHelper.DecodeResult;
import java.io.IOException;
import java.io.InputStream;
@@ -23,20 +23,21 @@ import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
-class ImageSizeCalculator {
+class ImageSizeCalculatorImpl implements ImageSizeCalculator {
private static final Logger LOG =
- getLogger(ImageSizeCalculator.class.getName());
+ getLogger(ImageSizeCalculatorImpl.class.getName());
private static final int READ_LIMIT = 1024 * 8192;
private final ImageHelper imageHelper;
- ImageSizeCalculator(ImageHelper imageHelper) {
+ ImageSizeCalculatorImpl(ImageHelper imageHelper) {
this.imageHelper = imageHelper;
}
- Size getSize(InputStream is, String contentType) {
+ @Override
+ public Size getSize(InputStream is, String contentType) {
Size size = new Size();
is = new MarkEnforcingInputStream(is);
is.mark(READ_LIMIT);
@@ -49,7 +50,7 @@ class ImageSizeCalculator {
logException(LOG, WARNING, e);
}
}
- if (size.error) {
+ if (size.hasError()) {
// need to mark again to re-add read limit
is.mark(READ_LIMIT);
try {
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/MediaModule.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/MediaModule.java
new file mode 100644
index 000000000..b0d3ed5b9
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/MediaModule.java
@@ -0,0 +1,24 @@
+package org.briarproject.briar.android.attachment.media;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public class MediaModule {
+
+ @Provides
+ ImageHelper provideImageHelper(ImageHelperImpl imageHelper) {
+ return imageHelper;
+ }
+
+ @Provides
+ ImageSizeCalculator provideImageSizeCalculator(ImageHelper imageHelper) {
+ return new ImageSizeCalculatorImpl(imageHelper);
+ }
+
+ @Provides
+ ImageCompressor provideImageCompressor(
+ ImageCompressorImpl imageCompressor) {
+ return imageCompressor;
+ }
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/Size.java b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/Size.java
new file mode 100644
index 000000000..90413c138
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/attachment/media/Size.java
@@ -0,0 +1,46 @@
+package org.briarproject.briar.android.attachment.media;
+
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+
+import javax.annotation.concurrent.Immutable;
+
+@Immutable
+@NotNullByDefault
+public class Size {
+
+ private final int width;
+ private final int height;
+ private final String mimeType;
+ private final boolean error;
+
+ public Size(int width, int height, String mimeType) {
+ this.width = width;
+ this.height = height;
+ this.mimeType = mimeType;
+ this.error = false;
+ }
+
+ public Size() {
+ this.width = 0;
+ this.height = 0;
+ this.mimeType = "";
+ this.error = true;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ public boolean hasError() {
+ return error;
+ }
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java
index 7f5ef0de5..7afaa77b4 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java
@@ -1,7 +1,7 @@
package org.briarproject.briar.android.blog;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.blog.BlogPostHeader;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogFragment.java
index 3f6f881ad..318bdecab 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogFragment.java
@@ -20,7 +20,7 @@ import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.view.TextInputView;
import org.briarproject.briar.android.view.TextSendController;
import org.briarproject.briar.android.view.TextSendController.SendListener;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import java.util.List;
@@ -156,16 +156,16 @@ public class ReblogFragment extends BaseFragment implements SendListener {
progressBar = v.findViewById(R.id.progressBar);
post = new BlogPostViewHolder(v.findViewById(R.id.postLayout),
true, new OnBlogPostClickListener() {
- @Override
- public void onBlogPostClick(BlogPostItem post) {
- // do nothing
- }
+ @Override
+ public void onBlogPostClick(BlogPostItem post) {
+ // do nothing
+ }
- @Override
- public void onAuthorClick(BlogPostItem post) {
- // probably don't want to allow author clicks here
- }
- }, getFragmentManager());
+ @Override
+ public void onAuthorClick(BlogPostItem post) {
+ // probably don't want to allow author clicks here
+ }
+ }, getFragmentManager());
input = v.findViewById(R.id.inputText);
}
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/WriteBlogPostActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/WriteBlogPostActivity.java
index 6ac261ecf..e8532d6a8 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/WriteBlogPostActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/WriteBlogPostActivity.java
@@ -19,10 +19,10 @@ import org.briarproject.briar.android.view.TextInputView;
import org.briarproject.briar.android.view.TextSendController;
import org.briarproject.briar.android.view.TextSendController.SendListener;
import org.briarproject.briar.api.android.AndroidNotificationManager;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogPost;
import org.briarproject.briar.api.blog.BlogPostFactory;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
import java.security.GeneralSecurityException;
import java.util.List;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/BaseContactListAdapter.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/BaseContactListAdapter.java
index 2ddac95bc..056d4c77a 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/contact/BaseContactListAdapter.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/BaseContactListAdapter.java
@@ -3,14 +3,12 @@ package org.briarproject.briar.android.contact;
import android.content.Context;
import android.view.View;
-import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.briar.android.util.BriarAdapter;
import javax.annotation.Nullable;
import androidx.annotation.NonNull;
-import static androidx.recyclerview.widget.SortedList.INVALID_POSITION;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
public abstract class BaseContactListAdapter>
@@ -47,15 +45,6 @@ public abstract class BaseContactListAdapter {
void onItemClick(View view, I item);
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItem.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItem.java
index 25d2f2806..803eb5d73 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItem.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItem.java
@@ -2,6 +2,7 @@ package org.briarproject.briar.android.contact;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.briar.api.identity.AuthorInfo;
import javax.annotation.concurrent.Immutable;
@@ -10,14 +11,17 @@ import javax.annotation.concurrent.Immutable;
public class ContactItem {
private final Contact contact;
+ private final AuthorInfo authorInfo;
private final boolean connected;
- public ContactItem(Contact contact) {
- this(contact, false);
+ public ContactItem(Contact contact, AuthorInfo authorInfo) {
+ this(contact, authorInfo, false);
}
- public ContactItem(Contact contact, boolean connected) {
+ public ContactItem(Contact contact, AuthorInfo authorInfo,
+ boolean connected) {
this.contact = contact;
+ this.authorInfo = authorInfo;
this.connected = connected;
}
@@ -25,6 +29,10 @@ public class ContactItem {
return contact;
}
+ public AuthorInfo getAuthorInfo() {
+ return authorInfo;
+ }
+
boolean isConnected() {
return connected;
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItemViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItemViewHolder.java
index 154287f47..ffdf9d6ec 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItemViewHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItemViewHolder.java
@@ -5,7 +5,6 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
-import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
@@ -14,9 +13,9 @@ import javax.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.recyclerview.widget.RecyclerView;
-import im.delight.android.identicons.IdenticonDrawable;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
+import static org.briarproject.briar.android.view.AuthorView.setAvatar;
@UiThread
@NotNullByDefault
@@ -40,9 +39,7 @@ public class ContactItemViewHolder
}
protected void bind(I item, @Nullable OnContactClickListener listener) {
- Author author = item.getContact().getAuthor();
- avatar.setImageDrawable(
- new IdenticonDrawable(author.getId().getBytes()));
+ setAvatar(avatar, item);
name.setText(getContactDisplayName(item.getContact()));
if (bulb != null) {
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListAdapter.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListAdapter.java
index 5ea246827..13b64815e 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListAdapter.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListAdapter.java
@@ -5,6 +5,7 @@ import android.view.View;
import android.view.ViewGroup;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.bramble.api.nullsafety.NullSafety;
import org.briarproject.briar.R;
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
@@ -48,7 +49,11 @@ public class ContactListAdapter extends
if (c1.getTimestamp() != c2.getTimestamp()) {
return false;
}
- return c1.isConnected() == c2.isConnected();
+ if (c1.isConnected() != c2.isConnected()) {
+ return false;
+ }
+ return NullSafety.equals(c1.getAuthorInfo().getAvatarHeader(),
+ c2.getAuthorInfo().getAvatarHeader());
}
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListItem.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListItem.java
index d674f5295..8ca5cd004 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListItem.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListItem.java
@@ -2,8 +2,10 @@ package org.briarproject.briar.android.contact;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
+import org.briarproject.briar.api.identity.AuthorInfo;
import javax.annotation.concurrent.Immutable;
@@ -16,33 +18,44 @@ public class ContactListItem extends ContactItem
private final long timestamp;
private final int unread;
- public ContactListItem(Contact contact, boolean connected,
- GroupCount count) {
- super(contact, connected);
+ public ContactListItem(Contact contact, AuthorInfo authorInfo,
+ boolean connected, GroupCount count) {
+ super(contact, authorInfo, connected);
this.empty = count.getMsgCount() == 0;
this.unread = count.getUnreadCount();
this.timestamp = count.getLatestMsgTime();
}
- private ContactListItem(Contact contact, boolean connected, boolean empty,
- int unread, long timestamp) {
- super(contact, connected);
+ private ContactListItem(Contact contact, AuthorInfo authorInfo,
+ boolean connected, boolean empty, int unread, long timestamp) {
+ super(contact, authorInfo, connected);
this.empty = empty;
this.timestamp = timestamp;
this.unread = unread;
}
ContactListItem(ContactListItem item, boolean connected) {
- this(item.getContact(), connected, item.empty, item.unread,
- item.timestamp);
+ this(item.getContact(), item.getAuthorInfo(), connected, item.empty,
+ item.unread, item.timestamp);
}
ContactListItem(ContactListItem item, ConversationMessageHeader h) {
- this(item.getContact(), item.isConnected(), false,
+ this(item.getContact(), item.getAuthorInfo(), item.isConnected(), false,
h.isRead() ? item.unread : item.unread + 1,
Math.max(h.getTimestamp(), item.timestamp));
}
+ /**
+ * Creates a new copy of the given item with a new avatar
+ * referenced by the given attachment header.
+ */
+ ContactListItem(ContactListItem item,
+ AttachmentHeader attachmentHeader) {
+ this(item.getContact(), new AuthorInfo(item.getAuthorInfo().getStatus(),
+ item.getAuthorInfo().getAlias(), attachmentHeader),
+ item.isConnected(), item.empty, item.unread, item.timestamp);
+ }
+
boolean isEmpty() {
return empty;
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListViewModel.java
index 729a4bdd8..71a1c5740 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListViewModel.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListViewModel.java
@@ -25,10 +25,13 @@ import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.android.viewmodel.DbViewModel;
import org.briarproject.briar.android.viewmodel.LiveResult;
import org.briarproject.briar.api.android.AndroidNotificationManager;
+import org.briarproject.briar.api.avatar.event.AvatarUpdatedEvent;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
import java.util.ArrayList;
import java.util.Collections;
@@ -55,6 +58,7 @@ class ContactListViewModel extends DbViewModel implements EventListener {
getLogger(ContactListViewModel.class.getName());
private final ContactManager contactManager;
+ private final AuthorManager authorManager;
private final ConversationManager conversationManager;
private final ConnectionRegistry connectionRegistry;
private final EventBus eventBus;
@@ -71,11 +75,13 @@ class ContactListViewModel extends DbViewModel implements EventListener {
@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, TransactionManager db,
AndroidExecutor androidExecutor, ContactManager contactManager,
+ AuthorManager authorManager,
ConversationManager conversationManager,
ConnectionRegistry connectionRegistry, EventBus eventBus,
AndroidNotificationManager notificationManager) {
super(application, dbExecutor, lifecycleManager, db, androidExecutor);
this.contactManager = contactManager;
+ this.authorManager = authorManager;
this.conversationManager = conversationManager;
this.connectionRegistry = connectionRegistry;
this.eventBus = eventBus;
@@ -99,10 +105,11 @@ class ContactListViewModel extends DbViewModel implements EventListener {
List contacts = new ArrayList<>();
for (Contact c : contactManager.getContacts(txn)) {
ContactId id = c.getId();
+ AuthorInfo authorInfo = authorManager.getAuthorInfo(txn, c);
MessageTracker.GroupCount count =
conversationManager.getGroupCount(txn, id);
boolean connected = connectionRegistry.isConnected(c.getId());
- contacts.add(new ContactListItem(c, connected, count));
+ contacts.add(new ContactListItem(c, authorInfo, connected, count));
}
Collections.sort(contacts);
logDuration(LOG, "Full load", start);
@@ -133,6 +140,10 @@ class ContactListViewModel extends DbViewModel implements EventListener {
} else if (e instanceof PendingContactAddedEvent ||
e instanceof PendingContactRemovedEvent) {
checkForPendingContacts();
+ } else if (e instanceof AvatarUpdatedEvent) {
+ AvatarUpdatedEvent a = (AvatarUpdatedEvent) e;
+ updateItem(a.getContactId(), item -> new ContactListItem(item,
+ a.getAttachmentHeader()), false);
}
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contactselection/ContactSelectorControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/contactselection/ContactSelectorControllerImpl.java
index 434d3b603..da25fbda3 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/contactselection/ContactSelectorControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contactselection/ContactSelectorControllerImpl.java
@@ -10,6 +10,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.android.controller.DbControllerImpl;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
import java.util.ArrayList;
import java.util.Collection;
@@ -31,11 +33,14 @@ public abstract class ContactSelectorControllerImpl
Logger.getLogger(ContactSelectorControllerImpl.class.getName());
private final ContactManager contactManager;
+ private final AuthorManager authorManager;
public ContactSelectorControllerImpl(@DatabaseExecutor Executor dbExecutor,
- LifecycleManager lifecycleManager, ContactManager contactManager) {
+ LifecycleManager lifecycleManager, ContactManager contactManager,
+ AuthorManager authorManager) {
super(dbExecutor, lifecycleManager);
this.contactManager = contactManager;
+ this.authorManager = authorManager;
}
@Override
@@ -45,12 +50,13 @@ public abstract class ContactSelectorControllerImpl
try {
Collection contacts = new ArrayList<>();
for (Contact c : contactManager.getContacts()) {
+ AuthorInfo authorInfo = authorManager.getAuthorInfo(c);
// was this contact already selected?
boolean selected = selection.contains(c.getId());
// can this contact be selected?
boolean disabled = isDisabled(g, c);
- contacts.add(new SelectableContactItem(c, selected,
- disabled));
+ contacts.add(new SelectableContactItem(c, authorInfo,
+ selected, disabled));
}
handler.onResult(contacts);
} catch (DbException e) {
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contactselection/SelectableContactItem.java b/briar-android/src/main/java/org/briarproject/briar/android/contactselection/SelectableContactItem.java
index d2a53a2a4..a3d09f8ce 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/contactselection/SelectableContactItem.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/contactselection/SelectableContactItem.java
@@ -3,6 +3,7 @@ package org.briarproject.briar.android.contactselection;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.contact.ContactItem;
+import org.briarproject.briar.api.identity.AuthorInfo;
import javax.annotation.concurrent.NotThreadSafe;
@@ -10,11 +11,12 @@ import javax.annotation.concurrent.NotThreadSafe;
@NotNullByDefault
public class SelectableContactItem extends ContactItem {
- private boolean selected, disabled;
+ private boolean selected;
+ private final boolean disabled;
- public SelectableContactItem(Contact contact, boolean selected,
- boolean disabled) {
- super(contact);
+ public SelectableContactItem(Contact contact, AuthorInfo authorInfo,
+ boolean selected, boolean disabled) {
+ super(contact, authorInfo);
this.selected = selected;
this.disabled = disabled;
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/AliasDialogFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/AliasDialogFragment.java
index 4144ca31d..30df0ecad 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/AliasDialogFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/AliasDialogFragment.java
@@ -21,7 +21,6 @@ import javax.inject.Inject;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatDialogFragment;
import androidx.lifecycle.ViewModelProvider;
-import androidx.lifecycle.ViewModelProviders;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
import static java.util.Objects.requireNonNull;
@@ -59,7 +58,7 @@ public class AliasDialogFragment extends AppCompatDialogFragment {
setStyle(STYLE_NO_TITLE, R.style.BriarDialogTheme);
- viewModel = ViewModelProviders.of(requireActivity(), viewModelFactory)
+ viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
.get(ConversationViewModel.class);
}
@@ -72,7 +71,8 @@ public class AliasDialogFragment extends AppCompatDialogFragment {
aliasEditLayout = v.findViewById(R.id.aliasEditLayout);
aliasEditText = v.findViewById(R.id.aliasEditText);
- Contact contact = requireNonNull(viewModel.getContact().getValue());
+ Contact contact = requireNonNull(viewModel.getContactItem().getValue())
+ .getContact();
String alias = contact.getAlias();
aliasEditText.setText(alias);
if (alias != null) aliasEditText.setSelection(alias.length());
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java
index 9f85d0d8f..9a46153f1 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java
@@ -59,6 +59,7 @@ import org.briarproject.briar.android.view.TextAttachmentController.AttachmentLi
import org.briarproject.briar.android.view.TextInputView;
import org.briarproject.briar.android.view.TextSendController;
import org.briarproject.briar.api.android.AndroidNotificationManager;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import org.briarproject.briar.api.blog.BlogSharingManager;
import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.client.SessionId;
@@ -71,7 +72,6 @@ import org.briarproject.briar.api.conversation.DeletionResult;
import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent;
import org.briarproject.briar.api.forum.ForumSharingManager;
import org.briarproject.briar.api.introduction.IntroductionManager;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
@@ -106,7 +106,6 @@ import androidx.recyclerview.selection.StorageStrategy;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import de.hdodenhof.circleimageview.CircleImageView;
-import im.delight.android.identicons.IdenticonDrawable;
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt;
import static android.os.Build.VERSION.SDK_INT;
@@ -134,6 +133,7 @@ import static org.briarproject.briar.android.conversation.ImageActivity.DATE;
import static org.briarproject.briar.android.conversation.ImageActivity.ITEM_ID;
import static org.briarproject.briar.android.conversation.ImageActivity.NAME;
import static org.briarproject.briar.android.util.UiUtils.observeOnce;
+import static org.briarproject.briar.android.view.AuthorView.setAvatar;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH;
@@ -233,10 +233,9 @@ public class ConversationActivity extends BriarActivity
toolbarStatus = toolbar.findViewById(R.id.contactStatus);
toolbarTitle = toolbar.findViewById(R.id.contactName);
- observeOnce(viewModel.getContactAuthorId(), this, authorId -> {
- requireNonNull(authorId);
- toolbarAvatar.setImageDrawable(
- new IdenticonDrawable(authorId.getBytes()));
+ viewModel.getContactItem().observe(this, contactItem -> {
+ requireNonNull(contactItem);
+ setAvatar(toolbarAvatar, contactItem);
});
viewModel.getContactDisplayName().observe(this, contactName -> {
requireNonNull(contactName);
@@ -366,7 +365,7 @@ public class ConversationActivity extends BriarActivity
}
});
// enable alias action if available
- observeOnce(viewModel.getContact(), this, contact ->
+ observeOnce(viewModel.getContactItem(), this, contact ->
menu.findItem(R.id.action_set_alias).setEnabled(true));
return super.onCreateOptionsMenu(menu);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java
index 7ff5d50e4..2a7e23dc7 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java
@@ -14,7 +14,6 @@ import org.briarproject.bramble.api.db.TransactionManager;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
-import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.settings.Settings;
@@ -27,11 +26,15 @@ import org.briarproject.briar.android.attachment.AttachmentCreator;
import org.briarproject.briar.android.attachment.AttachmentManager;
import org.briarproject.briar.android.attachment.AttachmentResult;
import org.briarproject.briar.android.attachment.AttachmentRetriever;
+import org.briarproject.briar.android.contact.ContactItem;
import org.briarproject.briar.android.util.UiUtils;
import org.briarproject.briar.android.viewmodel.DbViewModel;
import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.briarproject.briar.api.avatar.event.AvatarUpdatedEvent;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessage;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
@@ -49,8 +52,8 @@ import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
-import androidx.lifecycle.Transformations;
+import static androidx.lifecycle.Transformations.map;
import static java.util.Objects.requireNonNull;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
@@ -76,6 +79,7 @@ public class ConversationViewModel extends DbViewModel
private final EventBus eventBus;
private final MessagingManager messagingManager;
private final ContactManager contactManager;
+ private final AuthorManager authorManager;
private final SettingsManager settingsManager;
private final PrivateMessageFactory privateMessageFactory;
private final AttachmentRetriever attachmentRetriever;
@@ -83,11 +87,10 @@ public class ConversationViewModel extends DbViewModel
@Nullable
private ContactId contactId = null;
- private final MutableLiveData contact = new MutableLiveData<>();
- private final LiveData contactAuthorId =
- Transformations.map(contact, c -> c.getAuthor().getId());
- private final LiveData contactName =
- Transformations.map(contact, UiUtils::getContactDisplayName);
+ private final MutableLiveData contactItem =
+ new MutableLiveData<>();
+ private final LiveData contactName = map(contactItem, c ->
+ UiUtils.getContactDisplayName(c.getContact()));
private final LiveData messagingGroupId;
private final MutableLiveData imageSupport =
new MutableLiveData<>();
@@ -111,6 +114,7 @@ public class ConversationViewModel extends DbViewModel
EventBus eventBus,
MessagingManager messagingManager,
ContactManager contactManager,
+ AuthorManager authorManager,
SettingsManager settingsManager,
PrivateMessageFactory privateMessageFactory,
AttachmentRetriever attachmentRetriever,
@@ -120,12 +124,13 @@ public class ConversationViewModel extends DbViewModel
this.eventBus = eventBus;
this.messagingManager = messagingManager;
this.contactManager = contactManager;
+ this.authorManager = authorManager;
this.settingsManager = settingsManager;
this.privateMessageFactory = privateMessageFactory;
this.attachmentRetriever = attachmentRetriever;
this.attachmentCreator = attachmentCreator;
- messagingGroupId = Transformations
- .map(contact, c -> messagingManager.getContactGroup(c).getId());
+ messagingGroupId = map(contactItem, c ->
+ messagingManager.getContactGroup(c.getContact()).getId());
contactDeleted.setValue(false);
eventBus.addListener(this);
@@ -147,9 +152,33 @@ public class ConversationViewModel extends DbViewModel
runOnDbThread(() -> attachmentRetriever
.loadAttachmentItem(a.getMessageId()));
}
+ } else if (e instanceof AvatarUpdatedEvent) {
+ AvatarUpdatedEvent a = (AvatarUpdatedEvent) e;
+ if (a.getContactId().equals(contactId)) {
+ LOG.info("Avatar updated");
+ updateAvatar(a);
+ }
}
}
+ @UiThread
+ private void updateAvatar(AvatarUpdatedEvent a) {
+ // Make sure that contactItem has been set by the task initiated
+ // by loadContact() before we update the avatar.
+ observeForeverOnce(contactItem, oldContactItem -> {
+ requireNonNull(oldContactItem);
+
+ AuthorInfo oldAuthorInfo = oldContactItem.getAuthorInfo();
+
+ AuthorInfo newAuthorInfo = new AuthorInfo(oldAuthorInfo.getStatus(),
+ oldAuthorInfo.getAlias(), a.getAttachmentHeader());
+ ContactItem newContactItem =
+ new ContactItem(oldContactItem.getContact(), newAuthorInfo);
+
+ contactItem.setValue(newContactItem);
+ });
+ }
+
/**
* Setting the {@link ContactId} automatically triggers loading of other
* data.
@@ -168,7 +197,8 @@ public class ConversationViewModel extends DbViewModel
try {
long start = now();
Contact c = contactManager.getContact(contactId);
- contact.postValue(c);
+ AuthorInfo authorInfo = authorManager.getAuthorInfo(c);
+ contactItem.postValue(new ContactItem(c, authorInfo));
logDuration(LOG, "Loading contact", start);
start = now();
checkFeaturesAndOnboarding(contactId);
@@ -319,12 +349,8 @@ public class ConversationViewModel extends DbViewModel
return attachmentRetriever;
}
- LiveData getContact() {
- return contact;
- }
-
- LiveData getContactAuthorId() {
- return contactAuthorId;
+ LiveData getContactItem() {
+ return contactItem;
}
LiveData getContactDisplayName() {
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageFragment.java
index 0b750c53e..5d588b750 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageFragment.java
@@ -121,7 +121,7 @@ public class ImageFragment extends Fragment
private void loadImage() {
GlideApp.with(this)
- .load(attachment)
+ .load(attachment.getHeader())
// TODO allow if size < maxTextureSize ?
// .override(SIZE_ORIGINAL)
.diskCacheStrategy(NONE)
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageViewHolder.java
index 659bcf29e..a2dba12e2 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageViewHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageViewHolder.java
@@ -77,7 +77,7 @@ class ImageViewHolder extends ViewHolder {
private void loadImage(AttachmentItem a, Radii r) {
Transformation transformation = new BriarImageTransformation(r);
GlideApp.with(imageView)
- .load(a)
+ .load(a.getHeader())
.diskCacheStrategy(NONE)
.error(ERROR_RES)
.transform(transformation)
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageViewModel.java
index eeba81f3b..ca0b75179 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageViewModel.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageViewModel.java
@@ -20,8 +20,8 @@ import org.briarproject.briar.android.attachment.AttachmentItem;
import org.briarproject.briar.android.viewmodel.DbViewModel;
import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
-import org.briarproject.briar.api.messaging.Attachment;
-import org.briarproject.briar.api.messaging.MessagingManager;
+import org.briarproject.briar.api.attachment.Attachment;
+import org.briarproject.briar.api.attachment.AttachmentReader;
import org.briarproject.briar.api.messaging.event.AttachmentReceivedEvent;
import java.io.File;
@@ -56,7 +56,7 @@ public class ImageViewModel extends DbViewModel implements EventListener {
private static final Logger LOG = getLogger(ImageViewModel.class.getName());
- private final MessagingManager messagingManager;
+ private final AttachmentReader attachmentReader;
private final EventBus eventBus;
@IoExecutor
private final Executor ioExecutor;
@@ -75,16 +75,14 @@ public class ImageViewModel extends DbViewModel implements EventListener {
private int toolbarTop, toolbarBottom;
@Inject
- ImageViewModel(Application application,
- MessagingManager messagingManager,
- EventBus eventBus,
- @DatabaseExecutor Executor dbExecutor,
+ ImageViewModel(Application application, AttachmentReader attachmentReader,
+ EventBus eventBus, @DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager,
TransactionManager db,
AndroidExecutor androidExecutor,
@IoExecutor Executor ioExecutor) {
super(application, dbExecutor, lifecycleManager, db, androidExecutor);
- this.messagingManager = messagingManager;
+ this.attachmentReader = attachmentReader;
this.eventBus = eventBus;
this.ioExecutor = ioExecutor;
@@ -202,7 +200,7 @@ public class ImageViewModel extends DbViewModel implements EventListener {
runOnDbThread(() -> {
try {
Attachment a =
- messagingManager.getAttachment(attachment.getHeader());
+ attachmentReader.getAttachment(attachment.getHeader());
copyImageFromDb(a, osp, afterCopy);
} catch (DbException e) {
logException(LOG, WARNING, e);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarDataFetcher.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarDataFetcher.java
index d208ab475..44bb41aa0 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarDataFetcher.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarDataFetcher.java
@@ -7,9 +7,9 @@ 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.briar.android.attachment.AttachmentItem;
-import org.briarproject.briar.api.messaging.Attachment;
-import org.briarproject.briar.api.messaging.MessagingManager;
+import org.briarproject.briar.api.attachment.Attachment;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.briarproject.briar.api.attachment.AttachmentReader;
import java.io.InputStream;
import java.util.concurrent.Executor;
@@ -30,21 +30,22 @@ class BriarDataFetcher implements DataFetcher {
private final static Logger LOG =
getLogger(BriarDataFetcher.class.getName());
- private final MessagingManager messagingManager;
+ private final AttachmentReader attachmentReader;
@DatabaseExecutor
private final Executor dbExecutor;
- private final AttachmentItem attachment;
+ private final AttachmentHeader attachmentHeader;
@Nullable
private volatile InputStream inputStream;
private volatile boolean cancel = false;
@Inject
- BriarDataFetcher(MessagingManager messagingManager,
- @DatabaseExecutor Executor dbExecutor, AttachmentItem attachment) {
- this.messagingManager = messagingManager;
+ BriarDataFetcher(AttachmentReader attachmentReader,
+ @DatabaseExecutor Executor dbExecutor,
+ AttachmentHeader attachmentHeader) {
+ this.attachmentReader = attachmentReader;
this.dbExecutor = dbExecutor;
- this.attachment = attachment;
+ this.attachmentHeader = attachmentHeader;
}
@Override
@@ -53,8 +54,7 @@ class BriarDataFetcher implements DataFetcher {
dbExecutor.execute(() -> {
if (cancel) return;
try {
- Attachment a =
- messagingManager.getAttachment(attachment.getHeader());
+ Attachment a = attachmentReader.getAttachment(attachmentHeader);
inputStream = a.getStream();
callback.onDataReady(inputStream);
} catch (DbException e) {
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarDataFetcherFactory.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarDataFetcherFactory.java
index 69a920ae9..63867df74 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarDataFetcherFactory.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarDataFetcherFactory.java
@@ -2,8 +2,8 @@ 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.attachment.AttachmentItem;
-import org.briarproject.briar.api.messaging.MessagingManager;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.briarproject.briar.api.attachment.AttachmentReader;
import java.util.concurrent.Executor;
@@ -12,19 +12,19 @@ import javax.inject.Inject;
@NotNullByDefault
public class BriarDataFetcherFactory {
- private final MessagingManager messagingManager;
+ private final AttachmentReader attachmentReader;
@DatabaseExecutor
private final Executor dbExecutor;
@Inject
- public BriarDataFetcherFactory(MessagingManager messagingManager,
+ public BriarDataFetcherFactory(AttachmentReader attachmentReader,
@DatabaseExecutor Executor dbExecutor) {
- this.messagingManager = messagingManager;
+ this.attachmentReader = attachmentReader;
this.dbExecutor = dbExecutor;
}
- BriarDataFetcher createBriarDataFetcher(AttachmentItem model) {
- return new BriarDataFetcher(messagingManager, dbExecutor, model);
+ BriarDataFetcher createBriarDataFetcher(AttachmentHeader model) {
+ return new BriarDataFetcher(attachmentReader, dbExecutor, model);
}
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarGlideModule.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarGlideModule.java
index a477f2f44..b269a0307 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarGlideModule.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarGlideModule.java
@@ -10,7 +10,7 @@ import com.bumptech.glide.module.AppGlideModule;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.BriarApplication;
-import org.briarproject.briar.android.attachment.AttachmentItem;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import java.io.InputStream;
@@ -28,7 +28,7 @@ public final class BriarGlideModule extends AppGlideModule {
BriarApplication app =
(BriarApplication) context.getApplicationContext();
BriarModelLoaderFactory factory = new BriarModelLoaderFactory(app);
- registry.prepend(AttachmentItem.class, InputStream.class, factory);
+ registry.prepend(AttachmentHeader.class, InputStream.class, factory);
}
@Override
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarModelLoader.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarModelLoader.java
index dd5008f67..952a3bf6f 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarModelLoader.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarModelLoader.java
@@ -8,7 +8,7 @@ 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.attachment.AttachmentItem;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import java.io.InputStream;
@@ -17,7 +17,7 @@ import javax.inject.Inject;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public final class BriarModelLoader
- implements ModelLoader {
+ implements ModelLoader {
@Inject
BriarDataFetcherFactory dataFetcherFactory;
@@ -27,8 +27,8 @@ public final class BriarModelLoader
}
@Override
- public LoadData buildLoadData(AttachmentItem model, int width,
- int height, Options options) {
+ public LoadData buildLoadData(AttachmentHeader model,
+ int width, int height, Options options) {
ObjectKey key = new ObjectKey(model.getMessageId());
BriarDataFetcher dataFetcher =
dataFetcherFactory.createBriarDataFetcher(model);
@@ -36,7 +36,7 @@ public final class BriarModelLoader
}
@Override
- public boolean handles(AttachmentItem model) {
+ public boolean handles(AttachmentHeader model) {
return true;
}
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarModelLoaderFactory.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarModelLoaderFactory.java
index 07ed62227..adb6b95e8 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarModelLoaderFactory.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarModelLoaderFactory.java
@@ -6,13 +6,13 @@ 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.attachment.AttachmentItem;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import java.io.InputStream;
@NotNullByDefault
class BriarModelLoaderFactory
- implements ModelLoaderFactory {
+ implements ModelLoaderFactory {
private final BriarApplication app;
@@ -21,7 +21,7 @@ class BriarModelLoaderFactory
}
@Override
- public ModelLoader build(
+ public ModelLoader build(
MultiModelLoaderFactory multiFactory) {
return new BriarModelLoader(app);
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumPostItem.java b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumPostItem.java
index 03df1b0a0..7366725de 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumPostItem.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumPostItem.java
@@ -1,7 +1,7 @@
package org.briarproject.briar.android.forum;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.android.threaded.ThreadItem;
import org.briarproject.briar.api.forum.ForumPostHeader;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/introduction/ContactChooserFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/introduction/ContactChooserFragment.java
index 49e274c97..7a655f2af 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/introduction/ContactChooserFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/introduction/ContactChooserFragment.java
@@ -21,6 +21,8 @@ import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.conversation.ConversationManager;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
import java.util.ArrayList;
import java.util.List;
@@ -49,10 +51,12 @@ public class ContactChooserFragment extends BaseFragment {
private ContactId contactId;
// Fields that are accessed from background threads must be volatile
- volatile Contact c1;
+ private volatile Contact c1;
@Inject
volatile ContactManager contactManager;
@Inject
+ volatile AuthorManager authorManager;
+ @Inject
volatile ConversationManager conversationManager;
@Inject
volatile ConnectionRegistry connectionRegistry;
@@ -123,12 +127,14 @@ public class ContactChooserFragment extends BaseFragment {
if (c.getId().equals(contactId)) {
c1 = c;
} else {
+ AuthorInfo authorInfo = authorManager.getAuthorInfo(c);
ContactId id = c.getId();
GroupCount count =
conversationManager.getGroupCount(id);
boolean connected =
connectionRegistry.isConnected(c.getId());
- contacts.add(new ContactListItem(c, connected, count));
+ contacts.add(new ContactListItem(c, authorInfo,
+ connected, count));
}
}
displayContacts(contacts);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java
index e7d85dc91..28ed4bc8b 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/introduction/IntroductionMessageFragment.java
@@ -18,12 +18,15 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
+import org.briarproject.briar.android.contact.ContactItem;
import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.view.TextInputView;
import org.briarproject.briar.android.view.TextSendController;
import org.briarproject.briar.android.view.TextSendController.SendListener;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.introduction.IntroductionManager;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
import java.util.List;
import java.util.logging.Logger;
@@ -33,7 +36,6 @@ import javax.inject.Inject;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import de.hdodenhof.circleimageview.CircleImageView;
-import im.delight.android.identicons.IdenticonDrawable;
import static android.app.Activity.RESULT_OK;
import static android.view.View.GONE;
@@ -43,6 +45,7 @@ import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
import static org.briarproject.briar.android.util.UiUtils.hideSoftKeyboard;
+import static org.briarproject.briar.android.view.AuthorView.setAvatar;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_TEXT_LENGTH;
@MethodsNotNullByDefault
@@ -65,6 +68,8 @@ public class IntroductionMessageFragment extends BaseFragment
@Inject
protected volatile ContactManager contactManager;
@Inject
+ protected volatile AuthorManager authorManager;
+ @Inject
protected volatile IntroductionManager introductionManager;
public static IntroductionMessageFragment newInstance(int contactId1,
@@ -137,11 +142,16 @@ public class IntroductionMessageFragment extends BaseFragment
private void prepareToSetUpViews(int contactId1, int contactId2) {
introductionActivity.runOnDbThread(() -> {
try {
- Contact c1 = contactManager.getContact(
- new ContactId(contactId1));
- Contact c2 = contactManager.getContact(
- new ContactId(contactId2));
- boolean possible = introductionManager.canIntroduce(c1, c2);
+ Contact contact1 =
+ contactManager.getContact(new ContactId(contactId1));
+ Contact contact2 =
+ contactManager.getContact(new ContactId(contactId2));
+ AuthorInfo a1 = authorManager.getAuthorInfo(contact1);
+ AuthorInfo a2 = authorManager.getAuthorInfo(contact2);
+ boolean possible =
+ introductionManager.canIntroduce(contact1, contact2);
+ ContactItem c1 = new ContactItem(contact1, a1);
+ ContactItem c2 = new ContactItem(contact2, a2);
setUpViews(c1, c2, possible);
} catch (DbException e) {
logException(LOG, WARNING, e);
@@ -149,20 +159,18 @@ public class IntroductionMessageFragment extends BaseFragment
});
}
- private void setUpViews(Contact c1, Contact c2, boolean possible) {
+ private void setUpViews(ContactItem c1, ContactItem c2, boolean possible) {
introductionActivity.runOnUiThreadUnlessDestroyed(() -> {
- contact1 = c1;
- contact2 = c2;
+ contact1 = c1.getContact();
+ contact2 = c2.getContact();
// set avatars
- ui.avatar1.setImageDrawable(new IdenticonDrawable(
- c1.getAuthor().getId().getBytes()));
- ui.avatar2.setImageDrawable(new IdenticonDrawable(
- c2.getAuthor().getId().getBytes()));
+ setAvatar(ui.avatar1, c1);
+ setAvatar(ui.avatar2, c2);
// set contact names
- ui.contactName1.setText(getContactDisplayName(c1));
- ui.contactName2.setText(getContactDisplayName(c2));
+ ui.contactName1.setText(getContactDisplayName(c1.getContact()));
+ ui.contactName2.setText(getContactDisplayName(c2.getContact()));
// hide progress bar
ui.progressBar.setVisibility(GONE);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupMessageItem.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupMessageItem.java
index 315beff27..308f3d498 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupMessageItem.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupMessageItem.java
@@ -1,7 +1,7 @@
package org.briarproject.briar.android.privategroup.conversation;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/JoinMessageItemViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/JoinMessageItemViewHolder.java
index e6c1e9c98..6ee712071 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/JoinMessageItemViewHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/JoinMessageItemViewHolder.java
@@ -10,7 +10,7 @@ import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListe
import androidx.annotation.UiThread;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.OURSELVES;
@UiThread
@NotNullByDefault
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/creation/CreateGroupControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/creation/CreateGroupControllerImpl.java
index 88355a63b..809f224e6 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/creation/CreateGroupControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/creation/CreateGroupControllerImpl.java
@@ -15,6 +15,7 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
+import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.privategroup.GroupMessage;
import org.briarproject.briar.api.privategroup.GroupMessageFactory;
import org.briarproject.briar.api.privategroup.PrivateGroup;
@@ -59,12 +60,13 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
CreateGroupControllerImpl(@DatabaseExecutor Executor dbExecutor,
@CryptoExecutor Executor cryptoExecutor,
LifecycleManager lifecycleManager, ContactManager contactManager,
- IdentityManager identityManager, PrivateGroupFactory groupFactory,
+ AuthorManager authorManager, IdentityManager identityManager,
+ PrivateGroupFactory groupFactory,
GroupMessageFactory groupMessageFactory,
PrivateGroupManager groupManager,
GroupInvitationFactory groupInvitationFactory,
GroupInvitationManager groupInvitationManager, Clock clock) {
- super(dbExecutor, lifecycleManager, contactManager);
+ super(dbExecutor, lifecycleManager, contactManager, authorManager);
this.cryptoExecutor = cryptoExecutor;
this.contactManager = contactManager;
this.identityManager = identityManager;
@@ -176,7 +178,6 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
// Continue
}
}
- //noinspection ConstantConditions
handler.onResult(null);
} catch (DbException e) {
logException(LOG, WARNING, e);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupItem.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupItem.java
index 00ba7e97e..f7f361216 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupItem.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupItem.java
@@ -1,7 +1,7 @@
package org.briarproject.briar.android.privategroup.list;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListViewModel.java
index 25857fca5..041305a37 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListViewModel.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListViewModel.java
@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.identity.AuthorId;
-import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
@@ -24,6 +23,8 @@ import org.briarproject.briar.android.viewmodel.DbViewModel;
import org.briarproject.briar.android.viewmodel.LiveResult;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
import org.briarproject.briar.api.privategroup.PrivateGroup;
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
@@ -64,7 +65,7 @@ class GroupListViewModel extends DbViewModel implements EventListener {
private final PrivateGroupManager groupManager;
private final GroupInvitationManager groupInvitationManager;
- private final ContactManager contactManager;
+ private final AuthorManager authorManager;
private final AndroidNotificationManager notificationManager;
private final EventBus eventBus;
@@ -81,12 +82,12 @@ class GroupListViewModel extends DbViewModel implements EventListener {
AndroidExecutor androidExecutor,
PrivateGroupManager groupManager,
GroupInvitationManager groupInvitationManager,
- ContactManager contactManager,
+ AuthorManager authorManager,
AndroidNotificationManager notificationManager, EventBus eventBus) {
super(application, dbExecutor, lifecycleManager, db, androidExecutor);
this.groupManager = groupManager;
this.groupInvitationManager = groupInvitationManager;
- this.contactManager = contactManager;
+ this.authorManager = authorManager;
this.notificationManager = notificationManager;
this.eventBus = eventBus;
this.eventBus.addListener(this);
@@ -157,7 +158,7 @@ class GroupListViewModel extends DbViewModel implements EventListener {
if (authorInfos.containsKey(authorId)) {
authorInfo = requireNonNull(authorInfos.get(authorId));
} else {
- authorInfo = contactManager.getAuthorInfo(txn, authorId);
+ authorInfo = authorManager.getAuthorInfo(txn, authorId);
authorInfos.put(authorId, authorInfo);
}
GroupCount count = groupManager.getGroupCount(txn, id);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItem.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItem.java
index a83c2c9a3..2b57b4e69 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItem.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItem.java
@@ -2,8 +2,8 @@ package org.briarproject.briar.android.privategroup.memberlist;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
-import org.briarproject.bramble.api.identity.AuthorInfo.Status;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo.Status;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.privategroup.GroupMember;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItemHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItemHolder.java
index c0713a0cd..dd3d958b7 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItemHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItemHolder.java
@@ -13,7 +13,7 @@ import androidx.recyclerview.widget.RecyclerView;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
@UiThread
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealContactsControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealContactsControllerImpl.java
index f8c1fe1d7..edf50600c 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealContactsControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealContactsControllerImpl.java
@@ -14,6 +14,8 @@ import org.briarproject.briar.android.controller.DbControllerImpl;
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.api.client.ProtocolStateException;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.privategroup.GroupMember;
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
@@ -45,17 +47,20 @@ class RevealContactsControllerImpl extends DbControllerImpl
private final PrivateGroupManager groupManager;
private final GroupInvitationManager groupInvitationManager;
private final ContactManager contactManager;
+ private final AuthorManager authorManager;
private final SettingsManager settingsManager;
@Inject
RevealContactsControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, PrivateGroupManager groupManager,
GroupInvitationManager groupInvitationManager,
- ContactManager contactManager, SettingsManager settingsManager) {
+ ContactManager contactManager, AuthorManager authorManager,
+ SettingsManager settingsManager) {
super(dbExecutor, lifecycleManager);
this.groupManager = groupManager;
this.groupInvitationManager = groupInvitationManager;
this.contactManager = contactManager;
+ this.authorManager = authorManager;
this.settingsManager = settingsManager;
}
@@ -82,11 +87,12 @@ class RevealContactsControllerImpl extends DbControllerImpl
for (GroupMember m : members) {
for (Contact c : contacts) {
if (m.getAuthor().equals(c.getAuthor())) {
+ AuthorInfo authorInfo = authorManager.getAuthorInfo(c);
boolean disabled = m.getVisibility() != INVISIBLE;
boolean selected =
disabled || selection.contains(c.getId());
- items.add(new RevealableContactItem(c, selected, disabled,
- m.getVisibility()));
+ items.add(new RevealableContactItem(c, authorInfo, selected,
+ disabled, m.getVisibility()));
}
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealableContactItem.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealableContactItem.java
index 48ae57ecd..57f84947f 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealableContactItem.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/reveal/RevealableContactItem.java
@@ -3,6 +3,7 @@ package org.briarproject.briar.android.privategroup.reveal;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.contactselection.SelectableContactItem;
+import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.briar.api.privategroup.Visibility;
import javax.annotation.concurrent.NotThreadSafe;
@@ -13,9 +14,9 @@ class RevealableContactItem extends SelectableContactItem {
private final Visibility visibility;
- RevealableContactItem(Contact contact, boolean selected,
- boolean disabled, Visibility visibility) {
- super(contact, selected, disabled);
+ RevealableContactItem(Contact contact, AuthorInfo authorInfo,
+ boolean selected, boolean disabled, Visibility visibility) {
+ super(contact, authorInfo, selected, disabled);
this.visibility = visibility;
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/ConfirmAvatarDialogFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/ConfirmAvatarDialogFragment.java
new file mode 100644
index 000000000..4c87a3fc0
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/ConfirmAvatarDialogFragment.java
@@ -0,0 +1,91 @@
+package org.briarproject.briar.android.settings;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
+import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
+import org.briarproject.briar.R;
+import org.briarproject.briar.android.activity.BaseActivity;
+
+import javax.inject.Inject;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.ViewModelProvider;
+
+import static java.util.Objects.requireNonNull;
+
+@MethodsNotNullByDefault
+@ParametersNotNullByDefault
+public class ConfirmAvatarDialogFragment extends DialogFragment {
+
+ final static String TAG = ConfirmAvatarDialogFragment.class.getName();
+
+ @Inject
+ ViewModelProvider.Factory viewModelFactory;
+ private SettingsViewModel settingsViewModel;
+
+ private static final String ARG_URI = "uri";
+ private Uri uri;
+
+ static ConfirmAvatarDialogFragment newInstance(Uri uri) {
+ ConfirmAvatarDialogFragment f = new ConfirmAvatarDialogFragment();
+
+ Bundle args = new Bundle();
+ args.putString(ARG_URI, uri.toString());
+ f.setArguments(args);
+
+ return f;
+ }
+
+ @Override
+ public void onAttach(Context ctx) {
+ super.onAttach(ctx);
+ ((BaseActivity) requireActivity()).getActivityComponent().inject(this);
+ }
+
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ Bundle args = requireArguments();
+ String argUri = requireNonNull(args.getString(ARG_URI));
+ uri = Uri.parse(argUri);
+
+ FragmentActivity activity = requireActivity();
+
+ ViewModelProvider provider =
+ new ViewModelProvider(activity, viewModelFactory);
+ settingsViewModel = provider.get(SettingsViewModel.class);
+
+ AlertDialog.Builder builder =
+ new AlertDialog.Builder(activity, R.style.BriarDialogTheme);
+
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ final View view =
+ inflater.inflate(R.layout.fragment_confirm_avatar_dialog, null);
+ builder.setView(view);
+
+ builder.setTitle(R.string.dialog_confirm_profile_picture_title);
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.setPositiveButton(R.string.change,
+ (dialog, id) -> settingsViewModel.setAvatar(uri));
+
+ ImageView imageView = view.findViewById(R.id.image);
+ imageView.setImageURI(uri);
+
+ TextView textViewUserName = view.findViewById(R.id.username);
+ settingsViewModel.getOwnIdentityInfo().observe(activity,
+ us -> textViewUserName.setText(us.getLocalAuthor().getName()));
+
+ return builder.create();
+ }
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/OwnIdentityInfo.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/OwnIdentityInfo.java
new file mode 100644
index 000000000..b6a3780f3
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/OwnIdentityInfo.java
@@ -0,0 +1,29 @@
+package org.briarproject.briar.android.settings;
+
+import org.briarproject.bramble.api.identity.LocalAuthor;
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.briar.api.identity.AuthorInfo;
+
+import javax.annotation.concurrent.Immutable;
+
+@Immutable
+@NotNullByDefault
+class OwnIdentityInfo {
+
+ private final LocalAuthor localAuthor;
+ private final AuthorInfo authorInfo;
+
+ OwnIdentityInfo(LocalAuthor localAuthor, AuthorInfo authorInfo) {
+ this.localAuthor = localAuthor;
+ this.authorInfo = authorInfo;
+ }
+
+ LocalAuthor getLocalAuthor() {
+ return localAuthor;
+ }
+
+ AuthorInfo getAuthorInfo() {
+ return authorInfo;
+ }
+
+}
\ No newline at end of file
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java
index 311711b1b..747bc9895 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java
@@ -1,16 +1,39 @@
package org.briarproject.briar.android.settings;
+import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+import org.briarproject.bramble.api.FeatureFlags;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
+import org.briarproject.briar.android.util.UiUtils;
+import org.briarproject.briar.android.view.AuthorView;
+import javax.inject.Inject;
+
+import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
+import androidx.lifecycle.ViewModelProvider;
+import de.hdodenhof.circleimageview.CircleImageView;
+
+import static android.widget.Toast.LENGTH_LONG;
+import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_AVATAR_IMAGE;
public class SettingsActivity extends BriarActivity {
+ @Inject
+ ViewModelProvider.Factory viewModelFactory;
+ private SettingsViewModel settingsViewModel;
+
+ @Inject
+ FeatureFlags featureFlags;
+
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
@@ -22,6 +45,37 @@ public class SettingsActivity extends BriarActivity {
}
setContentView(R.layout.activity_settings);
+
+ if (featureFlags.shouldEnableProfilePictures()) {
+ ViewModelProvider provider =
+ new ViewModelProvider(this, viewModelFactory);
+ settingsViewModel = provider.get(SettingsViewModel.class);
+
+ TextView textViewUserName = findViewById(R.id.username);
+ CircleImageView imageViewAvatar =
+ findViewById(R.id.avatarImage);
+
+ settingsViewModel.getOwnIdentityInfo().observe(this, us -> {
+ textViewUserName.setText(us.getLocalAuthor().getName());
+ AuthorView.setAvatar(imageViewAvatar,
+ us.getLocalAuthor().getId(), us.getAuthorInfo());
+ });
+
+ settingsViewModel.getSetAvatarFailed()
+ .observeEvent(this, failed -> {
+ if (failed) {
+ Toast.makeText(this,
+ R.string.change_profile_picture_failed_message,
+ LENGTH_LONG).show();
+ }
+ });
+
+ View avatarGroup = findViewById(R.id.avatarGroup);
+ avatarGroup.setOnClickListener(e -> selectAvatarImage());
+ } else {
+ View view = findViewById(R.id.avatarGroup);
+ view.setVisibility(View.GONE);
+ }
}
@Override
@@ -37,4 +91,31 @@ public class SettingsActivity extends BriarActivity {
}
return false;
}
+
+ private void selectAvatarImage() {
+ Intent intent = UiUtils.createSelectImageIntent(false);
+ startActivityForResult(intent, REQUEST_AVATAR_IMAGE);
+ }
+
+ @Override
+ protected void onActivityResult(int request, int result,
+ @Nullable Intent data) {
+ super.onActivityResult(request, result, data);
+
+ if (request == REQUEST_AVATAR_IMAGE && result == RESULT_OK) {
+ onAvatarImageReceived(data);
+ }
+ }
+
+ private void onAvatarImageReceived(@Nullable Intent resultData) {
+ if (resultData == null) return;
+ Uri uri = resultData.getData();
+ if (uri == null) return;
+
+ ConfirmAvatarDialogFragment dialog =
+ ConfirmAvatarDialogFragment.newInstance(uri);
+ dialog.show(getSupportFragmentManager(),
+ ConfirmAvatarDialogFragment.TAG);
+ }
+
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsModule.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsModule.java
new file mode 100644
index 000000000..ed63c309a
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsModule.java
@@ -0,0 +1,19 @@
+package org.briarproject.briar.android.settings;
+
+import org.briarproject.briar.android.viewmodel.ViewModelKey;
+
+import androidx.lifecycle.ViewModel;
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.IntoMap;
+
+@Module
+public abstract class SettingsModule {
+
+ @Binds
+ @IntoMap
+ @ViewModelKey(SettingsViewModel.class)
+ abstract ViewModel bindSettingsViewModel(
+ SettingsViewModel settingsViewModel);
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsViewModel.java
new file mode 100644
index 000000000..c6821b92a
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsViewModel.java
@@ -0,0 +1,134 @@
+package org.briarproject.briar.android.settings;
+
+import android.app.Application;
+import android.content.ContentResolver;
+import android.net.Uri;
+
+import org.briarproject.bramble.api.db.DatabaseExecutor;
+import org.briarproject.bramble.api.db.DbException;
+import org.briarproject.bramble.api.identity.IdentityManager;
+import org.briarproject.bramble.api.identity.LocalAuthor;
+import org.briarproject.bramble.api.lifecycle.IoExecutor;
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.briar.android.attachment.UnsupportedMimeTypeException;
+import org.briarproject.briar.android.attachment.media.ImageCompressor;
+import org.briarproject.briar.android.viewmodel.LiveEvent;
+import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
+import org.briarproject.briar.api.avatar.AvatarManager;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.Executor;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+
+import static java.util.Arrays.asList;
+import static java.util.logging.Level.WARNING;
+import static java.util.logging.Logger.getLogger;
+import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes;
+import static org.briarproject.bramble.util.LogUtils.logException;
+
+@NotNullByDefault
+class SettingsViewModel extends AndroidViewModel {
+
+ private final static Logger LOG =
+ getLogger(SettingsViewModel.class.getName());
+
+ private final IdentityManager identityManager;
+ private final AvatarManager avatarManager;
+ private final AuthorManager authorManager;
+ private final ImageCompressor imageCompressor;
+ @IoExecutor
+ private final Executor ioExecutor;
+ @DatabaseExecutor
+ private final Executor dbExecutor;
+
+ private final MutableLiveData ownIdentityInfo =
+ new MutableLiveData<>();
+
+ private final MutableLiveEvent setAvatarFailed =
+ new MutableLiveEvent<>();
+
+ @Inject
+ SettingsViewModel(Application application,
+ IdentityManager identityManager,
+ AvatarManager avatarManager,
+ AuthorManager authorManager,
+ ImageCompressor imageCompressor,
+ @IoExecutor Executor ioExecutor,
+ @DatabaseExecutor Executor dbExecutor) {
+ super(application);
+ this.identityManager = identityManager;
+ this.imageCompressor = imageCompressor;
+ this.avatarManager = avatarManager;
+ this.authorManager = authorManager;
+ this.ioExecutor = ioExecutor;
+ this.dbExecutor = dbExecutor;
+
+ loadOwnIdentityInfo();
+ }
+
+ LiveData getOwnIdentityInfo() {
+ return ownIdentityInfo;
+ }
+
+ public LiveEvent getSetAvatarFailed() {
+ return setAvatarFailed;
+ }
+
+ private void loadOwnIdentityInfo() {
+ dbExecutor.execute(() -> {
+ try {
+ LocalAuthor localAuthor = identityManager.getLocalAuthor();
+ AuthorInfo authorInfo = authorManager.getMyAuthorInfo();
+ ownIdentityInfo.postValue(
+ new OwnIdentityInfo(localAuthor, authorInfo));
+ } catch (DbException e) {
+ logException(LOG, WARNING, e);
+ }
+ });
+ }
+
+ void setAvatar(Uri uri) {
+ ioExecutor.execute(() -> {
+ try {
+ trySetAvatar(uri);
+ } catch (IOException e) {
+ logException(LOG, WARNING, e);
+ setAvatarFailed.postEvent(true);
+ }
+ });
+ }
+
+ private void trySetAvatar(Uri uri) throws IOException {
+ ContentResolver contentResolver =
+ getApplication().getContentResolver();
+ String contentType = contentResolver.getType(uri);
+ if (contentType == null) throw new IOException("null content type");
+ if (!asList(getSupportedImageContentTypes()).contains(contentType)) {
+ throw new UnsupportedMimeTypeException(contentType, uri);
+ }
+ InputStream is = contentResolver.openInputStream(uri);
+ if (is == null) throw new IOException(
+ "ContentResolver returned null when opening InputStream");
+ InputStream compressed = imageCompressor.compressImage(is, contentType);
+
+ dbExecutor.execute(() -> {
+ try {
+ avatarManager.addAvatar(ImageCompressor.MIME_TYPE, compressed);
+ loadOwnIdentityInfo();
+ } catch (IOException | DbException e) {
+ logException(LOG, WARNING, e);
+ setAvatarFailed.postEvent(true);
+ }
+ });
+ }
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/BaseMessageFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/BaseMessageFragment.java
index 1d33fd83e..ed1750c27 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/BaseMessageFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/BaseMessageFragment.java
@@ -15,7 +15,7 @@ import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.view.LargeTextInputView;
import org.briarproject.briar.android.view.TextSendController;
import org.briarproject.briar.android.view.TextSendController.SendListener;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import java.util.List;
@@ -62,6 +62,7 @@ public abstract class BaseMessageFragment extends BaseFragment
@StringRes
protected abstract int getButtonText();
+
@StringRes
protected abstract int getHintText();
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareBlogControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareBlogControllerImpl.java
index 26af4b3ca..abff0f41d 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareBlogControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareBlogControllerImpl.java
@@ -15,6 +15,7 @@ import org.briarproject.briar.android.contactselection.ContactSelectorController
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
import org.briarproject.briar.api.blog.BlogSharingManager;
import org.briarproject.briar.api.conversation.ConversationManager;
+import org.briarproject.briar.api.identity.AuthorManager;
import java.util.Collection;
import java.util.concurrent.Executor;
@@ -42,9 +43,10 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
@Inject
ShareBlogControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, ContactManager contactManager,
+ AuthorManager authorManager,
ConversationManager conversationManager,
BlogSharingManager blogSharingManager, Clock clock) {
- super(dbExecutor, lifecycleManager, contactManager);
+ super(dbExecutor, lifecycleManager, contactManager, authorManager);
this.conversationManager = conversationManager;
this.blogSharingManager = blogSharingManager;
this.clock = clock;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareForumControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareForumControllerImpl.java
index 9bbc6d3e7..1ab210d2e 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareForumControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/ShareForumControllerImpl.java
@@ -15,6 +15,7 @@ import org.briarproject.briar.android.contactselection.ContactSelectorController
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
import org.briarproject.briar.api.conversation.ConversationManager;
import org.briarproject.briar.api.forum.ForumSharingManager;
+import org.briarproject.briar.api.identity.AuthorManager;
import java.util.Collection;
import java.util.concurrent.Executor;
@@ -42,9 +43,10 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
@Inject
ShareForumControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, ContactManager contactManager,
+ AuthorManager authorManager,
ConversationManager conversationManager,
ForumSharingManager forumSharingManager, Clock clock) {
- super(dbExecutor, lifecycleManager, contactManager);
+ super(dbExecutor, lifecycleManager, contactManager, authorManager);
this.conversationManager = conversationManager;
this.forumSharingManager = forumSharingManager;
this.clock = clock;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingStatusActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingStatusActivity.java
index ab8e0e1ae..1bf66256e 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingStatusActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingStatusActivity.java
@@ -20,6 +20,8 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.contact.ContactItem;
import org.briarproject.briar.android.view.BriarRecyclerView;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.sharing.event.ContactLeftShareableEvent;
import java.util.ArrayList;
@@ -42,8 +44,12 @@ import static org.briarproject.bramble.util.LogUtils.logException;
abstract class SharingStatusActivity extends BriarActivity
implements EventListener {
+ // objects accessed from background thread need to be volatile
@Inject
- ConnectionRegistry connectionRegistry;
+ volatile AuthorManager authorManager;
+ @Inject
+ volatile ConnectionRegistry connectionRegistry;
+
@Inject
EventBus eventBus;
@@ -134,8 +140,9 @@ abstract class SharingStatusActivity extends BriarActivity
try {
List contactItems = new ArrayList<>();
for (Contact c : getSharedWith()) {
+ AuthorInfo authorInfo = authorManager.getAuthorInfo(c);
boolean online = connectionRegistry.isConnected(c.getId());
- ContactItem item = new ContactItem(c, online);
+ ContactItem item = new ContactItem(c, authorInfo, online);
contactItems.add(item);
}
displaySharedWith(contactItems);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/test/TestDataActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/test/TestDataActivity.java
index f9f98185b..10ce1daf1 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/test/TestDataActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/test/TestDataActivity.java
@@ -24,20 +24,9 @@ public class TestDataActivity extends BriarActivity {
@Inject
TestDataCreator testDataCreator;
- private TextView contactsTextView;
- private SeekBar contactsSeekBar;
-
- private TextView messagesTextView;
- private SeekBar messagesSeekBar;
-
- private TextView blogPostsTextView;
- private SeekBar blogPostsSeekBar;
-
- private TextView forumsTextView;
- private SeekBar forumsSeekBar;
-
- private TextView forumPostsTextView;
- private SeekBar forumPostsSeekBar;
+ private TextView contactsTextView, forumsTextView;
+ private SeekBar contactsSeekBar, messagesSeekBar, avatarsSeekBar,
+ blogPostsSeekBar, forumsSeekBar, forumPostsSeekBar;
@Override
public void onCreate(Bundle bundle) {
@@ -51,12 +40,15 @@ public class TestDataActivity extends BriarActivity {
setContentView(R.layout.activity_test_data);
contactsTextView = findViewById(R.id.textViewContactsSb);
- messagesTextView = findViewById(R.id.textViewMessagesSb);
- blogPostsTextView = findViewById(R.id.TextViewBlogPostsSb);
+ TextView messagesTextView = findViewById(R.id.textViewMessagesSb);
+ TextView avatarsTextView = findViewById(R.id.textViewAvatarsSb);
+ TextView blogPostsTextView = findViewById(R.id.TextViewBlogPostsSb);
forumsTextView = findViewById(R.id.TextViewForumsSb);
- forumPostsTextView = findViewById(R.id.TextViewForumMessagesSb);
+ TextView forumPostsTextView =
+ findViewById(R.id.TextViewForumMessagesSb);
contactsSeekBar = findViewById(R.id.seekBarContacts);
messagesSeekBar = findViewById(R.id.seekBarMessages);
+ avatarsSeekBar = findViewById(R.id.seekBarAvatars);
blogPostsSeekBar = findViewById(R.id.seekBarBlogPosts);
forumsSeekBar = findViewById(R.id.seekBarForums);
forumPostsSeekBar = findViewById(R.id.seekBarForumMessages);
@@ -78,40 +70,12 @@ public class TestDataActivity extends BriarActivity {
}
});
- messagesSeekBar
- .setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar,
- int progress, boolean fromUser) {
- messagesTextView.setText(String.valueOf(progress));
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
-
- blogPostsSeekBar
- .setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar,
- int progress, boolean fromUser) {
- blogPostsTextView.setText(String.valueOf(progress));
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
-
+ messagesSeekBar.setOnSeekBarChangeListener(
+ new OnSeekBarChangeUpdateProgress(messagesTextView));
+ avatarsSeekBar.setOnSeekBarChangeListener(
+ new OnSeekBarChangeUpdateProgress(avatarsTextView));
+ blogPostsSeekBar.setOnSeekBarChangeListener(
+ new OnSeekBarChangeUpdateProgress(blogPostsTextView));
forumsSeekBar
.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
@@ -129,23 +93,8 @@ public class TestDataActivity extends BriarActivity {
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
-
- forumPostsSeekBar
- .setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar,
- int progress, boolean fromUser) {
- forumPostsTextView.setText(String.valueOf(progress));
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- }
- });
+ forumPostsSeekBar.setOnSeekBarChangeListener(
+ new OnSeekBarChangeUpdateProgress(forumPostsTextView));
findViewById(R.id.buttonCreateTestData).setOnClickListener(
v -> createTestData());
@@ -153,8 +102,9 @@ public class TestDataActivity extends BriarActivity {
private void createTestData() {
testDataCreator.createTestData(contactsSeekBar.getProgress() + 1,
- messagesSeekBar.getProgress(), blogPostsSeekBar.getProgress(),
- forumsSeekBar.getProgress(), forumPostsSeekBar.getProgress());
+ messagesSeekBar.getProgress(), avatarsSeekBar.getProgress(),
+ blogPostsSeekBar.getProgress(), forumsSeekBar.getProgress(),
+ forumPostsSeekBar.getProgress());
Intent intent = new Intent(this, ENTRY_ACTIVITY);
intent.addFlags(FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
@@ -174,4 +124,28 @@ public class TestDataActivity extends BriarActivity {
}
return false;
}
+
+ private static class OnSeekBarChangeUpdateProgress
+ implements OnSeekBarChangeListener {
+ private final TextView textView;
+
+ private OnSeekBarChangeUpdateProgress(TextView textView) {
+ this.textView = textView;
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress,
+ boolean fromUser) {
+ textView.setText(String.valueOf(progress));
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ }
+
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadItem.java b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadItem.java
index 4a9a51640..0d3d69205 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadItem.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadItem.java
@@ -1,7 +1,7 @@
package org.briarproject.briar.android.threaded;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.MessageTree.MessageNode;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListActivity.java
index 22138f19e..828be5091 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListActivity.java
@@ -27,8 +27,8 @@ import org.briarproject.briar.android.view.TextInputView;
import org.briarproject.briar.android.view.TextSendController;
import org.briarproject.briar.android.view.TextSendController.SendListener;
import org.briarproject.briar.android.view.UnreadMessageButton;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import org.briarproject.briar.api.client.NamedGroup;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
import java.util.Collection;
import java.util.List;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
index dda2fdb60..404b7e621 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java
@@ -57,7 +57,12 @@ import androidx.lifecycle.Observer;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Context.POWER_SERVICE;
+import static android.content.Intent.ACTION_GET_CONTENT;
+import static android.content.Intent.ACTION_OPEN_DOCUMENT;
import static android.content.Intent.CATEGORY_DEFAULT;
+import static android.content.Intent.CATEGORY_OPENABLE;
+import static android.content.Intent.EXTRA_ALLOW_MULTIPLE;
+import static android.content.Intent.EXTRA_MIME_TYPES;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.Build.MANUFACTURER;
import static android.os.Build.VERSION.SDK_INT;
@@ -89,6 +94,7 @@ import static androidx.core.graphics.drawable.DrawableCompat.setTint;
import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_RTL;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.DAYS;
+import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes;
import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_START_TIME;
@@ -250,6 +256,18 @@ public class UiUtils {
};
}
+ public static Intent createSelectImageIntent(boolean allowMultiple) {
+ Intent intent = new Intent(SDK_INT >= 19 ?
+ ACTION_OPEN_DOCUMENT : ACTION_GET_CONTENT);
+ intent.setType("image/*");
+ intent.addCategory(CATEGORY_OPENABLE);
+ if (SDK_INT >= 19)
+ intent.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
+ if (allowMultiple && SDK_INT >= 18)
+ intent.putExtra(EXTRA_ALLOW_MULTIPLE, true);
+ return intent;
+ }
+
public static void showOnboardingDialog(Context ctx, String text) {
new AlertDialog.Builder(ctx, R.style.OnboardingDialogTheme)
.setMessage(text)
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java
index 61de039ee..6cd18ed85 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java
@@ -8,10 +8,15 @@ import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.TextView;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
+import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.briar.R;
+import org.briarproject.briar.android.contact.ContactItem;
+import org.briarproject.briar.android.conversation.glide.GlideApp;
import org.briarproject.briar.android.util.UiUtils;
+import org.briarproject.briar.api.identity.AuthorInfo;
import javax.annotation.Nullable;
@@ -24,10 +29,10 @@ import im.delight.android.identicons.IdenticonDrawable;
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
import static android.graphics.Typeface.BOLD;
import static android.util.TypedValue.COMPLEX_UNIT_PX;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.NONE;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
import static org.briarproject.briar.android.util.UiUtils.resolveAttribute;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.NONE;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.OURSELVES;
@UiThread
public class AuthorView extends ConstraintLayout {
@@ -74,8 +79,7 @@ public class AuthorView extends ConstraintLayout {
public void setAuthor(Author author, AuthorInfo authorInfo) {
authorName
.setText(getContactDisplayName(author, authorInfo.getAlias()));
- IdenticonDrawable d = new IdenticonDrawable(author.getId().getBytes());
- avatar.setImageDrawable(d);
+ setAvatar(avatar, author.getId(), authorInfo);
if (authorInfo.getStatus() != NONE) {
trustIndicator.setTrustLevel(authorInfo.getStatus());
@@ -94,6 +98,27 @@ public class AuthorView extends ConstraintLayout {
requestLayout();
}
+ public static void setAvatar(ImageView v, AuthorId id, AuthorInfo info) {
+ IdenticonDrawable identicon = new IdenticonDrawable(id.getBytes());
+ if (info.getAvatarHeader() == null) {
+ GlideApp.with(v)
+ .clear(v);
+ v.setImageDrawable(identicon);
+ } else {
+ GlideApp.with(v)
+ .load(info.getAvatarHeader())
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .error(identicon)
+ .into(v)
+ .waitForLayout();
+ }
+ }
+
+ public static void setAvatar(ImageView v, ContactItem contactItem) {
+ AuthorId authorId = contactItem.getContact().getAuthor().getId();
+ setAvatar(v, authorId, contactItem.getAuthorInfo());
+ }
+
public void setDate(long date) {
this.date.setText(UiUtils.formatDate(getContext(), date));
@@ -117,10 +142,10 @@ public class AuthorView extends ConstraintLayout {
/**
* Styles this view for a different persona.
- *
+ *
* Attention: RSS_FEED and RSS_FEED_REBLOGGED change the avatar
- * and override the one set by
- * {@link AuthorView#setAuthor(Author, AuthorInfo)}.
+ * and override the one set by
+ * {@link AuthorView#setAuthor(Author, AuthorInfo)}.
*/
public void setPersona(int persona) {
switch (persona) {
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/ImagePreviewViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/view/ImagePreviewViewHolder.java
index f78c4f9fc..5d75f85c3 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/view/ImagePreviewViewHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/view/ImagePreviewViewHolder.java
@@ -46,7 +46,7 @@ class ImagePreviewViewHolder extends ViewHolder {
.clear(imageView);
} else {
GlideApp.with(imageView)
- .load(item.getItem())
+ .load(item.getItem().getHeader())
.diskCacheStrategy(NONE)
.error(ERROR_RES)
.downsample(FIT_CENTER)
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java b/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java
index 806fd9e4c..07d7f08a6 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/view/TextAttachmentController.java
@@ -14,6 +14,7 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.attachment.AttachmentItemResult;
import org.briarproject.briar.android.attachment.AttachmentManager;
import org.briarproject.briar.android.attachment.AttachmentResult;
+import org.briarproject.briar.android.util.UiUtils;
import org.briarproject.briar.android.view.ImagePreview.ImagePreviewListener;
import java.util.ArrayList;
@@ -29,18 +30,12 @@ import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt;
-import static android.content.Intent.ACTION_GET_CONTENT;
-import static android.content.Intent.ACTION_OPEN_DOCUMENT;
-import static android.content.Intent.CATEGORY_OPENABLE;
-import static android.content.Intent.EXTRA_ALLOW_MULTIPLE;
-import static android.content.Intent.EXTRA_MIME_TYPES;
import static android.os.Build.VERSION.SDK_INT;
import static android.view.View.GONE;
import static android.widget.Toast.LENGTH_LONG;
import static androidx.core.content.ContextCompat.getColor;
import static androidx.customview.view.AbsSavedState.EMPTY_STATE;
import static androidx.lifecycle.Lifecycle.State.DESTROYED;
-import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes;
import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE;
@@ -120,26 +115,15 @@ public class TextAttachmentController extends TextSendController
builder.show();
return;
}
- Intent intent = getAttachFileIntent();
+ Intent intent = UiUtils.createSelectImageIntent(true);
if (attachmentListener.getLifecycle().getCurrentState() != DESTROYED) {
attachmentListener.onAttachImage(intent);
}
}
- private Intent getAttachFileIntent() {
- Intent intent = new Intent(SDK_INT >= 19 ?
- ACTION_OPEN_DOCUMENT : ACTION_GET_CONTENT);
- intent.setType("image/*");
- intent.addCategory(CATEGORY_OPENABLE);
- if (SDK_INT >= 19)
- intent.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
- if (SDK_INT >= 18) intent.putExtra(EXTRA_ALLOW_MULTIPLE, true);
- return intent;
- }
-
/**
- * This is called with the result Intent
- * returned by the Activity started with {@link #getAttachFileIntent()}.
+ * This is called with the result Intent returned by the Activity started
+ * with {@link UiUtils#createSelectImageIntent(boolean)}.
*
* This method must be called at most once per call to
* {@link AttachmentListener#onAttachImage(Intent)}.
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/TextSendController.java b/briar-android/src/main/java/org/briarproject/briar/android/view/TextSendController.java
index ae2ac2313..e070a3dd4 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/view/TextSendController.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/view/TextSendController.java
@@ -8,7 +8,7 @@ import com.google.android.material.snackbar.Snackbar;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.view.EmojiTextInputView.TextInputListener;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import java.util.List;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/TrustIndicatorView.java b/briar-android/src/main/java/org/briarproject/briar/android/view/TrustIndicatorView.java
index fb045db00..ec1fc98ac 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/view/TrustIndicatorView.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/view/TrustIndicatorView.java
@@ -3,7 +3,7 @@ package org.briarproject.briar.android.view;
import android.content.Context;
import android.util.AttributeSet;
-import org.briarproject.bramble.api.identity.AuthorInfo.Status;
+import org.briarproject.briar.api.identity.AuthorInfo.Status;
import org.briarproject.briar.R;
import androidx.annotation.UiThread;
diff --git a/briar-android/src/main/res/layout/activity_settings.xml b/briar-android/src/main/res/layout/activity_settings.xml
index f5c555431..88bf95130 100644
--- a/briar-android/src/main/res/layout/activity_settings.xml
+++ b/briar-android/src/main/res/layout/activity_settings.xml
@@ -1,11 +1,78 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+ android:layout_height="match_parent"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior" />
+
+
\ No newline at end of file
diff --git a/briar-android/src/main/res/layout/activity_test_data.xml b/briar-android/src/main/res/layout/activity_test_data.xml
index bd0c13dc6..0c8733c65 100644
--- a/briar-android/src/main/res/layout/activity_test_data.xml
+++ b/briar-android/src/main/res/layout/activity_test_data.xml
@@ -71,6 +71,36 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewMessages" />
+
+
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@+id/seekBarAvatars" />
+ android:text="@string/change" />
diff --git a/briar-android/src/main/res/layout/fragment_confirm_avatar_dialog.xml b/briar-android/src/main/res/layout/fragment_confirm_avatar_dialog.xml
new file mode 100644
index 000000000..db7c6a5cc
--- /dev/null
+++ b/briar-android/src/main/res/layout/fragment_confirm_avatar_dialog.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/briar-android/src/main/res/layout/introduction_message.xml b/briar-android/src/main/res/layout/introduction_message.xml
index 99a2e980a..518fa2f91 100644
--- a/briar-android/src/main/res/layout/introduction_message.xml
+++ b/briar-android/src/main/res/layout/introduction_message.xml
@@ -14,7 +14,7 @@
android:id="@+id/avatarContact1"
style="@style/BriarAvatar"
android:layout_width="42dp"
- android:layout_height="wrap_content"
+ android:layout_height="42dp"
android:layout_margin="@dimen/margin_large"
app:layout_constraintEnd_toStartOf="@+id/introductionIcon"
app:layout_constraintStart_toStartOf="parent"
@@ -55,7 +55,7 @@
android:id="@+id/avatarContact2"
style="@style/BriarAvatar"
android:layout_width="42dp"
- android:layout_height="wrap_content"
+ android:layout_height="42dp"
android:layout_margin="@dimen/margin_large"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/introductionIcon"
diff --git a/briar-android/src/main/res/values-ar/strings.xml b/briar-android/src/main/res/values-ar/strings.xml
index 726ea252f..3fa4adc06 100644
--- a/briar-android/src/main/res/values-ar/strings.xml
+++ b/briar-android/src/main/res/values-ar/strings.xml
@@ -171,7 +171,7 @@
نوع ملف الصورة غير مدعوم : %s
تعديل إسم جهة الاتصال
اسم جهة الاتصال
- تعديل
+ تعديل
حذف جميع الرّسائل
تأكيد حذف الرّسالة
هل أنتم متأكّدون من حذف جميع الرّسائل؟
diff --git a/briar-android/src/main/res/values-az/strings.xml b/briar-android/src/main/res/values-az/strings.xml
index 5e5423603..8af48fc39 100644
--- a/briar-android/src/main/res/values-az/strings.xml
+++ b/briar-android/src/main/res/values-az/strings.xml
@@ -125,7 +125,7 @@
Şəkil formatı dəstəklənmir: %s
Kontakt adı dəyişdirin
Əlaqə adı
- Dəyiş
+ Dəyiş
Bütün mesajları sil
Silməyi təsdiq et
Bütün mesajları silmək istədiyinizə əminsinizmi?
diff --git a/briar-android/src/main/res/values-bs/strings.xml b/briar-android/src/main/res/values-bs/strings.xml
index 4690d4968..e9b276289 100644
--- a/briar-android/src/main/res/values-bs/strings.xml
+++ b/briar-android/src/main/res/values-bs/strings.xml
@@ -102,7 +102,7 @@
Fromat fotografije nije podržan: %s
Promijeni ime kontakta
Ime kontakta
- Promijeni
+ Promijeni
Izbrišite kontakt
Potvrdite brisanje kontakta
Jeste li sigurni da želite da uklonite ovaj kontakt i sve poruke koje ste razmijenili?
diff --git a/briar-android/src/main/res/values-ca/strings.xml b/briar-android/src/main/res/values-ca/strings.xml
index 961780760..934269c30 100644
--- a/briar-android/src/main/res/values-ca/strings.xml
+++ b/briar-android/src/main/res/values-ca/strings.xml
@@ -151,7 +151,7 @@
El format de la imatge no s\'admet: %s
Canvia el nom del contacte
Nom del contacte
- Canvia
+ Canvia
Esborra tots els missatges
Confirmeu l\'esborrat dels missatges
Segur que voleu esborrar tots els missatges?
diff --git a/briar-android/src/main/res/values-de/strings.xml b/briar-android/src/main/res/values-de/strings.xml
index 81ecbee8a..eaf2a37f3 100644
--- a/briar-android/src/main/res/values-de/strings.xml
+++ b/briar-android/src/main/res/values-de/strings.xml
@@ -151,7 +151,7 @@
Nicht unterstütztes Bildformat: %s
Kontaktnamen ändern
Name des Kontakts
- Ändern
+ Ändern
Alle Nachrichten löschen
Löschen der Nachrichten bestätigen
Bist du sicher, dass du alle Nachrichten löschen willst?
diff --git a/briar-android/src/main/res/values-es/strings.xml b/briar-android/src/main/res/values-es/strings.xml
index 53605cf31..e50129bd1 100644
--- a/briar-android/src/main/res/values-es/strings.xml
+++ b/briar-android/src/main/res/values-es/strings.xml
@@ -151,7 +151,7 @@
Formato de imagen no admitido: %s
Cambiar nombre del contacto
Nombre del contacto
- Cambiar
+ Cambiar
Eliminar todos los mensajes
Confirmar la eliminación del mensaje
¿Estás seguro de que deseas eliminar todos los mensajes?
diff --git a/briar-android/src/main/res/values-eu/strings.xml b/briar-android/src/main/res/values-eu/strings.xml
index c3c5afeb3..435b132f7 100644
--- a/briar-android/src/main/res/values-eu/strings.xml
+++ b/briar-android/src/main/res/values-eu/strings.xml
@@ -127,7 +127,7 @@
Onartu gabeko irudi formatua: %s
Aldatu kontaktuaren izena
Kontaktuaren izena
- Aldatu
+ Aldatu
Ezabatu mezu guztiak
Baieztatu mezuak ezabatzea
Ziur mezu guztiak ezabatu nahi dituzula?
diff --git a/briar-android/src/main/res/values-fa/strings.xml b/briar-android/src/main/res/values-fa/strings.xml
index 30ad82a31..c8b8635ab 100644
--- a/briar-android/src/main/res/values-fa/strings.xml
+++ b/briar-android/src/main/res/values-fa/strings.xml
@@ -154,7 +154,7 @@
فرمت تصویر پشتیبانی نمیشود: %s
تغییر نام مخاطب
نام مخاطب
- تغییر
+ تغییر
حذف تمام پیام ها
تایید حذف پیام
آیا از حذف تمام پیام ها اطمینان دارید؟
diff --git a/briar-android/src/main/res/values-fi/strings.xml b/briar-android/src/main/res/values-fi/strings.xml
index 4d987b3e1..0a5b356aa 100644
--- a/briar-android/src/main/res/values-fi/strings.xml
+++ b/briar-android/src/main/res/values-fi/strings.xml
@@ -113,7 +113,7 @@
Ei viestejä
Kirjoita viesti
Yhteystiedon nimi
- Muuta
+ Muuta
Poista yhteystieto
Vahvista yhteystiedon poistaminen
Oletko varma, että haluat poistaa tämän yhteyshenkilön ja kaikki hänen kanssa vaihdetut viestit?
diff --git a/briar-android/src/main/res/values-fr/strings.xml b/briar-android/src/main/res/values-fr/strings.xml
index daeea0d0b..3dcd24442 100644
--- a/briar-android/src/main/res/values-fr/strings.xml
+++ b/briar-android/src/main/res/values-fr/strings.xml
@@ -151,7 +151,7 @@
Format d’image non pris en charge : %s
Changer le nom du contact
Nom du contact
- Modifier
+ Modifier
Supprimer tous les messages
Confirmer la suppression des messages
Voulez-vous vraiment supprimer tous les messages ?
diff --git a/briar-android/src/main/res/values-gl/strings.xml b/briar-android/src/main/res/values-gl/strings.xml
index aa63f2d58..9d0157883 100644
--- a/briar-android/src/main/res/values-gl/strings.xml
+++ b/briar-android/src/main/res/values-gl/strings.xml
@@ -151,7 +151,7 @@
Formato non admitido: %s
Cambiar o nome do contacto
Nome do contacto
- Cambiar
+ Cambiar
Borrar todas as mensaxes
Confirmar borrado de mensaxes
Seguro que queres borrar todas as mensaxes?
diff --git a/briar-android/src/main/res/values-he/strings.xml b/briar-android/src/main/res/values-he/strings.xml
index 238a1d5e5..1d5baabef 100644
--- a/briar-android/src/main/res/values-he/strings.xml
+++ b/briar-android/src/main/res/values-he/strings.xml
@@ -161,7 +161,7 @@
תסדיר תמונה בלתי נתמך: %s
שַׁנֵּה שם איש קשר
שם איש הקשר
- שַׁנֵּה
+ שַׁנֵּה
מחק את כל ההודעות
אשר מחיקת הודעה
האם אתה בטוח שאתה רוצה למחוק את כל ההודעות?
diff --git a/briar-android/src/main/res/values-hi/strings.xml b/briar-android/src/main/res/values-hi/strings.xml
index 45e0563f7..ccbbc9c53 100644
--- a/briar-android/src/main/res/values-hi/strings.xml
+++ b/briar-android/src/main/res/values-hi/strings.xml
@@ -122,7 +122,7 @@
छवि प्रारूप असमर्थित: %s
संपर्क नाम बदलें
संपर्क नाम
- परिवर्तन
+ परिवर्तन
संपर्क मिटा दें
संपर्क हटाने की पुष्टि करें
क्या आप निश्चित हैं कि आप इस संपर्क और सभी संदेशों को इस संपर्क से निकाला जाना चाहते हैं?
diff --git a/briar-android/src/main/res/values-hu/strings.xml b/briar-android/src/main/res/values-hu/strings.xml
index e5e339d45..5b2256a21 100644
--- a/briar-android/src/main/res/values-hu/strings.xml
+++ b/briar-android/src/main/res/values-hu/strings.xml
@@ -151,7 +151,7 @@
Képformátum nem támogatott: %s
Kapcsolat nevének megváltoztatása
Kapcsolat neve
- Módosítás
+ Módosítás
Minden üzenet törlése
Üzenet törlés megerősítése
Biztos, hogy szeretné törölni összes üzenetét?
diff --git a/briar-android/src/main/res/values-is/strings.xml b/briar-android/src/main/res/values-is/strings.xml
index 1cfb27dd8..48b23dcbb 100644
--- a/briar-android/src/main/res/values-is/strings.xml
+++ b/briar-android/src/main/res/values-is/strings.xml
@@ -151,7 +151,7 @@
Myndskráarsnið er ekki stutt: %s
Breyta nafni tengiliðar
Nafn tengiliðar
- Breyta
+ Breyta
Eyða öllum skilaboðum
Staðfesta eyðingu skilaboða
Ertu viss um að þú viljir eyða öllum skilaboðum?
diff --git a/briar-android/src/main/res/values-it/strings.xml b/briar-android/src/main/res/values-it/strings.xml
index 18cc8d1f3..7259a4e40 100644
--- a/briar-android/src/main/res/values-it/strings.xml
+++ b/briar-android/src/main/res/values-it/strings.xml
@@ -151,7 +151,7 @@
Formato immagine non supportato: %s
Cambia il nome del contatto
Nome contatto
- Cambia
+ Cambia
Elimina tutti i messaggi
Conferma l\'eliminazione dei messaggi
Sei sicuro di voler eliminare tutti i messaggi?
diff --git a/briar-android/src/main/res/values-ja/strings.xml b/briar-android/src/main/res/values-ja/strings.xml
index 5bfc5276c..f7e2d4395 100644
--- a/briar-android/src/main/res/values-ja/strings.xml
+++ b/briar-android/src/main/res/values-ja/strings.xml
@@ -117,7 +117,7 @@
サポートされていない画像形式:%s
連絡先を変更
連絡先名
- 変更
+ 変更
この連絡先を削除
連絡先の削除時に確認
この連絡先と、この連絡先とのすべてのメッセージを削除してもよろしいですか?
diff --git a/briar-android/src/main/res/values-ko/strings.xml b/briar-android/src/main/res/values-ko/strings.xml
index 531d197ca..e80d81619 100644
--- a/briar-android/src/main/res/values-ko/strings.xml
+++ b/briar-android/src/main/res/values-ko/strings.xml
@@ -146,7 +146,7 @@
%s는 지원되지 않는 이미지 형식입니다.
연락처 이름 바꾸기
연락처 이름
- 바꾸기
+ 바꾸기
모든 메시지 지우기
메시지 삭제 확인
정말로 모든 메시지를 지우려고 하시나요?
diff --git a/briar-android/src/main/res/values-lt/strings.xml b/briar-android/src/main/res/values-lt/strings.xml
index 5d1df63a7..ed1a177b1 100644
--- a/briar-android/src/main/res/values-lt/strings.xml
+++ b/briar-android/src/main/res/values-lt/strings.xml
@@ -161,7 +161,7 @@
Nepalaikomas paveikslo formatas: %s
Pakeisti adresato vardą
Adresato vardas
- Pakeisti
+ Pakeisti
Ištrinti visas žinutes
Patvirtinkite žinutės ištrynimą
Ar tikrai norite ištrinti visas žinutes?
diff --git a/briar-android/src/main/res/values-mk/strings.xml b/briar-android/src/main/res/values-mk/strings.xml
index 1ede3c65c..b28825613 100644
--- a/briar-android/src/main/res/values-mk/strings.xml
+++ b/briar-android/src/main/res/values-mk/strings.xml
@@ -151,7 +151,7 @@
Форматот на сликата не е поддржан: %s
Промена на име на контакт
Име на контакт
- Промени
+ Промени
Избриши ги сите пораки
Потврди бришење на порака
Дали сте сигурни дека сакате да ги избришете сите пораки?
diff --git a/briar-android/src/main/res/values-nl/strings.xml b/briar-android/src/main/res/values-nl/strings.xml
index 40fc6c8a3..f042aeb6c 100644
--- a/briar-android/src/main/res/values-nl/strings.xml
+++ b/briar-android/src/main/res/values-nl/strings.xml
@@ -151,7 +151,7 @@
Bestandsformaat wordt niet ondersteund: %s
Verander naam van contact
Contactnaam
- Wijzigen
+ Wijzigen
Verwijder alle berichten
Bevestig verwijderen berichten
Weet je zeker dat je alle berichten wil verwijderen?
diff --git a/briar-android/src/main/res/values-oc/strings.xml b/briar-android/src/main/res/values-oc/strings.xml
index cd36c4356..c35bb4715 100644
--- a/briar-android/src/main/res/values-oc/strings.xml
+++ b/briar-android/src/main/res/values-oc/strings.xml
@@ -128,7 +128,7 @@ Volètz suprimir vòstre compte e ne crear un nòu ?\n
Format d’imatge pas compatible : %s
Cambiar lo nom del contacte
Nom del contacte
- Cambiar
+ Cambiar
Suprimir totes los messatges
Confirmatz la supression dels messatges
Volètz vertadièrament suprimir totes los messatges ?
diff --git a/briar-android/src/main/res/values-pl/strings.xml b/briar-android/src/main/res/values-pl/strings.xml
index 91ca853e7..2102fb0bd 100644
--- a/briar-android/src/main/res/values-pl/strings.xml
+++ b/briar-android/src/main/res/values-pl/strings.xml
@@ -132,7 +132,7 @@
Format obrazu jest nieobsługiwany: %s
Zmień nazwę kontaktu
Nazwa kontaktu
- Zmień
+ Zmień
Nie mogłem usunąć wszystkich wiadomości
Usuń kontakt
Potwierdź usunięcie kontaktu
diff --git a/briar-android/src/main/res/values-pt-rBR/strings.xml b/briar-android/src/main/res/values-pt-rBR/strings.xml
index a96158099..d77be53f7 100644
--- a/briar-android/src/main/res/values-pt-rBR/strings.xml
+++ b/briar-android/src/main/res/values-pt-rBR/strings.xml
@@ -151,7 +151,7 @@
Formato da imagem não suportado: %s
Alterar nome do contato
Nome de contato
- Trocar
+ Trocar
Deletar todas as mensagens
Confirmar exclusão de mensagem
Você tem certeza que deseja excluir todas as mensagens?
diff --git a/briar-android/src/main/res/values-ro/strings.xml b/briar-android/src/main/res/values-ro/strings.xml
index b370420aa..eb68ea923 100644
--- a/briar-android/src/main/res/values-ro/strings.xml
+++ b/briar-android/src/main/res/values-ro/strings.xml
@@ -156,7 +156,7 @@
Format de imagine incompatibil: %s
Schimbă nume contact
Nume contact
- Modifică
+ Modifică
Șterge toate mesajele
Confirmare ștergere mesaj
Sigur doriți să ștergeți toate mesajele?
diff --git a/briar-android/src/main/res/values-ru/strings.xml b/briar-android/src/main/res/values-ru/strings.xml
index 8ef090038..52f0ca4a5 100644
--- a/briar-android/src/main/res/values-ru/strings.xml
+++ b/briar-android/src/main/res/values-ru/strings.xml
@@ -163,7 +163,7 @@
Формат изображения не поддерживается: %s
Изменить имя контакта
Имя контакта
- Изменить
+ Изменить
Удалить все сообщения
Подтверждение удаления сообщений
Вы уверены, что хотите удалить все сообщения?
diff --git a/briar-android/src/main/res/values-sq/strings.xml b/briar-android/src/main/res/values-sq/strings.xml
index 35ab819a1..cc843a727 100644
--- a/briar-android/src/main/res/values-sq/strings.xml
+++ b/briar-android/src/main/res/values-sq/strings.xml
@@ -151,7 +151,7 @@
Format i pambuluar figure: %s
Ndryshoni emër kontakti
Emër kontakti
- Ndryshoje
+ Ndryshoje
Fshiji krejt mesazhet
Ripohoni Fshirje Mesazhesh
Jeni i sigurt se doni të fshihen krejt mesazhet?
diff --git a/briar-android/src/main/res/values-sr/strings.xml b/briar-android/src/main/res/values-sr/strings.xml
index bcc653172..b4c9ade7c 100644
--- a/briar-android/src/main/res/values-sr/strings.xml
+++ b/briar-android/src/main/res/values-sr/strings.xml
@@ -123,7 +123,7 @@ Izaberite dugačku lozinku koju je teško pogoditi, kao četiri nasumične rije
Zakaci sliku
Promijeni ime kontakta
Ime kontakta
- Promijeni
+ Promijeni
Izbrišite kontakt
Potvrdite brisanje kontakta
Jeste li sigurni da želite da uklonite ovaj kontakt i sve poruke koje ste razmijenili?
diff --git a/briar-android/src/main/res/values-sv/strings.xml b/briar-android/src/main/res/values-sv/strings.xml
index e10385a8d..d5ebc4945 100644
--- a/briar-android/src/main/res/values-sv/strings.xml
+++ b/briar-android/src/main/res/values-sv/strings.xml
@@ -151,7 +151,7 @@
Bildformatet stöds ej: %s
Ändra namn på kontakt
Kontaktnamn
- Ändra
+ Ändra
Ta bort alla meddelanden
Bekräfta meddelande borttagning
Är du säker på att du vill ta bort alla meddelanden?
diff --git a/briar-android/src/main/res/values-sw/strings.xml b/briar-android/src/main/res/values-sw/strings.xml
index 272d66b6d..6722f5ab0 100644
--- a/briar-android/src/main/res/values-sw/strings.xml
+++ b/briar-android/src/main/res/values-sw/strings.xml
@@ -143,7 +143,7 @@ Umepoteza nenosiri.
Mfumo wa taswira hauendani: %s
Badilisha jina lako la mawasiliano
Jina la mawasiliano
- Badili
+ Badili
Futa jumbe zote
Hakiki ufutaji wa ujumbe
Je, unauhakika wa kufuta jumbe zote?
diff --git a/briar-android/src/main/res/values-tr/strings.xml b/briar-android/src/main/res/values-tr/strings.xml
index fbb78480b..8d3a45848 100644
--- a/briar-android/src/main/res/values-tr/strings.xml
+++ b/briar-android/src/main/res/values-tr/strings.xml
@@ -151,7 +151,7 @@
Resim biçimi desteklenmiyor: %s
Kişi adını değiştir
Ad
- Değiştir
+ Değiştir
Tüm iletileri sil
İleti Silmeyi Onayla
Tüm iletileri silmek istediğinizden emin misiniz?
diff --git a/briar-android/src/main/res/values-uk/strings.xml b/briar-android/src/main/res/values-uk/strings.xml
index ac4d1638b..f2d3faa88 100644
--- a/briar-android/src/main/res/values-uk/strings.xml
+++ b/briar-android/src/main/res/values-uk/strings.xml
@@ -132,7 +132,7 @@
Формат зображення непідтримуваний: %s
Змінити ім\'я контакту
Назва контакту
- Змінити
+ Змінити
Не вдалося видалити всі повідомлення
Вилучити контакт
Підтвердити вилучення контакту
diff --git a/briar-android/src/main/res/values-zh-rCN/strings.xml b/briar-android/src/main/res/values-zh-rCN/strings.xml
index 9fefed7fc..75ee0f867 100644
--- a/briar-android/src/main/res/values-zh-rCN/strings.xml
+++ b/briar-android/src/main/res/values-zh-rCN/strings.xml
@@ -146,7 +146,7 @@
不支持的图片格式:%s
更改联系人姓名
联系人姓名
- 更改
+ 更改
删除全部消息
确认删除消息
您确定要删除所有消息吗?
diff --git a/briar-android/src/main/res/values-zh-rTW/strings.xml b/briar-android/src/main/res/values-zh-rTW/strings.xml
index 6c497eb95..b3ff9fee9 100644
--- a/briar-android/src/main/res/values-zh-rTW/strings.xml
+++ b/briar-android/src/main/res/values-zh-rTW/strings.xml
@@ -117,7 +117,7 @@
圖片格式不支援: %s
更改聯絡人姓名
聯絡人姓名
- 更改
+ 更改
刪除聯絡人
確認刪除聯絡人
確認要刪除該聯絡人和與其交流訊息嗎?
diff --git a/briar-android/src/main/res/values/dimens.xml b/briar-android/src/main/res/values/dimens.xml
index 0b8e5f2c8..cc74b7be3 100644
--- a/briar-android/src/main/res/values/dimens.xml
+++ b/briar-android/src/main/res/values/dimens.xml
@@ -30,7 +30,7 @@
40dp
40dp
48dp
- 2dp
+ 1dp
30sp
48dp
diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml
index 9d7e56b68..480335017 100644
--- a/briar-android/src/main/res/values/strings.xml
+++ b/briar-android/src/main/res/values/strings.xml
@@ -141,6 +141,7 @@
Send
Allow
Open
+ Change
No data
…
The entered text is too long
@@ -164,7 +165,6 @@
Image format unsupported: %s
Change contact name
Contact name
- Change
Delete all messages
Confirm Message Deletion
Are you sure that you want to delete all messages?
@@ -453,6 +453,12 @@
No RSS feeds to show\n\nTap the + icon to import a feed
There was a problem loading your feeds. Please try again later.
+
+ Tap to change your profile picture
+ Change profile picture
+ Only your contacts can see your profile image
+ We\'re sorry, but something went wrong while updating your profile picture
+
Language & region
This setting will take effect when you restart Briar. Please sign out and restart Briar.
diff --git a/briar-android/src/release/java/org/briarproject/briar/android/test/TestAvatarCreatorImpl.java b/briar-android/src/release/java/org/briarproject/briar/android/test/TestAvatarCreatorImpl.java
new file mode 100644
index 000000000..3d235fcc4
--- /dev/null
+++ b/briar-android/src/release/java/org/briarproject/briar/android/test/TestAvatarCreatorImpl.java
@@ -0,0 +1,21 @@
+package org.briarproject.briar.android.test;
+
+import org.briarproject.briar.api.test.TestAvatarCreator;
+
+import java.io.InputStream;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+public class TestAvatarCreatorImpl implements TestAvatarCreator {
+
+ @Inject
+ TestAvatarCreatorImpl() {
+ }
+
+ @Nullable
+ @Override
+ public InputStream getAvatarInputStream() {
+ return null;
+ }
+}
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/attachment/AttachmentRetrieverTest.java b/briar-android/src/test/java/org/briarproject/briar/android/attachment/AttachmentRetrieverTest.java
index 5504da00b..1b9bdde06 100644
--- a/briar-android/src/test/java/org/briarproject/briar/android/attachment/AttachmentRetrieverTest.java
+++ b/briar-android/src/test/java/org/briarproject/briar/android/attachment/AttachmentRetrieverTest.java
@@ -1,11 +1,15 @@
package org.briarproject.briar.android.attachment;
+import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.ImmediateExecutor;
-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.android.attachment.media.ImageHelper;
+import org.briarproject.briar.android.attachment.media.ImageSizeCalculator;
+import org.briarproject.briar.android.attachment.media.Size;
+import org.briarproject.briar.api.attachment.Attachment;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.briarproject.briar.api.attachment.AttachmentReader;
import org.jmock.Expectations;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test;
@@ -25,6 +29,7 @@ public class AttachmentRetrieverTest extends BrambleMockTestCase {
private final AttachmentDimensions dimensions = new AttachmentDimensions(
100, 50, 200, 75, 300
);
+ private final GroupId groupId = new GroupId(getRandomId());
private final MessageId msgId = new MessageId(getRandomId());
private final ImageHelper imageHelper = context.mock(ImageHelper.class);
private final ImageSizeCalculator imageSizeCalculator;
@@ -32,11 +37,11 @@ public class AttachmentRetrieverTest extends BrambleMockTestCase {
public AttachmentRetrieverTest() {
context.setImposteriser(ClassImposteriser.INSTANCE);
- MessagingManager messagingManager =
- context.mock(MessagingManager.class);
+ AttachmentReader attachmentReader =
+ context.mock(AttachmentReader.class);
imageSizeCalculator = context.mock(ImageSizeCalculator.class);
Executor dbExecutor = new ImmediateExecutor();
- retriever = new AttachmentRetrieverImpl(dbExecutor, messagingManager,
+ retriever = new AttachmentRetrieverImpl(dbExecutor, attachmentReader,
dimensions, imageHelper, imageSizeCalculator);
}
@@ -133,7 +138,8 @@ public class AttachmentRetrieverTest extends BrambleMockTestCase {
}
private Attachment getAttachment(String contentType) {
- AttachmentHeader header = new AttachmentHeader(msgId, contentType);
+ AttachmentHeader header =
+ new AttachmentHeader(groupId, msgId, contentType);
InputStream in = new ByteArrayInputStream(getRandomBytes(42));
return new Attachment(header, in);
}
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/forum/ForumActivityTest.java b/briar-android/src/test/java/org/briarproject/briar/android/forum/ForumActivityTest.java
index 8c04d85b4..50c4c80fc 100644
--- a/briar-android/src/test/java/org/briarproject/briar/android/forum/ForumActivityTest.java
+++ b/briar-android/src/test/java/org/briarproject/briar/android/forum/ForumActivityTest.java
@@ -6,7 +6,7 @@ import junit.framework.Assert;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.android.threaded.ThreadItemAdapter;
@@ -25,7 +25,7 @@ import org.robolectric.annotation.Config;
import java.util.Arrays;
import static junit.framework.Assert.assertEquals;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.UNKNOWN;
import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/privategroup/list/GroupListViewModelTest.java b/briar-android/src/test/java/org/briarproject/briar/android/privategroup/list/GroupListViewModelTest.java
index 8030995b0..bfdc0c126 100644
--- a/briar-android/src/test/java/org/briarproject/briar/android/privategroup/list/GroupListViewModelTest.java
+++ b/briar-android/src/test/java/org/briarproject/briar/android/privategroup/list/GroupListViewModelTest.java
@@ -3,14 +3,12 @@ package org.briarproject.briar.android.privategroup.list;
import android.app.Application;
import org.briarproject.bramble.api.contact.Contact;
-import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.db.TransactionManager;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
-import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.system.AndroidExecutor;
@@ -20,6 +18,8 @@ import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.briar.android.AndroidExecutorTestImpl;
import org.briarproject.briar.android.viewmodel.LiveResult;
import org.briarproject.briar.api.android.AndroidNotificationManager;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.privategroup.PrivateGroup;
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
import org.briarproject.briar.api.privategroup.event.GroupDissolvedEvent;
@@ -67,8 +67,8 @@ public class GroupListViewModelTest extends BrambleMockTestCase {
context.mock(PrivateGroupManager.class);
private final GroupInvitationManager groupInvitationManager =
context.mock(GroupInvitationManager.class);
- private final ContactManager contactManager =
- context.mock(ContactManager.class);
+ private final AuthorManager authorManager =
+ context.mock(AuthorManager.class);
private final AndroidNotificationManager notificationManager =
context.mock(AndroidNotificationManager.class);
private final EventBus eventBus = context.mock(EventBus.class);
@@ -105,7 +105,7 @@ public class GroupListViewModelTest extends BrambleMockTestCase {
new AndroidExecutorTestImpl(dbExecutor);
viewModel = new GroupListViewModel(app, dbExecutor, lifecycleManager,
db, androidExecutor, groupManager, groupInvitationManager,
- contactManager, notificationManager, eventBus);
+ authorManager, notificationManager, eventBus);
}
@Test
@@ -199,7 +199,7 @@ public class GroupListViewModelTest extends BrambleMockTestCase {
AuthorInfo authorInfo, GroupCount groupCount, boolean dissolved)
throws DbException {
context.checking(new DbExpectations() {{
- oneOf(contactManager)
+ oneOf(authorManager)
.getAuthorInfo(txn, privateGroup.getCreator().getId());
will(returnValue(authorInfo));
oneOf(groupManager).getGroupCount(txn, privateGroup.getId());
diff --git a/briar-android/witness.gradle b/briar-android/witness.gradle
index 30cac8dce..668b51e64 100644
--- a/briar-android/witness.gradle
+++ b/briar-android/witness.gradle
@@ -97,10 +97,10 @@ dependencyVerification {
'com.android.tools:sdklib:27.1.1:sdklib-27.1.1.jar:08e6b83961ac9724b3c1e3d0eff971f13be6701292c77914b8794480f3391250',
'com.android:signflinger:4.1.1:signflinger-4.1.1.jar:0c66825988873ec2d51057fa463f54a8f18fc7326ff4530b9da363b71e97ce60',
'com.android:zipflinger:4.1.1:zipflinger-4.1.1.jar:0a8c3e52ac13dd031236f9fb5ba4408b1d5dcd12325a05440b36da09d8881446',
- 'com.github.bumptech.glide:annotations:4.10.0:annotations-4.10.0.jar:9cdc56335efeffe5b7ea5f7c98555451c6ea3c79c2f95f8e4ce2d4f76de226f8',
- 'com.github.bumptech.glide:compiler:4.10.0:compiler-4.10.0.jar:25083f3be077a890e7dda638a57d1a231842c794051f2c9705b857895d588e6d',
- 'com.github.bumptech.glide:gifdecoder:4.10.0:gifdecoder-4.10.0.aar:197a1cd5b76855aa02b230c13974e293229b901dc2b96fab4315201e78baa804',
- 'com.github.bumptech.glide:glide:4.10.0:glide-4.10.0.aar:07471967af4c290699feadb587c15671d31aebd95987f9b0b24210abfff1681e',
+ 'com.github.bumptech.glide:annotations:4.11.0:annotations-4.11.0.jar:d219d238006d824962176229d4708abcdddcfe342c6a18a5d0fa48d6f0479b3e',
+ 'com.github.bumptech.glide:compiler:4.11.0:compiler-4.11.0.jar:a98cdf265c36261b1d523448b05c322ec290afa865946425eded6361980e8770',
+ 'com.github.bumptech.glide:gifdecoder:4.11.0:gifdecoder-4.11.0.aar:197a1cd5b76855aa02b230c13974e293229b901dc2b96fab4315201e78baa804',
+ 'com.github.bumptech.glide:glide:4.11.0:glide-4.11.0.aar:5c294e6a5f0f812cef876b8412954c1822da184af38e082a5b766e3c4f4fcd95',
'com.github.chrisbanes:PhotoView:2.3.0:PhotoView-2.3.0.aar:6c8989f2945d50ab38b3e0300064f1f8d2d75bbcae1434fe535d9fb6898e9ad6',
'com.github.kobakei:MaterialFabSpeedDial:1.2.1:MaterialFabSpeedDial-1.2.1.aar:e86198c3c48cd832fb209a769a9f222c2a3cc045743b110ac2391d9737e3ea02',
'com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.0:accessibility-test-framework-2.0.jar:cdf16ef8f5b8023d003ce3cc1b0d51bda737762e2dab2fedf43d1c4292353f7f',
diff --git a/briar-api/build.gradle b/briar-api/build.gradle
index 6331e5edd..a4410603d 100644
--- a/briar-api/build.gradle
+++ b/briar-api/build.gradle
@@ -10,4 +10,7 @@ dependencies {
implementation project(path: ':bramble-api', configuration: 'default')
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
+
+ testImplementation project(path: ':bramble-api', configuration: 'testOutput')
+ testImplementation 'junit:junit:4.13.1'
}
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/Attachment.java b/briar-api/src/main/java/org/briarproject/briar/api/attachment/Attachment.java
similarity index 91%
rename from briar-api/src/main/java/org/briarproject/briar/api/messaging/Attachment.java
rename to briar-api/src/main/java/org/briarproject/briar/api/attachment/Attachment.java
index 61bb0b0eb..a6abd37b4 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/messaging/Attachment.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/attachment/Attachment.java
@@ -1,4 +1,4 @@
-package org.briarproject.briar.api.messaging;
+package org.briarproject.briar.api.attachment;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/AttachmentHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/attachment/AttachmentHeader.java
similarity index 56%
rename from briar-api/src/main/java/org/briarproject/briar/api/messaging/AttachmentHeader.java
rename to briar-api/src/main/java/org/briarproject/briar/api/attachment/AttachmentHeader.java
index 970401211..ab1efe121 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/messaging/AttachmentHeader.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/attachment/AttachmentHeader.java
@@ -1,6 +1,7 @@
-package org.briarproject.briar.api.messaging;
+package org.briarproject.briar.api.attachment;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.concurrent.Immutable;
@@ -9,14 +10,21 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault
public class AttachmentHeader {
+ private final GroupId groupId;
private final MessageId messageId;
private final String contentType;
- public AttachmentHeader(MessageId messageId, String contentType) {
+ public AttachmentHeader(GroupId groupId, MessageId messageId,
+ String contentType) {
+ this.groupId = groupId;
this.messageId = messageId;
this.contentType = contentType;
}
+ public GroupId getGroupId() {
+ return groupId;
+ }
+
public MessageId getMessageId() {
return messageId;
}
@@ -27,13 +35,15 @@ public class AttachmentHeader {
@Override
public boolean equals(Object o) {
- return o instanceof AttachmentHeader &&
- messageId.equals(((AttachmentHeader) o).messageId);
+ if (o instanceof AttachmentHeader) {
+ AttachmentHeader h = (AttachmentHeader) o;
+ return groupId.equals(h.groupId) && messageId.equals(h.messageId);
+ }
+ return false;
}
@Override
public int hashCode() {
return messageId.hashCode();
}
-
}
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/attachment/AttachmentReader.java b/briar-api/src/main/java/org/briarproject/briar/api/attachment/AttachmentReader.java
new file mode 100644
index 000000000..378c513c4
--- /dev/null
+++ b/briar-api/src/main/java/org/briarproject/briar/api/attachment/AttachmentReader.java
@@ -0,0 +1,20 @@
+package org.briarproject.briar.api.attachment;
+
+import org.briarproject.bramble.api.db.DbException;
+import org.briarproject.bramble.api.db.NoSuchMessageException;
+
+public interface AttachmentReader {
+
+ /**
+ * Returns the attachment with the given attachment header.
+ *
+ * @throws NoSuchMessageException If the header refers to a message in
+ * a different group from the one specified in the header, to a message
+ * that is not an attachment, or to an attachment that does not have the
+ * expected content type. This is meant to prevent social engineering
+ * attacks that use invalid attachment IDs to test whether messages exist
+ * in the victim's database
+ */
+ Attachment getAttachment(AttachmentHeader h) throws DbException;
+
+}
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/FileTooBigException.java b/briar-api/src/main/java/org/briarproject/briar/api/attachment/FileTooBigException.java
similarity index 64%
rename from briar-api/src/main/java/org/briarproject/briar/api/messaging/FileTooBigException.java
rename to briar-api/src/main/java/org/briarproject/briar/api/attachment/FileTooBigException.java
index f7d9c7c04..b905acc00 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/messaging/FileTooBigException.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/attachment/FileTooBigException.java
@@ -1,4 +1,4 @@
-package org.briarproject.briar.api.messaging;
+package org.briarproject.briar.api.attachment;
import java.io.IOException;
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/attachment/MediaConstants.java b/briar-api/src/main/java/org/briarproject/briar/api/attachment/MediaConstants.java
new file mode 100644
index 000000000..7c13575c3
--- /dev/null
+++ b/briar-api/src/main/java/org/briarproject/briar/api/attachment/MediaConstants.java
@@ -0,0 +1,21 @@
+package org.briarproject.briar.api.attachment;
+
+import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
+
+public interface MediaConstants {
+
+ // Metadata keys for messages
+ String MSG_KEY_CONTENT_TYPE = "contentType";
+ String MSG_KEY_DESCRIPTOR_LENGTH = "descriptorLength";
+
+ /**
+ * The maximum length of an attachment's content type in UTF-8 bytes.
+ */
+ int MAX_CONTENT_TYPE_BYTES = 50;
+
+ /**
+ * The maximum allowed size of image attachments.
+ * TODO: Different limit for GIFs?
+ */
+ int MAX_IMAGE_SIZE = MAX_MESSAGE_BODY_LENGTH - 100; // 6 * 1024 * 1024;
+}
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/avatar/AvatarManager.java b/briar-api/src/main/java/org/briarproject/briar/api/avatar/AvatarManager.java
new file mode 100644
index 000000000..631ceb27d
--- /dev/null
+++ b/briar-api/src/main/java/org/briarproject/briar/api/avatar/AvatarManager.java
@@ -0,0 +1,53 @@
+package org.briarproject.briar.api.avatar;
+
+import org.briarproject.bramble.api.contact.Contact;
+import org.briarproject.bramble.api.db.DbException;
+import org.briarproject.bramble.api.db.Transaction;
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.bramble.api.sync.ClientId;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.annotation.Nullable;
+
+@NotNullByDefault
+public interface AvatarManager {
+
+ /**
+ * The unique ID of the avatar client.
+ */
+ ClientId CLIENT_ID = new ClientId("org.briarproject.briar.avatar");
+
+ /**
+ * The current major version of the avatar client.
+ */
+ int MAJOR_VERSION = 0;
+
+ /**
+ * The current minor version of the avatar client.
+ */
+ int MINOR_VERSION = 0;
+
+ /**
+ * Store a new profile image represented by the given InputStream
+ * and share it with all contacts.
+ */
+ AttachmentHeader addAvatar(String contentType, InputStream in)
+ throws DbException, IOException;
+
+ /**
+ * Returns the current known profile image header for the given contact
+ * or null if none is known.
+ */
+ @Nullable
+ AttachmentHeader getAvatarHeader(Transaction txn, Contact c)
+ throws DbException;
+
+ /**
+ * Returns our current profile image header or null if none has been added.
+ */
+ @Nullable
+ AttachmentHeader getMyAvatarHeader(Transaction txn) throws DbException;
+}
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/avatar/AvatarMessageEncoder.java b/briar-api/src/main/java/org/briarproject/briar/api/avatar/AvatarMessageEncoder.java
new file mode 100644
index 000000000..e186522f8
--- /dev/null
+++ b/briar-api/src/main/java/org/briarproject/briar/api/avatar/AvatarMessageEncoder.java
@@ -0,0 +1,18 @@
+package org.briarproject.briar.api.avatar;
+
+import org.briarproject.bramble.api.Pair;
+import org.briarproject.bramble.api.data.BdfDictionary;
+import org.briarproject.bramble.api.sync.GroupId;
+import org.briarproject.bramble.api.sync.Message;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface AvatarMessageEncoder {
+ /**
+ * Returns an update message and its metadata.
+ */
+ Pair encodeUpdateMessage(GroupId groupId,
+ long version, String contentType, InputStream in)
+ throws IOException;
+}
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/avatar/event/AvatarUpdatedEvent.java b/briar-api/src/main/java/org/briarproject/briar/api/avatar/event/AvatarUpdatedEvent.java
new file mode 100644
index 000000000..382b1572a
--- /dev/null
+++ b/briar-api/src/main/java/org/briarproject/briar/api/avatar/event/AvatarUpdatedEvent.java
@@ -0,0 +1,33 @@
+package org.briarproject.briar.api.avatar.event;
+
+import org.briarproject.bramble.api.contact.ContactId;
+import org.briarproject.bramble.api.event.Event;
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * An event that is broadcast when a new avatar is received.
+ */
+@Immutable
+@NotNullByDefault
+public class AvatarUpdatedEvent extends Event {
+
+ private final ContactId contactId;
+ private final AttachmentHeader attachmentHeader;
+
+ public AvatarUpdatedEvent(ContactId contactId,
+ AttachmentHeader attachmentHeader) {
+ this.contactId = contactId;
+ this.attachmentHeader = attachmentHeader;
+ }
+
+ public ContactId getContactId() {
+ return contactId;
+ }
+
+ public AttachmentHeader getAttachmentHeader() {
+ return attachmentHeader;
+ }
+}
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java
index 16bf16ab8..02ab6a2d5 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java
@@ -1,7 +1,7 @@
package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java
index ba6ad20a3..4d99b10f9 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java
@@ -1,7 +1,7 @@
package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/client/PostHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/client/PostHeader.java
index 3a975f5c2..9957d8dfa 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/client/PostHeader.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/client/PostHeader.java
@@ -1,8 +1,8 @@
package org.briarproject.briar.api.client;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
-import org.briarproject.bramble.api.identity.AuthorInfo.Status;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo.Status;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/forum/ForumPostHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/forum/ForumPostHeader.java
index ae8304d4a..ebf93b621 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/forum/ForumPostHeader.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/forum/ForumPostHeader.java
@@ -1,7 +1,7 @@
package org.briarproject.briar.api.forum;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.PostHeader;
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/AuthorInfo.java b/briar-api/src/main/java/org/briarproject/briar/api/identity/AuthorInfo.java
similarity index 60%
rename from bramble-api/src/main/java/org/briarproject/bramble/api/identity/AuthorInfo.java
rename to briar-api/src/main/java/org/briarproject/briar/api/identity/AuthorInfo.java
index 26f4f3398..c6bf5549c 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/AuthorInfo.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/identity/AuthorInfo.java
@@ -1,6 +1,8 @@
-package org.briarproject.bramble.api.identity;
+package org.briarproject.briar.api.identity;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.bramble.api.nullsafety.NullSafety;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@@ -20,14 +22,18 @@ public class AuthorInfo {
private final Status status;
@Nullable
private final String alias;
+ @Nullable
+ private final AttachmentHeader avatarHeader;
- public AuthorInfo(Status status, @Nullable String alias) {
+ public AuthorInfo(Status status, @Nullable String alias,
+ @Nullable AttachmentHeader avatarHeader) {
this.status = status;
this.alias = alias;
+ this.avatarHeader = avatarHeader;
}
public AuthorInfo(Status status) {
- this(status, null);
+ this(status, null, null);
}
public Status getStatus() {
@@ -39,6 +45,11 @@ public class AuthorInfo {
return alias;
}
+ @Nullable
+ public AttachmentHeader getAvatarHeader() {
+ return avatarHeader;
+ }
+
@Override
public int hashCode() {
int hashCode = status.ordinal();
@@ -51,6 +62,9 @@ public class AuthorInfo {
if (!(o instanceof AuthorInfo)) return false;
AuthorInfo info = (AuthorInfo) o;
return status == info.status &&
- (alias == null ? info.alias == null : alias.equals(info.alias));
+ // aliases are equal
+ NullSafety.equals(alias, info.alias) &&
+ // avatars are equal
+ NullSafety.equals(avatarHeader, info.avatarHeader);
}
}
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/identity/AuthorManager.java b/briar-api/src/main/java/org/briarproject/briar/api/identity/AuthorManager.java
new file mode 100644
index 000000000..46381a3c0
--- /dev/null
+++ b/briar-api/src/main/java/org/briarproject/briar/api/identity/AuthorManager.java
@@ -0,0 +1,43 @@
+package org.briarproject.briar.api.identity;
+
+import org.briarproject.bramble.api.contact.Contact;
+import org.briarproject.bramble.api.db.DbException;
+import org.briarproject.bramble.api.db.Transaction;
+import org.briarproject.bramble.api.identity.AuthorId;
+import org.briarproject.bramble.api.identity.LocalAuthor;
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+
+@NotNullByDefault
+public interface AuthorManager {
+
+ /**
+ * Returns the {@link AuthorInfo} for the given author.
+ */
+ AuthorInfo getAuthorInfo(AuthorId a) throws DbException;
+
+ /**
+ * Returns the {@link AuthorInfo} for the given author.
+ */
+ AuthorInfo getAuthorInfo(Transaction txn, AuthorId a) throws DbException;
+
+ /**
+ * Returns the {@link AuthorInfo} for the given contact.
+ */
+ AuthorInfo getAuthorInfo(Contact c) throws DbException;
+
+ /**
+ * Returns the {@link AuthorInfo} for the given contact.
+ */
+ AuthorInfo getAuthorInfo(Transaction txn, Contact c)
+ throws DbException;
+
+ /**
+ * Returns the {@link AuthorInfo} for the {@link LocalAuthor}.
+ */
+ AuthorInfo getMyAuthorInfo() throws DbException;
+
+ /**
+ * Returns the {@link AuthorInfo} for the {@link LocalAuthor}.
+ */
+ AuthorInfo getMyAuthorInfo(Transaction txn) throws DbException;
+}
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionRequest.java b/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionRequest.java
index fbb341ce0..c6a5db547 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionRequest.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionRequest.java
@@ -1,7 +1,7 @@
package org.briarproject.briar.api.introduction;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionResponse.java b/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionResponse.java
index 18c68c159..4b1777611 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionResponse.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/introduction/IntroductionResponse.java
@@ -1,7 +1,7 @@
package org.briarproject.briar.api.introduction;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/InvalidAttachmentException.java b/briar-api/src/main/java/org/briarproject/briar/api/messaging/InvalidAttachmentException.java
deleted file mode 100644
index df32602d1..000000000
--- a/briar-api/src/main/java/org/briarproject/briar/api/messaging/InvalidAttachmentException.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.briarproject.briar.api.messaging;
-
-import org.briarproject.bramble.api.db.DbException;
-import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
-
-/**
- * An exception that is thrown when an {@link AttachmentHeader} is used to
- * load an {@link Attachment}, and the header refers to a message that is not
- * an attachment, or to an attachment that does not have the expected content
- * type.
- */
-@NotNullByDefault
-public class InvalidAttachmentException extends DbException {
-}
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingConstants.java b/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingConstants.java
index f957a404a..4a87775e0 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingConstants.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingConstants.java
@@ -14,15 +14,4 @@ public interface MessagingConstants {
*/
int MAX_ATTACHMENTS_PER_MESSAGE = 10;
- /**
- * The maximum length of an attachment's content type in UTF-8 bytes.
- */
- int MAX_CONTENT_TYPE_BYTES = 50;
-
- /**
- * The maximum allowed size of image attachments.
- * TODO: Different limit for GIFs?
- */
- int MAX_IMAGE_SIZE = MAX_MESSAGE_BODY_LENGTH - 100; // 6 * 1024 * 1024;
-
}
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingManager.java b/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingManager.java
index ccb8f776f..dfe18806a 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingManager.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/messaging/MessagingManager.java
@@ -7,6 +7,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.briarproject.briar.api.attachment.FileTooBigException;
import org.briarproject.briar.api.conversation.ConversationManager.ConversationClient;
import java.io.IOException;
@@ -67,19 +69,10 @@ public interface MessagingManager extends ConversationClient {
@Nullable
String getMessageText(MessageId m) throws DbException;
- /**
- * Returns the attachment with the given attachment header.
- *
- * @throws InvalidAttachmentException If the header refers to a message
- * that is not an attachment, or to an attachment that does not have the
- * expected content type
- */
- Attachment getAttachment(AttachmentHeader h) throws DbException;
-
/**
* Returns true if the contact with the given {@link ContactId} does support
* image attachments.
- *
+ *
* Added: 2019-01-01
*/
boolean contactSupportsImages(Transaction txn, ContactId c)
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessage.java b/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessage.java
index 58bde1576..188364444 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessage.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessage.java
@@ -2,6 +2,7 @@ package org.briarproject.briar.api.messaging;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Message;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import java.util.List;
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageFactory.java b/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageFactory.java
index 2f7e1127b..da4532015 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageFactory.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageFactory.java
@@ -3,6 +3,7 @@ package org.briarproject.briar.api.messaging;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import java.util.List;
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageHeader.java
index 010466f6f..9e17af615 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageHeader.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/messaging/PrivateMessageHeader.java
@@ -3,6 +3,7 @@ package org.briarproject.briar.api.messaging;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
import org.briarproject.briar.api.conversation.ConversationMessageVisitor;
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/privategroup/GroupMember.java b/briar-api/src/main/java/org/briarproject/briar/api/privategroup/GroupMember.java
index 6080b6a9e..5f842f020 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/privategroup/GroupMember.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/privategroup/GroupMember.java
@@ -2,7 +2,7 @@ package org.briarproject.briar.api.privategroup;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/privategroup/GroupMessageHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/privategroup/GroupMessageHeader.java
index 7174114c5..724b077cb 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/privategroup/GroupMessageHeader.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/privategroup/GroupMessageHeader.java
@@ -1,7 +1,7 @@
package org.briarproject.briar.api.privategroup;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/test/TestAvatarCreator.java b/briar-api/src/main/java/org/briarproject/briar/api/test/TestAvatarCreator.java
new file mode 100644
index 000000000..d2d322c92
--- /dev/null
+++ b/briar-api/src/main/java/org/briarproject/briar/api/test/TestAvatarCreator.java
@@ -0,0 +1,11 @@
+package org.briarproject.briar.api.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.annotation.Nullable;
+
+public interface TestAvatarCreator {
+ @Nullable
+ InputStream getAvatarInputStream() throws IOException;
+}
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/test/TestDataCreator.java b/briar-api/src/main/java/org/briarproject/briar/api/test/TestDataCreator.java
index 3db7b48b9..0f8ebc2ff 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/test/TestDataCreator.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/test/TestDataCreator.java
@@ -14,13 +14,15 @@ public interface TestDataCreator {
* @param numContacts Number of contacts to create. Must be >= 1
* @param numPrivateMsgs Number of private messages to create for each
* contact.
+ * @param avatarPercent Percentage of contacts
+ * that will use a random profile image. Between 0 and 100.
* @param numBlogPosts Number of blog posts to create.
* @param numForums Number of forums to create.
* @param numForumPosts Number of forum posts to create per forum.
*/
- void createTestData(int numContacts, int numPrivateMsgs, int numBlogPosts,
- int numForums, int numForumPosts);
+ void createTestData(int numContacts, int numPrivateMsgs, int avatarPercent,
+ int numBlogPosts, int numForums, int numForumPosts);
@IoExecutor
- Contact addContact(String name) throws DbException;
+ Contact addContact(String name, boolean avatar) throws DbException;
}
diff --git a/briar-api/src/test/java/org/briarproject/briar/api/identity/AuthorInfoTest.java b/briar-api/src/test/java/org/briarproject/briar/api/identity/AuthorInfoTest.java
new file mode 100644
index 000000000..a9a616236
--- /dev/null
+++ b/briar-api/src/test/java/org/briarproject/briar/api/identity/AuthorInfoTest.java
@@ -0,0 +1,61 @@
+package org.briarproject.briar.api.identity;
+
+import org.briarproject.bramble.api.sync.GroupId;
+import org.briarproject.bramble.api.sync.MessageId;
+import org.briarproject.bramble.test.BrambleTestCase;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.junit.Test;
+
+import static org.briarproject.bramble.test.TestUtils.getRandomId;
+import static org.briarproject.bramble.util.StringUtils.getRandomString;
+import static org.briarproject.briar.api.attachment.MediaConstants.MAX_CONTENT_TYPE_BYTES;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.NONE;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.VERIFIED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+public class AuthorInfoTest extends BrambleTestCase {
+
+ private final String contentType = getRandomString(MAX_CONTENT_TYPE_BYTES);
+ private final AttachmentHeader avatarHeader =
+ new AttachmentHeader(new GroupId(getRandomId()),
+ new MessageId(getRandomId()), contentType);
+
+ @Test
+ public void testEquals() {
+ assertEquals(
+ new AuthorInfo(NONE),
+ new AuthorInfo(NONE, null, null)
+ );
+ assertEquals(
+ new AuthorInfo(NONE, "test", null),
+ new AuthorInfo(NONE, "test", null)
+ );
+ assertEquals(
+ new AuthorInfo(NONE, "test", avatarHeader),
+ new AuthorInfo(NONE, "test", avatarHeader)
+ );
+
+ assertNotEquals(
+ new AuthorInfo(NONE),
+ new AuthorInfo(VERIFIED)
+ );
+ assertNotEquals(
+ new AuthorInfo(NONE, "test", null),
+ new AuthorInfo(NONE)
+ );
+ assertNotEquals(
+ new AuthorInfo(NONE),
+ new AuthorInfo(NONE, "test", null)
+ );
+ assertNotEquals(
+ new AuthorInfo(NONE, "a", null),
+ new AuthorInfo(NONE, "b", null)
+ );
+ assertNotEquals(
+ new AuthorInfo(NONE, "a", null),
+ new AuthorInfo(NONE, "a", avatarHeader)
+ );
+ }
+
+}
diff --git a/briar-core/src/main/java/org/briarproject/briar/BriarCoreEagerSingletons.java b/briar-core/src/main/java/org/briarproject/briar/BriarCoreEagerSingletons.java
index 3d42616b5..72c5cf603 100644
--- a/briar-core/src/main/java/org/briarproject/briar/BriarCoreEagerSingletons.java
+++ b/briar-core/src/main/java/org/briarproject/briar/BriarCoreEagerSingletons.java
@@ -1,8 +1,10 @@
package org.briarproject.briar;
+import org.briarproject.briar.avatar.AvatarModule;
import org.briarproject.briar.blog.BlogModule;
import org.briarproject.briar.feed.FeedModule;
import org.briarproject.briar.forum.ForumModule;
+import org.briarproject.briar.identity.IdentityModule;
import org.briarproject.briar.introduction.IntroductionModule;
import org.briarproject.briar.messaging.MessagingModule;
import org.briarproject.briar.privategroup.PrivateGroupModule;
@@ -11,6 +13,8 @@ import org.briarproject.briar.sharing.SharingModule;
public interface BriarCoreEagerSingletons {
+ void inject(AvatarModule.EagerSingletons init);
+
void inject(BlogModule.EagerSingletons init);
void inject(FeedModule.EagerSingletons init);
@@ -19,6 +23,8 @@ public interface BriarCoreEagerSingletons {
void inject(GroupInvitationModule.EagerSingletons init);
+ void inject(IdentityModule.EagerSingletons init);
+
void inject(IntroductionModule.EagerSingletons init);
void inject(MessagingModule.EagerSingletons init);
@@ -30,6 +36,7 @@ public interface BriarCoreEagerSingletons {
class Helper {
public static void injectEagerSingletons(BriarCoreEagerSingletons c) {
+ c.inject(new AvatarModule.EagerSingletons());
c.inject(new BlogModule.EagerSingletons());
c.inject(new FeedModule.EagerSingletons());
c.inject(new ForumModule.EagerSingletons());
@@ -37,6 +44,7 @@ public interface BriarCoreEagerSingletons {
c.inject(new MessagingModule.EagerSingletons());
c.inject(new PrivateGroupModule.EagerSingletons());
c.inject(new SharingModule.EagerSingletons());
+ c.inject(new IdentityModule.EagerSingletons());
c.inject(new IntroductionModule.EagerSingletons());
}
}
diff --git a/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java b/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java
index 5ba057706..222e41a96 100644
--- a/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java
+++ b/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java
@@ -1,10 +1,13 @@
package org.briarproject.briar;
+import org.briarproject.briar.attachment.AttachmentModule;
+import org.briarproject.briar.avatar.AvatarModule;
import org.briarproject.briar.blog.BlogModule;
import org.briarproject.briar.client.BriarClientModule;
import org.briarproject.briar.feed.DnsModule;
import org.briarproject.briar.feed.FeedModule;
import org.briarproject.briar.forum.ForumModule;
+import org.briarproject.briar.identity.IdentityModule;
import org.briarproject.briar.introduction.IntroductionModule;
import org.briarproject.briar.messaging.MessagingModule;
import org.briarproject.briar.privategroup.PrivateGroupModule;
@@ -15,13 +18,16 @@ import org.briarproject.briar.test.TestModule;
import dagger.Module;
@Module(includes = {
+ AvatarModule.class,
BlogModule.class,
BriarClientModule.class,
FeedModule.class,
DnsModule.class,
ForumModule.class,
GroupInvitationModule.class,
+ IdentityModule.class,
IntroductionModule.class,
+ AttachmentModule.class,
MessagingModule.class,
PrivateGroupModule.class,
SharingModule.class,
diff --git a/briar-core/src/main/java/org/briarproject/briar/attachment/AttachmentModule.java b/briar-core/src/main/java/org/briarproject/briar/attachment/AttachmentModule.java
new file mode 100644
index 000000000..e14747a74
--- /dev/null
+++ b/briar-core/src/main/java/org/briarproject/briar/attachment/AttachmentModule.java
@@ -0,0 +1,20 @@
+package org.briarproject.briar.attachment;
+
+import org.briarproject.briar.api.attachment.AttachmentReader;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public class AttachmentModule {
+
+ @Provides
+ @Singleton
+ AttachmentReader provideAttachmentReader(
+ AttachmentReaderImpl attachmentReader) {
+ return attachmentReader;
+ }
+
+}
diff --git a/briar-core/src/main/java/org/briarproject/briar/attachment/AttachmentReaderImpl.java b/briar-core/src/main/java/org/briarproject/briar/attachment/AttachmentReaderImpl.java
new file mode 100644
index 000000000..e61918cfe
--- /dev/null
+++ b/briar-core/src/main/java/org/briarproject/briar/attachment/AttachmentReaderImpl.java
@@ -0,0 +1,56 @@
+package org.briarproject.briar.attachment;
+
+import org.briarproject.bramble.api.FormatException;
+import org.briarproject.bramble.api.client.ClientHelper;
+import org.briarproject.bramble.api.data.BdfDictionary;
+import org.briarproject.bramble.api.db.DbException;
+import org.briarproject.bramble.api.db.NoSuchMessageException;
+import org.briarproject.bramble.api.sync.Message;
+import org.briarproject.bramble.api.sync.MessageId;
+import org.briarproject.briar.api.attachment.Attachment;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.briarproject.briar.api.attachment.AttachmentReader;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import javax.inject.Inject;
+
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_CONTENT_TYPE;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH;
+
+public class AttachmentReaderImpl implements AttachmentReader {
+
+ private final ClientHelper clientHelper;
+
+ @Inject
+ public AttachmentReaderImpl(ClientHelper clientHelper) {
+ this.clientHelper = clientHelper;
+ }
+
+ @Override
+ public Attachment getAttachment(AttachmentHeader h) throws DbException {
+ // TODO: Support large messages
+ MessageId m = h.getMessageId();
+ Message message = clientHelper.getMessage(m);
+ // Check that the message is in the expected group, to prevent it from
+ // being loaded in the context of a different group
+ if (!message.getGroupId().equals(h.getGroupId())) {
+ throw new NoSuchMessageException();
+ }
+ byte[] body = message.getBody();
+ try {
+ BdfDictionary meta = clientHelper.getMessageMetadataAsDictionary(m);
+ String contentType = meta.getString(MSG_KEY_CONTENT_TYPE);
+ if (!contentType.equals(h.getContentType()))
+ throw new NoSuchMessageException();
+ int offset = meta.getLong(MSG_KEY_DESCRIPTOR_LENGTH).intValue();
+ InputStream stream = new ByteArrayInputStream(body, offset,
+ body.length - offset);
+ return new Attachment(h, stream);
+ } catch (FormatException e) {
+ throw new NoSuchMessageException();
+ }
+ }
+
+}
diff --git a/briar-core/src/main/java/org/briarproject/briar/messaging/CountingInputStream.java b/briar-core/src/main/java/org/briarproject/briar/attachment/CountingInputStream.java
similarity index 85%
rename from briar-core/src/main/java/org/briarproject/briar/messaging/CountingInputStream.java
rename to briar-core/src/main/java/org/briarproject/briar/attachment/CountingInputStream.java
index 4f6063565..8c1d8ecf9 100644
--- a/briar-core/src/main/java/org/briarproject/briar/messaging/CountingInputStream.java
+++ b/briar-core/src/main/java/org/briarproject/briar/attachment/CountingInputStream.java
@@ -1,4 +1,4 @@
-package org.briarproject.briar.messaging;
+package org.briarproject.briar.attachment;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -13,19 +13,19 @@ import javax.annotation.concurrent.NotThreadSafe;
*/
@NotThreadSafe
@NotNullByDefault
-class CountingInputStream extends InputStream {
+public class CountingInputStream extends InputStream {
private final InputStream delegate;
private final long maxBytesToRead;
private long bytesRead = 0;
- CountingInputStream(InputStream delegate, long maxBytesToRead) {
+ public CountingInputStream(InputStream delegate, long maxBytesToRead) {
this.delegate = delegate;
this.maxBytesToRead = maxBytesToRead;
}
- long getBytesRead() {
+ public long getBytesRead() {
return bytesRead;
}
diff --git a/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarConstants.java b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarConstants.java
new file mode 100644
index 000000000..56c4163b7
--- /dev/null
+++ b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarConstants.java
@@ -0,0 +1,14 @@
+package org.briarproject.briar.avatar;
+
+interface AvatarConstants {
+
+ // Message type constants
+ int MSG_TYPE_UPDATE = 0;
+
+ // Metadata keys for groups
+ String GROUP_KEY_CONTACT_ID = "contactId";
+
+ // Message metadata keys
+ String MSG_KEY_VERSION = "version";
+
+}
diff --git a/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarManagerImpl.java
new file mode 100644
index 000000000..7c5967dab
--- /dev/null
+++ b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarManagerImpl.java
@@ -0,0 +1,282 @@
+package org.briarproject.briar.avatar;
+
+import org.briarproject.bramble.api.FormatException;
+import org.briarproject.bramble.api.Pair;
+import org.briarproject.bramble.api.client.ClientHelper;
+import org.briarproject.bramble.api.contact.Contact;
+import org.briarproject.bramble.api.contact.ContactId;
+import org.briarproject.bramble.api.contact.ContactManager.ContactHook;
+import org.briarproject.bramble.api.data.BdfDictionary;
+import org.briarproject.bramble.api.data.MetadataParser;
+import org.briarproject.bramble.api.db.DatabaseComponent;
+import org.briarproject.bramble.api.db.DbException;
+import org.briarproject.bramble.api.db.Metadata;
+import org.briarproject.bramble.api.db.Transaction;
+import org.briarproject.bramble.api.identity.AuthorId;
+import org.briarproject.bramble.api.identity.IdentityManager;
+import org.briarproject.bramble.api.identity.LocalAuthor;
+import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.bramble.api.sync.Group;
+import org.briarproject.bramble.api.sync.Group.Visibility;
+import org.briarproject.bramble.api.sync.GroupFactory;
+import org.briarproject.bramble.api.sync.GroupId;
+import org.briarproject.bramble.api.sync.InvalidMessageException;
+import org.briarproject.bramble.api.sync.Message;
+import org.briarproject.bramble.api.sync.MessageId;
+import org.briarproject.bramble.api.sync.validation.IncomingMessageHook;
+import org.briarproject.bramble.api.versioning.ClientVersioningManager;
+import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.briarproject.briar.api.avatar.AvatarManager;
+import org.briarproject.briar.api.avatar.AvatarMessageEncoder;
+import org.briarproject.briar.api.avatar.event.AvatarUpdatedEvent;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import javax.inject.Inject;
+
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_CONTENT_TYPE;
+import static org.briarproject.briar.avatar.AvatarConstants.GROUP_KEY_CONTACT_ID;
+import static org.briarproject.briar.avatar.AvatarConstants.MSG_KEY_VERSION;
+
+@Immutable
+@NotNullByDefault
+class AvatarManagerImpl implements AvatarManager, OpenDatabaseHook, ContactHook,
+ ClientVersioningHook, IncomingMessageHook {
+
+ private final DatabaseComponent db;
+ private final IdentityManager identityManager;
+ private final ClientHelper clientHelper;
+ private final ClientVersioningManager clientVersioningManager;
+ private final MetadataParser metadataParser;
+ private final GroupFactory groupFactory;
+ private final AvatarMessageEncoder avatarMessageEncoder;
+
+ @Inject
+ AvatarManagerImpl(
+ DatabaseComponent db,
+ IdentityManager identityManager,
+ ClientHelper clientHelper,
+ ClientVersioningManager clientVersioningManager,
+ MetadataParser metadataParser,
+ GroupFactory groupFactory,
+ AvatarMessageEncoder avatarMessageEncoder) {
+ this.db = db;
+ this.identityManager = identityManager;
+ this.clientHelper = clientHelper;
+ this.clientVersioningManager = clientVersioningManager;
+ this.metadataParser = metadataParser;
+ this.groupFactory = groupFactory;
+ this.avatarMessageEncoder = avatarMessageEncoder;
+ }
+
+ @Override
+ public void onDatabaseOpened(Transaction txn) throws DbException {
+ // Create our avatar group if necessary
+ LocalAuthor a = identityManager.getLocalAuthor(txn);
+ Group ourGroup = getGroup(a.getId());
+ if (db.containsGroup(txn, ourGroup.getId())) return;
+ db.addGroup(txn, ourGroup);
+
+ // Set things up for any pre-existing contacts
+ for (Contact c : db.getContacts(txn)) addingContact(txn, c);
+ }
+
+ @Override
+ public void addingContact(Transaction txn, Contact c) throws DbException {
+ // Create a group to share with the contact
+ Group theirGroup = getGroup(c.getAuthor().getId());
+ db.addGroup(txn, theirGroup);
+ // Attach the contact ID to the group
+ BdfDictionary d = new BdfDictionary();
+ d.put(GROUP_KEY_CONTACT_ID, c.getId().getInt());
+ try {
+ clientHelper.mergeGroupMetadata(txn, theirGroup.getId(), d);
+ } catch (FormatException e) {
+ throw new AssertionError(e);
+ }
+ // Apply the client's visibility to our and their group
+ Group ourGroup = getOurGroup(txn);
+ Visibility client = clientVersioningManager.getClientVisibility(txn,
+ c.getId(), CLIENT_ID, MAJOR_VERSION);
+ db.setGroupVisibility(txn, c.getId(), ourGroup.getId(), client);
+ db.setGroupVisibility(txn, c.getId(), theirGroup.getId(), client);
+ }
+
+ @Override
+ public void removingContact(Transaction txn, Contact c) throws DbException {
+ db.removeGroup(txn, getGroup(c.getAuthor().getId()));
+ }
+
+ @Override
+ public void onClientVisibilityChanging(Transaction txn, Contact c,
+ Visibility v) throws DbException {
+ // Apply the client's visibility to our and the contact group
+ Group ourGroup = getOurGroup(txn);
+ Group theirGroup = getGroup(c.getAuthor().getId());
+ db.setGroupVisibility(txn, c.getId(), ourGroup.getId(), v);
+ db.setGroupVisibility(txn, c.getId(), theirGroup.getId(), v);
+ }
+
+ @Override
+ public boolean incomingMessage(Transaction txn, Message m, Metadata meta)
+ throws DbException, InvalidMessageException {
+ Group ourGroup = getOurGroup(txn);
+ if (m.getGroupId().equals(ourGroup.getId())) {
+ throw new InvalidMessageException(
+ "Received incoming message in my avatar group");
+ }
+ try {
+ // Find the latest update, if any
+ BdfDictionary d = metadataParser.parse(meta);
+ LatestUpdate latest = findLatest(txn, m.getGroupId());
+ if (latest != null) {
+ if (d.getLong(MSG_KEY_VERSION) > latest.version) {
+ // This update is newer - delete the previous update
+ db.deleteMessage(txn, latest.messageId);
+ db.deleteMessageMetadata(txn, latest.messageId);
+ } else {
+ // We've already received a newer update - delete this one
+ db.deleteMessage(txn, m.getId());
+ db.deleteMessageMetadata(txn, m.getId());
+ return false; // don't broadcast update
+ }
+ }
+ ContactId contactId = getContactId(txn, m.getGroupId());
+ String contentType = d.getString(MSG_KEY_CONTENT_TYPE);
+ AttachmentHeader a = new AttachmentHeader(m.getGroupId(), m.getId(),
+ contentType);
+ txn.attach(new AvatarUpdatedEvent(contactId, a));
+ } catch (FormatException e) {
+ throw new InvalidMessageException(e);
+ }
+ return false;
+ }
+
+ @Override
+ public AttachmentHeader addAvatar(String contentType, InputStream in)
+ throws DbException, IOException {
+ // find latest avatar
+ GroupId groupId;
+ LatestUpdate latest;
+ Transaction txn = db.startTransaction(true);
+ try {
+ groupId = getOurGroup(txn).getId();
+ latest = findLatest(txn, groupId);
+ db.commitTransaction(txn);
+ } finally {
+ db.endTransaction(txn);
+ }
+ long version = latest == null ? 0 : latest.version + 1;
+ // encode message and metadata
+ Pair encodedMessage = avatarMessageEncoder
+ .encodeUpdateMessage(groupId, version, contentType, in);
+ Message m = encodedMessage.getFirst();
+ BdfDictionary meta = encodedMessage.getSecond();
+ // save/send avatar and delete old one
+ return db.transactionWithResult(false, txn2 -> {
+ // re-query latest update as it might have changed since last query
+ LatestUpdate newLatest = findLatest(txn2, groupId);
+ if (newLatest != null && newLatest.version > version) {
+ // latest update is newer than our own
+ // no need to store or delete anything, just return latest
+ return new AttachmentHeader(groupId, newLatest.messageId,
+ newLatest.contentType);
+ } else if (newLatest != null) {
+ // delete latest update if it has the same or lower version
+ db.deleteMessage(txn2, newLatest.messageId);
+ db.deleteMessageMetadata(txn2, newLatest.messageId);
+ }
+ clientHelper.addLocalMessage(txn2, m, meta, true, false);
+ return new AttachmentHeader(groupId, m.getId(), contentType);
+ });
+ }
+
+ @Nullable
+ @Override
+ public AttachmentHeader getAvatarHeader(Transaction txn, Contact c)
+ throws DbException {
+ try {
+ Group g = getGroup(c.getAuthor().getId());
+ return getAvatarHeader(txn, g.getId());
+ } catch (FormatException e) {
+ throw new DbException(e);
+ }
+ }
+
+ @Nullable
+ @Override
+ public AttachmentHeader getMyAvatarHeader(Transaction txn)
+ throws DbException {
+ try {
+ Group g = getOurGroup(txn);
+ return getAvatarHeader(txn, g.getId());
+ } catch (FormatException e) {
+ throw new DbException(e);
+ }
+ }
+
+ @Nullable
+ private AttachmentHeader getAvatarHeader(Transaction txn, GroupId groupId)
+ throws DbException, FormatException {
+ LatestUpdate latest = findLatest(txn, groupId);
+ if (latest == null) return null;
+ return new AttachmentHeader(groupId, latest.messageId,
+ latest.contentType);
+ }
+
+ @Nullable
+ private LatestUpdate findLatest(Transaction txn, GroupId g)
+ throws DbException, FormatException {
+ Map metadata =
+ clientHelper.getMessageMetadataAsDictionary(txn, g);
+ for (Map.Entry e : metadata.entrySet()) {
+ BdfDictionary meta = e.getValue();
+ long version = meta.getLong(MSG_KEY_VERSION);
+ String contentType = meta.getString(MSG_KEY_CONTENT_TYPE);
+ return new LatestUpdate(e.getKey(), version, contentType);
+ }
+ return null;
+ }
+
+ private ContactId getContactId(Transaction txn, GroupId g)
+ throws DbException {
+ try {
+ BdfDictionary meta =
+ clientHelper.getGroupMetadataAsDictionary(txn, g);
+ return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue());
+ } catch (FormatException e) {
+ throw new DbException(e);
+ }
+ }
+
+ private Group getOurGroup(Transaction txn) throws DbException {
+ LocalAuthor a = identityManager.getLocalAuthor(txn);
+ return getGroup(a.getId());
+ }
+
+ private Group getGroup(AuthorId authorId) {
+ return groupFactory
+ .createGroup(CLIENT_ID, MAJOR_VERSION, authorId.getBytes());
+ }
+
+ private static class LatestUpdate {
+
+ private final MessageId messageId;
+ private final long version;
+ private final String contentType;
+
+ private LatestUpdate(MessageId messageId, long version,
+ String contentType) {
+ this.messageId = messageId;
+ this.version = version;
+ this.contentType = contentType;
+ }
+ }
+
+}
diff --git a/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarMessageEncoderImpl.java b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarMessageEncoderImpl.java
new file mode 100644
index 000000000..c4308a469
--- /dev/null
+++ b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarMessageEncoderImpl.java
@@ -0,0 +1,70 @@
+package org.briarproject.briar.avatar;
+
+import org.briarproject.bramble.api.Pair;
+import org.briarproject.bramble.api.client.ClientHelper;
+import org.briarproject.bramble.api.data.BdfDictionary;
+import org.briarproject.bramble.api.data.BdfList;
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.bramble.api.sync.GroupId;
+import org.briarproject.bramble.api.sync.Message;
+import org.briarproject.bramble.api.system.Clock;
+import org.briarproject.briar.api.attachment.FileTooBigException;
+import org.briarproject.briar.api.avatar.AvatarMessageEncoder;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.annotation.concurrent.Immutable;
+import javax.inject.Inject;
+
+import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
+import static org.briarproject.bramble.util.IoUtils.copyAndClose;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_CONTENT_TYPE;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH;
+import static org.briarproject.briar.avatar.AvatarConstants.MSG_KEY_VERSION;
+import static org.briarproject.briar.avatar.AvatarConstants.MSG_TYPE_UPDATE;
+
+@Immutable
+@NotNullByDefault
+class AvatarMessageEncoderImpl implements AvatarMessageEncoder {
+
+ private final ClientHelper clientHelper;
+ private final Clock clock;
+
+ @Inject
+ AvatarMessageEncoderImpl(ClientHelper clientHelper, Clock clock) {
+ this.clientHelper = clientHelper;
+ this.clock = clock;
+ }
+
+ @Override
+ public Pair encodeUpdateMessage(GroupId groupId,
+ long version, String contentType, InputStream in)
+ throws IOException {
+ // 0.0: Message Type, Version, Content-Type
+ BdfList list = BdfList.of(MSG_TYPE_UPDATE, version, contentType);
+ byte[] descriptor = clientHelper.toByteArray(list);
+
+ // add BdfList and stream content to body
+ ByteArrayOutputStream bodyOut = new ByteArrayOutputStream();
+ bodyOut.write(descriptor);
+ copyAndClose(in, bodyOut);
+ if (bodyOut.size() > MAX_MESSAGE_BODY_LENGTH)
+ throw new FileTooBigException();
+
+ // assemble message
+ byte[] body = bodyOut.toByteArray();
+ long timestamp = clock.currentTimeMillis();
+ Message m = clientHelper.createMessage(groupId, timestamp, body);
+
+ // encode metadata
+ BdfDictionary meta = new BdfDictionary();
+ meta.put(MSG_KEY_VERSION, version);
+ meta.put(MSG_KEY_CONTENT_TYPE, contentType);
+ meta.put(MSG_KEY_DESCRIPTOR_LENGTH, descriptor.length);
+
+ return new Pair<>(m, meta);
+ }
+
+}
diff --git a/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarModule.java b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarModule.java
new file mode 100644
index 000000000..86c4696e2
--- /dev/null
+++ b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarModule.java
@@ -0,0 +1,69 @@
+package org.briarproject.briar.avatar;
+
+import org.briarproject.bramble.api.contact.ContactManager;
+import org.briarproject.bramble.api.data.BdfReaderFactory;
+import org.briarproject.bramble.api.data.MetadataEncoder;
+import org.briarproject.bramble.api.lifecycle.LifecycleManager;
+import org.briarproject.bramble.api.sync.validation.ValidationManager;
+import org.briarproject.bramble.api.system.Clock;
+import org.briarproject.bramble.api.versioning.ClientVersioningManager;
+import org.briarproject.briar.api.avatar.AvatarManager;
+import org.briarproject.briar.api.avatar.AvatarMessageEncoder;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+import static org.briarproject.briar.api.avatar.AvatarManager.CLIENT_ID;
+import static org.briarproject.briar.api.avatar.AvatarManager.MAJOR_VERSION;
+import static org.briarproject.briar.api.avatar.AvatarManager.MINOR_VERSION;
+
+@Module
+public class AvatarModule {
+
+ public static class EagerSingletons {
+ @Inject
+ AvatarValidator avatarValidator;
+ @Inject
+ AvatarManager avatarManager;
+ }
+
+ @Provides
+ @Singleton
+ AvatarValidator provideAvatarValidator(ValidationManager validationManager,
+ BdfReaderFactory bdfReaderFactory, MetadataEncoder metadataEncoder,
+ Clock clock) {
+ AvatarValidator avatarValidator =
+ new AvatarValidator(bdfReaderFactory, metadataEncoder, clock);
+ validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
+ avatarValidator);
+ return avatarValidator;
+ }
+
+ @Provides
+ @Singleton
+ AvatarMessageEncoder provideMessageEncoder(
+ AvatarMessageEncoderImpl messageEncoder) {
+ return messageEncoder;
+ }
+
+ @Provides
+ @Singleton
+ AvatarManager provideAvatarManager(
+ LifecycleManager lifecycleManager,
+ ContactManager contactManager,
+ ValidationManager validationManager,
+ ClientVersioningManager clientVersioningManager,
+ AvatarManagerImpl avatarManager) {
+ lifecycleManager.registerOpenDatabaseHook(avatarManager);
+ contactManager.registerContactHook(avatarManager);
+ validationManager.registerIncomingMessageHook(CLIENT_ID,
+ MAJOR_VERSION, avatarManager);
+ clientVersioningManager.registerClient(CLIENT_ID,
+ MAJOR_VERSION, MINOR_VERSION, avatarManager);
+ return avatarManager;
+ }
+
+}
diff --git a/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarValidator.java b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarValidator.java
new file mode 100644
index 000000000..9f0b6c532
--- /dev/null
+++ b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarValidator.java
@@ -0,0 +1,96 @@
+package org.briarproject.briar.avatar;
+
+import org.briarproject.bramble.api.FormatException;
+import org.briarproject.bramble.api.data.BdfDictionary;
+import org.briarproject.bramble.api.data.BdfList;
+import org.briarproject.bramble.api.data.BdfReader;
+import org.briarproject.bramble.api.data.BdfReaderFactory;
+import org.briarproject.bramble.api.data.MetadataEncoder;
+import org.briarproject.bramble.api.db.Metadata;
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.bramble.api.sync.Group;
+import org.briarproject.bramble.api.sync.InvalidMessageException;
+import org.briarproject.bramble.api.sync.Message;
+import org.briarproject.bramble.api.sync.MessageContext;
+import org.briarproject.bramble.api.sync.validation.MessageValidator;
+import org.briarproject.bramble.api.system.Clock;
+import org.briarproject.briar.attachment.CountingInputStream;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.annotation.concurrent.Immutable;
+
+import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
+import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
+import static org.briarproject.bramble.util.ValidationUtils.checkLength;
+import static org.briarproject.bramble.util.ValidationUtils.checkSize;
+import static org.briarproject.briar.api.attachment.MediaConstants.MAX_CONTENT_TYPE_BYTES;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_CONTENT_TYPE;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH;
+import static org.briarproject.briar.avatar.AvatarConstants.MSG_KEY_VERSION;
+import static org.briarproject.briar.avatar.AvatarConstants.MSG_TYPE_UPDATE;
+
+@Immutable
+@NotNullByDefault
+class AvatarValidator implements MessageValidator {
+
+ private final BdfReaderFactory bdfReaderFactory;
+ private final MetadataEncoder metadataEncoder;
+ private final Clock clock;
+
+ AvatarValidator(BdfReaderFactory bdfReaderFactory,
+ MetadataEncoder metadataEncoder, Clock clock) {
+ this.bdfReaderFactory = bdfReaderFactory;
+ this.metadataEncoder = metadataEncoder;
+ this.clock = clock;
+ }
+
+ @Override
+ public MessageContext validateMessage(Message m, Group g)
+ throws InvalidMessageException {
+ // Reject the message if it's too far in the future
+ long now = clock.currentTimeMillis();
+ if (m.getTimestamp() - now > MAX_CLOCK_DIFFERENCE) {
+ throw new InvalidMessageException(
+ "Timestamp is too far in the future");
+ }
+ try {
+ InputStream in = new ByteArrayInputStream(m.getBody());
+ CountingInputStream countIn =
+ new CountingInputStream(in, MAX_MESSAGE_BODY_LENGTH);
+ BdfReader reader = bdfReaderFactory.createReader(countIn);
+ BdfList list = reader.readList();
+ long bytesRead = countIn.getBytesRead();
+ BdfDictionary d = validateUpdate(list, bytesRead);
+ Metadata meta = metadataEncoder.encode(d);
+ return new MessageContext(meta);
+ } catch (IOException e) {
+ throw new InvalidMessageException(e);
+ }
+ }
+
+ private BdfDictionary validateUpdate(BdfList body, long descriptorLength)
+ throws FormatException {
+ // 0.0: Message Type, Version, Content-Type
+ checkSize(body, 3);
+ // Message Type
+ long messageType = body.getLong(0);
+ if (messageType != MSG_TYPE_UPDATE) throw new FormatException();
+ // Version
+ long version = body.getLong(1);
+ if (version < 0) throw new FormatException();
+ // Content-Type
+ String contentType = body.getString(2);
+ checkLength(contentType, 1, MAX_CONTENT_TYPE_BYTES);
+
+ // Return the metadata
+ BdfDictionary meta = new BdfDictionary();
+ meta.put(MSG_KEY_VERSION, version);
+ meta.put(MSG_KEY_CONTENT_TYPE, contentType);
+ meta.put(MSG_KEY_DESCRIPTOR_LENGTH, descriptorLength);
+ return meta;
+ }
+
+}
diff --git a/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java
index 009b5030d..cd6f2f428 100644
--- a/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java
@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.BdfIncomingMessageHook;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.Contact;
-import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.ContactManager.ContactHook;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
@@ -15,7 +14,6 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
-import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
@@ -33,6 +31,8 @@ import org.briarproject.briar.api.blog.BlogPostFactory;
import org.briarproject.briar.api.blog.BlogPostHeader;
import org.briarproject.briar.api.blog.MessageType;
import org.briarproject.briar.api.blog.event.BlogPostAddedEvent;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
@@ -50,7 +50,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.Nullable;
import javax.inject.Inject;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.NONE;
import static org.briarproject.briar.api.blog.BlogConstants.KEY_AUTHOR;
import static org.briarproject.briar.api.blog.BlogConstants.KEY_COMMENT;
import static org.briarproject.briar.api.blog.BlogConstants.KEY_ORIGINAL_MSG_ID;
@@ -65,25 +64,26 @@ import static org.briarproject.briar.api.blog.MessageType.COMMENT;
import static org.briarproject.briar.api.blog.MessageType.POST;
import static org.briarproject.briar.api.blog.MessageType.WRAPPED_COMMENT;
import static org.briarproject.briar.api.blog.MessageType.WRAPPED_POST;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.NONE;
@NotNullByDefault
class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
OpenDatabaseHook, ContactHook {
- private final ContactManager contactManager;
private final IdentityManager identityManager;
+ private final AuthorManager authorManager;
private final BlogFactory blogFactory;
private final BlogPostFactory blogPostFactory;
private final List removeHooks;
@Inject
- BlogManagerImpl(DatabaseComponent db, ContactManager contactManager,
- IdentityManager identityManager, ClientHelper clientHelper,
+ BlogManagerImpl(DatabaseComponent db, IdentityManager identityManager,
+ AuthorManager authorManager, ClientHelper clientHelper,
MetadataParser metadataParser, BlogFactory blogFactory,
BlogPostFactory blogPostFactory) {
super(db, clientHelper, metadataParser);
- this.contactManager = contactManager;
this.identityManager = identityManager;
+ this.authorManager = authorManager;
this.blogFactory = blogFactory;
this.blogPostFactory = blogPostFactory;
removeHooks = new CopyOnWriteArrayList<>();
@@ -519,7 +519,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
Map authorInfos = new HashMap<>();
for (AuthorId authorId : authors) {
authorInfos.put(authorId,
- contactManager.getAuthorInfo(txn, authorId));
+ authorManager.getAuthorInfo(txn, authorId));
}
// get post headers
for (Entry entry : metadata.entrySet()) {
@@ -586,7 +586,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
} else if (authorInfos.containsKey(author.getId())) {
authorInfo = authorInfos.get(author.getId());
} else {
- authorInfo = contactManager.getAuthorInfo(txn, author.getId());
+ authorInfo = authorManager.getAuthorInfo(txn, author.getId());
}
boolean read = meta.getBoolean(KEY_READ, false);
diff --git a/briar-core/src/main/java/org/briarproject/briar/forum/ForumManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/forum/ForumManagerImpl.java
index 17935ab7b..4e4a63af3 100644
--- a/briar-core/src/main/java/org/briarproject/briar/forum/ForumManagerImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/forum/ForumManagerImpl.java
@@ -3,7 +3,6 @@ package org.briarproject.briar.forum;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.BdfIncomingMessageHook;
import org.briarproject.bramble.api.client.ClientHelper;
-import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataParser;
@@ -12,7 +11,6 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
-import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
@@ -28,6 +26,8 @@ import org.briarproject.briar.api.forum.ForumPost;
import org.briarproject.briar.api.forum.ForumPostFactory;
import org.briarproject.briar.api.forum.ForumPostHeader;
import org.briarproject.briar.api.forum.event.ForumPostReceivedEvent;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
@@ -45,7 +45,6 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.briar.api.forum.ForumConstants.KEY_AUTHOR;
import static org.briarproject.briar.api.forum.ForumConstants.KEY_LOCAL;
import static org.briarproject.briar.api.forum.ForumConstants.KEY_PARENT;
@@ -56,19 +55,19 @@ import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ
@NotNullByDefault
class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
- private final ContactManager contactManager;
+ private final AuthorManager authorManager;
private final ForumFactory forumFactory;
private final ForumPostFactory forumPostFactory;
private final MessageTracker messageTracker;
private final List removeHooks;
@Inject
- ForumManagerImpl(DatabaseComponent db, ContactManager contactManager,
- ClientHelper clientHelper, MetadataParser metadataParser,
+ ForumManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
+ MetadataParser metadataParser, AuthorManager authorManager,
ForumFactory forumFactory, ForumPostFactory forumPostFactory,
MessageTracker messageTracker) {
super(db, clientHelper, metadataParser);
- this.contactManager = contactManager;
+ this.authorManager = authorManager;
this.forumFactory = forumFactory;
this.forumPostFactory = forumPostFactory;
this.messageTracker = messageTracker;
@@ -127,23 +126,27 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
@Override
public ForumPostHeader addLocalPost(ForumPost p) throws DbException {
- db.transaction(false, txn -> {
+ return db.transactionWithResult(false, txn -> {
try {
- BdfDictionary meta = new BdfDictionary();
- meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp());
- if (p.getParent() != null) meta.put(KEY_PARENT, p.getParent());
- Author a = p.getAuthor();
- meta.put(KEY_AUTHOR, clientHelper.toList(a));
- meta.put(KEY_LOCAL, true);
- meta.put(MSG_KEY_READ, true);
- clientHelper.addLocalMessage(txn, p.getMessage(), meta, true,
- false);
- messageTracker.trackOutgoingMessage(txn, p.getMessage());
+ return addLocalPost(txn, p);
} catch (FormatException e) {
throw new AssertionError(e);
}
});
- AuthorInfo authorInfo = new AuthorInfo(OURSELVES);
+ }
+
+ private ForumPostHeader addLocalPost(Transaction txn, ForumPost p)
+ throws DbException, FormatException {
+ BdfDictionary meta = new BdfDictionary();
+ meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp());
+ if (p.getParent() != null) meta.put(KEY_PARENT, p.getParent());
+ Author a = p.getAuthor();
+ meta.put(KEY_AUTHOR, clientHelper.toList(a));
+ meta.put(KEY_LOCAL, true);
+ meta.put(MSG_KEY_READ, true);
+ clientHelper.addLocalMessage(txn, p.getMessage(), meta, true, false);
+ messageTracker.trackOutgoingMessage(txn, p.getMessage());
+ AuthorInfo authorInfo = authorManager.getMyAuthorInfo(txn);
return new ForumPostHeader(p.getMessage().getId(), p.getParent(),
p.getMessage().getTimestamp(), p.getAuthor(), authorInfo, true);
}
@@ -213,7 +216,7 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
// get information for all authors
Map authorInfos = new HashMap<>();
for (AuthorId id : authors) {
- authorInfos.put(id, contactManager.getAuthorInfo(txn, id));
+ authorInfos.put(id, authorManager.getAuthorInfo(txn, id));
}
// Parse the metadata
for (Entry entry :
@@ -275,7 +278,7 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
Author author = clientHelper.parseAndValidateAuthor(authorList);
AuthorInfo authorInfo = authorInfos.get(author.getId());
if (authorInfo == null)
- authorInfo = contactManager.getAuthorInfo(txn, author.getId());
+ authorInfo = authorManager.getAuthorInfo(txn, author.getId());
boolean read = meta.getBoolean(MSG_KEY_READ);
return new ForumPostHeader(id, parentId, timestamp, author, authorInfo,
diff --git a/briar-core/src/main/java/org/briarproject/briar/identity/AuthorManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/identity/AuthorManagerImpl.java
new file mode 100644
index 000000000..7b1b64ec0
--- /dev/null
+++ b/briar-core/src/main/java/org/briarproject/briar/identity/AuthorManagerImpl.java
@@ -0,0 +1,84 @@
+package org.briarproject.briar.identity;
+
+import org.briarproject.bramble.api.contact.Contact;
+import org.briarproject.bramble.api.db.DatabaseComponent;
+import org.briarproject.bramble.api.db.DbException;
+import org.briarproject.bramble.api.db.Transaction;
+import org.briarproject.bramble.api.identity.AuthorId;
+import org.briarproject.bramble.api.identity.IdentityManager;
+import org.briarproject.bramble.api.identity.LocalAuthor;
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.briarproject.briar.api.avatar.AvatarManager;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
+
+import java.util.Collection;
+
+import javax.annotation.concurrent.ThreadSafe;
+import javax.inject.Inject;
+
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.OURSELVES;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.UNKNOWN;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.UNVERIFIED;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.VERIFIED;
+
+@ThreadSafe
+@NotNullByDefault
+class AuthorManagerImpl implements AuthorManager {
+
+ private final DatabaseComponent db;
+ private final IdentityManager identityManager;
+ private final AvatarManager avatarManager;
+
+ @Inject
+ AuthorManagerImpl(DatabaseComponent db, IdentityManager identityManager,
+ AvatarManager avatarManager) {
+ this.db = db;
+ this.identityManager = identityManager;
+ this.avatarManager = avatarManager;
+ }
+
+ @Override
+ public AuthorInfo getAuthorInfo(AuthorId a) throws DbException {
+ return db.transactionWithResult(true, txn -> getAuthorInfo(txn, a));
+ }
+
+ @Override
+ public AuthorInfo getAuthorInfo(Transaction txn, AuthorId authorId)
+ throws DbException {
+ LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
+ if (localAuthor.getId().equals(authorId)) return getMyAuthorInfo(txn);
+ Collection contacts = db.getContactsByAuthorId(txn, authorId);
+ if (contacts.isEmpty()) return new AuthorInfo(UNKNOWN);
+ if (contacts.size() > 1) throw new AssertionError();
+ Contact c = contacts.iterator().next();
+ return getAuthorInfo(txn, c);
+ }
+
+ @Override
+ public AuthorInfo getAuthorInfo(Contact c) throws DbException {
+ return db.transactionWithResult(true, txn -> getAuthorInfo(txn, c));
+ }
+
+ @Override
+ public AuthorInfo getAuthorInfo(Transaction txn, Contact c)
+ throws DbException {
+ AttachmentHeader avatar = avatarManager.getAvatarHeader(txn, c);
+ if (c.isVerified())
+ return new AuthorInfo(VERIFIED, c.getAlias(), avatar);
+ else return new AuthorInfo(UNVERIFIED, c.getAlias(), avatar);
+ }
+
+ @Override
+ public AuthorInfo getMyAuthorInfo() throws DbException {
+ return db.transactionWithResult(true, this::getMyAuthorInfo);
+ }
+
+ @Override
+ public AuthorInfo getMyAuthorInfo(Transaction txn) throws DbException {
+ AttachmentHeader avatar = avatarManager.getMyAvatarHeader(txn);
+ return new AuthorInfo(OURSELVES, null, avatar);
+ }
+
+}
diff --git a/briar-core/src/main/java/org/briarproject/briar/identity/IdentityModule.java b/briar-core/src/main/java/org/briarproject/briar/identity/IdentityModule.java
new file mode 100644
index 000000000..d0c4f2c85
--- /dev/null
+++ b/briar-core/src/main/java/org/briarproject/briar/identity/IdentityModule.java
@@ -0,0 +1,25 @@
+package org.briarproject.briar.identity;
+
+import org.briarproject.briar.api.identity.AuthorManager;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public class IdentityModule {
+
+ public static class EagerSingletons {
+ @Inject
+ AuthorManager authorManager;
+ }
+
+ @Provides
+ @Singleton
+ AuthorManager provideAuthorManager(AuthorManagerImpl authorManager) {
+ return authorManager;
+ }
+
+}
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java
index b84f62463..37b84e516 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/AbstractProtocolEngine.java
@@ -12,7 +12,6 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
-import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
@@ -22,6 +21,8 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.SessionId;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.introduction.IntroductionResponse;
import org.briarproject.briar.api.introduction.event.IntroductionResponseReceivedEvent;
@@ -48,6 +49,7 @@ abstract class AbstractProtocolEngine
protected final ContactGroupFactory contactGroupFactory;
protected final MessageTracker messageTracker;
protected final IdentityManager identityManager;
+ protected final AuthorManager authorManager;
protected final MessageParser messageParser;
protected final MessageEncoder messageEncoder;
protected final Clock clock;
@@ -59,6 +61,7 @@ abstract class AbstractProtocolEngine
ContactGroupFactory contactGroupFactory,
MessageTracker messageTracker,
IdentityManager identityManager,
+ AuthorManager authorManager,
MessageParser messageParser,
MessageEncoder messageEncoder,
Clock clock) {
@@ -68,6 +71,7 @@ abstract class AbstractProtocolEngine
this.contactGroupFactory = contactGroupFactory;
this.messageTracker = messageTracker;
this.identityManager = identityManager;
+ this.authorManager = authorManager;
this.messageParser = messageParser;
this.messageEncoder = messageEncoder;
this.clock = clock;
@@ -152,7 +156,7 @@ abstract class AbstractProtocolEngine
AuthorId localAuthorId = identityManager.getLocalAuthor(txn).getId();
Contact c = contactManager.getContact(txn, sender, localAuthorId);
AuthorInfo otherAuthorInfo =
- contactManager.getAuthorInfo(txn, otherAuthor.getId());
+ authorManager.getAuthorInfo(txn, otherAuthor.getId());
IntroductionResponse response =
new IntroductionResponse(m.getMessageId(), m.getGroupId(),
m.getTimestamp(), false, false, false, false,
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
index 33750f358..8d7dd4a46 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroduceeProtocolEngine.java
@@ -14,7 +14,6 @@ import org.briarproject.bramble.api.db.ContactExistsException;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
-import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -29,6 +28,8 @@ import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.client.SessionId;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.introduction.IntroductionRequest;
import org.briarproject.briar.api.introduction.event.IntroductionAbortedEvent;
import org.briarproject.briar.api.introduction.event.IntroductionRequestReceivedEvent;
@@ -71,6 +72,7 @@ class IntroduceeProtocolEngine
ContactGroupFactory contactGroupFactory,
MessageTracker messageTracker,
IdentityManager identityManager,
+ AuthorManager authorManager,
MessageParser messageParser,
MessageEncoder messageEncoder,
Clock clock,
@@ -78,8 +80,8 @@ class IntroduceeProtocolEngine
KeyManager keyManager,
TransportPropertyManager transportPropertyManager) {
super(db, clientHelper, contactManager, contactGroupFactory,
- messageTracker, identityManager, messageParser, messageEncoder,
- clock);
+ messageTracker, identityManager, authorManager, messageParser,
+ messageEncoder, clock);
this.crypto = crypto;
this.keyManager = keyManager;
this.transportPropertyManager = transportPropertyManager;
@@ -254,7 +256,7 @@ class IntroduceeProtocolEngine
Contact c = contactManager.getContact(txn, s.getIntroducer().getId(),
localAuthor.getId());
AuthorInfo authorInfo =
- contactManager.getAuthorInfo(txn, m.getAuthor().getId());
+ authorManager.getAuthorInfo(txn, m.getAuthor().getId());
IntroductionRequest request = new IntroductionRequest(m.getMessageId(),
m.getGroupId(), m.getTimestamp(), false, false, false, false,
s.getSessionId(), m.getAuthor(), m.getText(), false,
@@ -443,13 +445,11 @@ class IntroduceeProtocolEngine
s.getRemote().author.getId(), localAuthor.getId());
// add the keys to the new contact
- //noinspection ConstantConditions
keys = keyManager.addRotationKeys(txn, c.getId(),
new SecretKey(s.getMasterKey()), timestamp,
s.getLocal().alice, false);
// add signed transport properties for the contact
- //noinspection ConstantConditions
transportPropertyManager.addRemoteProperties(txn, c.getId(),
s.getRemote().transportProperties);
} catch (ContactExistsException e) {
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java
index fa468a939..39f2faf5a 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroducerProtocolEngine.java
@@ -15,6 +15,7 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException;
+import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.introduction.event.IntroductionAbortedEvent;
import org.briarproject.briar.introduction.IntroducerSession.Introducee;
@@ -48,12 +49,13 @@ class IntroducerProtocolEngine
ContactGroupFactory contactGroupFactory,
MessageTracker messageTracker,
IdentityManager identityManager,
+ AuthorManager authorManager,
MessageParser messageParser,
MessageEncoder messageEncoder,
Clock clock) {
super(db, clientHelper, contactManager, contactGroupFactory,
- messageTracker, identityManager, messageParser, messageEncoder,
- clock);
+ messageTracker, identityManager, authorManager, messageParser,
+ messageEncoder, clock);
}
@Override
diff --git a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java
index 2ee085969..dd88159c8 100644
--- a/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/introduction/IntroductionManagerImpl.java
@@ -16,7 +16,6 @@ import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
-import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
@@ -33,6 +32,8 @@ import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
import org.briarproject.briar.api.conversation.DeletionResult;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.introduction.IntroductionManager;
import org.briarproject.briar.api.introduction.IntroductionRequest;
import org.briarproject.briar.api.introduction.IntroductionResponse;
@@ -83,6 +84,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
private final IntroduceeProtocolEngine introduceeEngine;
private final IntroductionCrypto crypto;
private final IdentityManager identityManager;
+ private final AuthorManager authorManager;
private final Group localGroup;
@@ -101,7 +103,8 @@ class IntroductionManagerImpl extends ConversationClientImpl
IntroducerProtocolEngine introducerEngine,
IntroduceeProtocolEngine introduceeEngine,
IntroductionCrypto crypto,
- IdentityManager identityManager) {
+ IdentityManager identityManager,
+ AuthorManager authorManager) {
super(db, clientHelper, metadataParser, messageTracker);
this.clientVersioningManager = clientVersioningManager;
this.contactGroupFactory = contactGroupFactory;
@@ -113,6 +116,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
this.introduceeEngine = introduceeEngine;
this.crypto = crypto;
this.identityManager = identityManager;
+ this.authorManager = authorManager;
this.localGroup =
contactGroupFactory.createLocalGroup(CLIENT_ID, MAJOR_VERSION);
}
@@ -455,7 +459,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
Author author = rm.getAuthor();
AuthorInfo authorInfo = authorInfos.get(author.getId());
if (authorInfo == null) {
- authorInfo = contactManager.getAuthorInfo(txn, author.getId());
+ authorInfo = authorManager.getAuthorInfo(txn, author.getId());
authorInfos.put(author.getId(), authorInfo);
}
return new IntroductionRequest(m, contactGroupId, meta.getTimestamp(),
@@ -494,7 +498,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
} else throw new AssertionError();
AuthorInfo authorInfo = authorInfos.get(author.getId());
if (authorInfo == null) {
- authorInfo = contactManager.getAuthorInfo(txn, author.getId());
+ authorInfo = authorManager.getAuthorInfo(txn, author.getId());
authorInfos.put(author.getId(), authorInfo);
}
return new IntroductionResponse(m, contactGroupId, meta.getTimestamp(),
diff --git a/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingConstants.java b/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingConstants.java
index c9cb6b1eb..a280ba46a 100644
--- a/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingConstants.java
+++ b/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingConstants.java
@@ -9,8 +9,6 @@ interface MessagingConstants {
String MSG_KEY_TIMESTAMP = "timestamp";
String MSG_KEY_LOCAL = "local";
String MSG_KEY_MSG_TYPE = "messageType";
- String MSG_KEY_CONTENT_TYPE = "contentType";
- String MSG_KEY_DESCRIPTOR_LENGTH = "descriptorLength";
String MSG_KEY_HAS_TEXT = "hasText";
String MSG_KEY_ATTACHMENT_HEADERS = "attachmentHeaders";
}
diff --git a/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingManagerImpl.java
index c048e0034..0688ee14d 100644
--- a/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingManagerImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/messaging/MessagingManagerImpl.java
@@ -27,22 +27,19 @@ import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.briarproject.briar.api.attachment.FileTooBigException;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.conversation.ConversationManager.ConversationClient;
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
import org.briarproject.briar.api.conversation.DeletionResult;
-import org.briarproject.briar.api.messaging.Attachment;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
-import org.briarproject.briar.api.messaging.FileTooBigException;
-import org.briarproject.briar.api.messaging.InvalidAttachmentException;
import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessage;
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import org.briarproject.briar.api.messaging.event.AttachmentReceivedEvent;
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent;
-import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -60,13 +57,13 @@ import static java.util.Collections.emptyList;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_CONTENT_TYPE;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH;
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
import static org.briarproject.briar.messaging.MessageTypes.ATTACHMENT;
import static org.briarproject.briar.messaging.MessageTypes.PRIVATE_MESSAGE;
import static org.briarproject.briar.messaging.MessagingConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_ATTACHMENT_HEADERS;
-import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_CONTENT_TYPE;
-import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_DESCRIPTOR_LENGTH;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_HAS_TEXT;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_MSG_TYPE;
@@ -175,7 +172,7 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
} else if (messageType == PRIVATE_MESSAGE) {
boolean hasText = metaDict.getBoolean(MSG_KEY_HAS_TEXT);
List headers =
- parseAttachmentHeaders(metaDict);
+ parseAttachmentHeaders(m.getGroupId(), metaDict);
incomingPrivateMessage(txn, m, metaDict, hasText, headers);
} else if (messageType == ATTACHMENT) {
incomingAttachment(txn, m);
@@ -206,16 +203,17 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
messageTracker.trackIncomingMessage(txn, m);
}
- private List parseAttachmentHeaders(BdfDictionary meta)
+ private List parseAttachmentHeaders(GroupId g,
+ BdfDictionary meta)
throws FormatException {
BdfList attachmentHeaders = meta.getList(MSG_KEY_ATTACHMENT_HEADERS);
int length = attachmentHeaders.size();
List headers = new ArrayList<>(length);
for (int i = 0; i < length; i++) {
BdfList header = attachmentHeaders.getList(i);
- MessageId id = new MessageId(header.getRaw(0));
+ MessageId m = new MessageId(header.getRaw(0));
String contentType = header.getString(1);
- headers.add(new AttachmentHeader(id, contentType));
+ headers.add(new AttachmentHeader(g, m, contentType));
}
return headers;
}
@@ -283,7 +281,7 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
// Mark attachments as temporary, not shared until we're ready to send
db.transaction(false, txn ->
clientHelper.addLocalMessage(txn, m, meta, false, true));
- return new AttachmentHeader(m.getId(), contentType);
+ return new AttachmentHeader(groupId, m.getId(), contentType);
}
@Override
@@ -297,7 +295,7 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
try {
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g);
- return new ContactId(meta.getLong("contactId").intValue());
+ return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue());
} catch (FormatException e) {
throw new DbException(e);
}
@@ -307,7 +305,7 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
public ContactId getContactId(GroupId g) throws DbException {
try {
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(g);
- return new ContactId(meta.getLong("contactId").intValue());
+ return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue());
} catch (FormatException e) {
throw new DbException(e);
}
@@ -360,7 +358,7 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
boolean hasText = meta.getBoolean(MSG_KEY_HAS_TEXT);
headers.add(new PrivateMessageHeader(id, g, timestamp,
local, read, s.isSent(), s.isSeen(), hasText,
- parseAttachmentHeaders(meta)));
+ parseAttachmentHeaders(g, meta)));
}
} catch (FormatException e) {
throw new DbException(e);
@@ -400,27 +398,6 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
}
}
- @Override
- public Attachment getAttachment(AttachmentHeader h) throws DbException {
- // TODO: Support large messages
- MessageId m = h.getMessageId();
- byte[] body = clientHelper.getMessage(m).getBody();
- try {
- BdfDictionary meta = clientHelper.getMessageMetadataAsDictionary(m);
- Long messageType = meta.getOptionalLong(MSG_KEY_MSG_TYPE);
- if (messageType == null || messageType != ATTACHMENT)
- throw new InvalidAttachmentException();
- String contentType = meta.getString(MSG_KEY_CONTENT_TYPE);
- if (!contentType.equals(h.getContentType()))
- throw new InvalidAttachmentException();
- int offset = meta.getLong(MSG_KEY_DESCRIPTOR_LENGTH).intValue();
- return new Attachment(h, new ByteArrayInputStream(body, offset,
- body.length - offset));
- } catch (FormatException e) {
- throw new DbException(e);
- }
- }
-
@Override
public boolean contactSupportsImages(Transaction txn, ContactId c)
throws DbException {
@@ -448,6 +425,7 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
public DeletionResult deleteMessages(Transaction txn, ContactId c,
Set messageIds) throws DbException {
DeletionResult result = new DeletionResult();
+ GroupId g = getContactGroup(db.getContact(txn, c)).getId();
for (MessageId m : messageIds) {
// get attachment headers
List headers;
@@ -458,7 +436,7 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
if (messageType != null && messageType != PRIVATE_MESSAGE)
throw new AssertionError("not supported");
headers = messageType == null ? emptyList() :
- parseAttachmentHeaders(meta);
+ parseAttachmentHeaders(g, meta);
} catch (FormatException e) {
throw new DbException(e);
}
@@ -484,7 +462,6 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
result.addNotFullyDownloaded();
}
}
- GroupId g = getContactGroup(db.getContact(txn, c)).getId();
recalculateGroupCount(txn, g);
return result;
}
diff --git a/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageFactoryImpl.java b/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageFactoryImpl.java
index 467540a3c..c4b7f54f7 100644
--- a/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageFactoryImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageFactoryImpl.java
@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import org.briarproject.briar.api.messaging.PrivateMessage;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
diff --git a/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageValidator.java b/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageValidator.java
index 0b4909bf8..af735c73c 100644
--- a/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageValidator.java
+++ b/briar-core/src/main/java/org/briarproject/briar/messaging/PrivateMessageValidator.java
@@ -16,6 +16,7 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageContext;
import org.briarproject.bramble.api.sync.validation.MessageValidator;
import org.briarproject.bramble.api.system.Clock;
+import org.briarproject.briar.attachment.CountingInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -27,15 +28,15 @@ import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_L
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
+import static org.briarproject.briar.api.attachment.MediaConstants.MAX_CONTENT_TYPE_BYTES;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_CONTENT_TYPE;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE;
-import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_BYTES;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH;
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
import static org.briarproject.briar.messaging.MessageTypes.ATTACHMENT;
import static org.briarproject.briar.messaging.MessageTypes.PRIVATE_MESSAGE;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_ATTACHMENT_HEADERS;
-import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_CONTENT_TYPE;
-import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_DESCRIPTOR_LENGTH;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_HAS_TEXT;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_MSG_TYPE;
diff --git a/briar-core/src/main/java/org/briarproject/briar/privategroup/PrivateGroupManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/privategroup/PrivateGroupManagerImpl.java
index 72d1ae12e..7bf044347 100644
--- a/briar-core/src/main/java/org/briarproject/briar/privategroup/PrivateGroupManagerImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/privategroup/PrivateGroupManagerImpl.java
@@ -15,8 +15,6 @@ import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
-import org.briarproject.bramble.api.identity.AuthorInfo;
-import org.briarproject.bramble.api.identity.AuthorInfo.Status;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -27,6 +25,9 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.client.ProtocolStateException;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorInfo.Status;
+import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.api.privategroup.GroupMember;
import org.briarproject.briar.api.privategroup.GroupMessage;
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
@@ -54,9 +55,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.UNVERIFIED;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.VERIFIED;
import static org.briarproject.briar.api.privategroup.MessageType.JOIN;
import static org.briarproject.briar.api.privategroup.MessageType.POST;
import static org.briarproject.briar.api.privategroup.Visibility.INVISIBLE;
@@ -84,6 +84,7 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
private final PrivateGroupFactory privateGroupFactory;
private final ContactManager contactManager;
private final IdentityManager identityManager;
+ private final AuthorManager authorManager;
private final MessageTracker messageTracker;
private final List hooks;
@@ -92,11 +93,12 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
MetadataParser metadataParser, DatabaseComponent db,
PrivateGroupFactory privateGroupFactory,
ContactManager contactManager, IdentityManager identityManager,
- MessageTracker messageTracker) {
+ AuthorManager authorManager, MessageTracker messageTracker) {
super(db, clientHelper, metadataParser);
this.privateGroupFactory = privateGroupFactory;
this.contactManager = contactManager;
this.identityManager = identityManager;
+ this.authorManager = authorManager;
this.messageTracker = messageTracker;
hooks = new CopyOnWriteArrayList<>();
}
@@ -208,32 +210,31 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
@Override
public GroupMessageHeader addLocalMessage(GroupMessage m)
throws DbException {
- Transaction txn = db.startTransaction(false);
- try {
- // store message and metadata
- BdfDictionary meta = new BdfDictionary();
- meta.put(KEY_TYPE, POST.getInt());
- if (m.getParent() != null)
- meta.put(KEY_PARENT_MSG_ID, m.getParent());
- addMessageMetadata(meta, m);
- GroupId g = m.getMessage().getGroupId();
- clientHelper.addLocalMessage(txn, m.getMessage(), meta, true,
- false);
+ return db.transactionWithResult(false, txn -> {
+ try {
+ return addLocalMessage(txn, m);
+ } catch (FormatException e) {
+ throw new DbException(e);
+ }
+ });
+ }
- // track message
- setPreviousMsgId(txn, g, m.getMessage().getId());
- messageTracker.trackOutgoingMessage(txn, m.getMessage());
-
- // broadcast event
- attachGroupMessageAddedEvent(txn, m.getMessage(), meta, true);
-
- db.commitTransaction(txn);
- } catch (FormatException e) {
- throw new DbException(e);
- } finally {
- db.endTransaction(txn);
- }
- AuthorInfo authorInfo = new AuthorInfo(OURSELVES);
+ private GroupMessageHeader addLocalMessage(Transaction txn, GroupMessage m)
+ throws DbException, FormatException {
+ // store message and metadata
+ BdfDictionary meta = new BdfDictionary();
+ meta.put(KEY_TYPE, POST.getInt());
+ if (m.getParent() != null)
+ meta.put(KEY_PARENT_MSG_ID, m.getParent());
+ addMessageMetadata(meta, m);
+ GroupId g = m.getMessage().getGroupId();
+ clientHelper.addLocalMessage(txn, m.getMessage(), meta, true, false);
+ // track message
+ setPreviousMsgId(txn, g, m.getMessage().getId());
+ messageTracker.trackOutgoingMessage(txn, m.getMessage());
+ // broadcast event
+ attachGroupMessageAddedEvent(txn, m.getMessage(), meta, true);
+ AuthorInfo authorInfo = authorManager.getMyAuthorInfo(txn);
return new GroupMessageHeader(m.getMessage().getGroupId(),
m.getMessage().getId(), m.getParent(),
m.getMessage().getTimestamp(), m.getMember(), authorInfo, true);
@@ -336,7 +337,7 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
// get information for all authors
Map authorInfos = new HashMap<>();
for (AuthorId id : authors) {
- authorInfos.put(id, contactManager.getAuthorInfo(txn, id));
+ authorInfos.put(id, authorManager.getAuthorInfo(txn, id));
}
// get current visibilities for join messages
Map visibilities = getMembers(txn, g);
@@ -378,7 +379,7 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
if (authorInfos.containsKey(member.getId())) {
authorInfo = authorInfos.get(member.getId());
} else {
- authorInfo = contactManager.getAuthorInfo(txn, member.getId());
+ authorInfo = authorManager.getAuthorInfo(txn, member.getId());
}
boolean read = meta.getBoolean(KEY_READ);
@@ -408,7 +409,7 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
for (Entry m : authors.entrySet()) {
Author a = m.getKey();
AuthorInfo authorInfo =
- contactManager.getAuthorInfo(txn, a.getId());
+ authorManager.getAuthorInfo(txn, a.getId());
Status status = authorInfo.getStatus();
Visibility v = m.getValue();
ContactId c = null;
diff --git a/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java b/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java
index ebd45cfcb..e773d3195 100644
--- a/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java
@@ -1,15 +1,12 @@
package org.briarproject.briar.test;
import org.briarproject.bramble.api.FormatException;
-import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.SecretKey;
-import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
-import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
@@ -23,22 +20,28 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.Group;
+import org.briarproject.bramble.api.sync.GroupFactory;
import org.briarproject.bramble.api.sync.GroupId;
+import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
+import org.briarproject.briar.api.avatar.AvatarManager;
+import org.briarproject.briar.api.avatar.AvatarMessageEncoder;
import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogPost;
import org.briarproject.briar.api.blog.BlogPostFactory;
-import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumManager;
import org.briarproject.briar.api.forum.ForumPost;
import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessage;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
+import org.briarproject.briar.api.test.TestAvatarCreator;
import org.briarproject.briar.api.test.TestDataCreator;
+import java.io.IOException;
+import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -54,8 +57,10 @@ import javax.inject.Inject;
import static java.util.Collections.emptyList;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
+import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
+import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.test.TestData.AUTHOR_NAMES;
import static org.briarproject.briar.test.TestData.GROUP_NAMES;
@@ -64,13 +69,12 @@ import static org.briarproject.briar.test.TestData.GROUP_NAMES;
public class TestDataCreatorImpl implements TestDataCreator {
private final Logger LOG =
- Logger.getLogger(TestDataCreatorImpl.class.getName());
+ getLogger(TestDataCreatorImpl.class.getName());
private final AuthorFactory authorFactory;
private final Clock clock;
+ private final GroupFactory groupFactory;
private final PrivateMessageFactory privateMessageFactory;
- private final ClientHelper clientHelper;
- private final MessageTracker messageTracker;
private final BlogPostFactory blogPostFactory;
private final DatabaseComponent db;
@@ -80,6 +84,8 @@ public class TestDataCreatorImpl implements TestDataCreator {
private final MessagingManager messagingManager;
private final BlogManager blogManager;
private final ForumManager forumManager;
+ private final TestAvatarCreator testAvatarCreator;
+ private final AvatarMessageEncoder avatarMessageEncoder;
@IoExecutor
private final Executor ioExecutor;
@@ -89,18 +95,20 @@ public class TestDataCreatorImpl implements TestDataCreator {
@Inject
TestDataCreatorImpl(AuthorFactory authorFactory, Clock clock,
+ GroupFactory groupFactory,
PrivateMessageFactory privateMessageFactory,
- ClientHelper clientHelper, MessageTracker messageTracker,
BlogPostFactory blogPostFactory, DatabaseComponent db,
IdentityManager identityManager, ContactManager contactManager,
TransportPropertyManager transportPropertyManager,
MessagingManager messagingManager, BlogManager blogManager,
- ForumManager forumManager, @IoExecutor Executor ioExecutor) {
+ ForumManager forumManager,
+ TestAvatarCreator testAvatarCreator,
+ AvatarMessageEncoder avatarMessageEncoder,
+ @IoExecutor Executor ioExecutor) {
this.authorFactory = authorFactory;
this.clock = clock;
+ this.groupFactory = groupFactory;
this.privateMessageFactory = privateMessageFactory;
- this.clientHelper = clientHelper;
- this.messageTracker = messageTracker;
this.blogPostFactory = blogPostFactory;
this.db = db;
this.identityManager = identityManager;
@@ -109,52 +117,56 @@ public class TestDataCreatorImpl implements TestDataCreator {
this.messagingManager = messagingManager;
this.blogManager = blogManager;
this.forumManager = forumManager;
+ this.testAvatarCreator = testAvatarCreator;
+ this.avatarMessageEncoder = avatarMessageEncoder;
this.ioExecutor = ioExecutor;
}
@Override
public void createTestData(int numContacts, int numPrivateMsgs,
- int numBlogPosts, int numForums, int numForumPosts) {
- if (numContacts == 0) throw new IllegalArgumentException(
- "Number of contacts must be >= 1");
+ int avatarPercent, int numBlogPosts, int numForums,
+ int numForumPosts) {
+ if (numContacts == 0) throw new IllegalArgumentException();
+ if (avatarPercent < 0 || avatarPercent > 100)
+ throw new IllegalArgumentException();
ioExecutor.execute(() -> {
try {
createTestDataOnIoExecutor(numContacts, numPrivateMsgs,
- numBlogPosts, numForums, numForumPosts);
+ avatarPercent, numBlogPosts, numForums, numForumPosts);
} catch (DbException e) {
- LOG.log(WARNING, "Creating test data failed", e);
+ logException(LOG, WARNING, e);
}
});
}
@IoExecutor
private void createTestDataOnIoExecutor(int numContacts, int numPrivateMsgs,
- int numBlogPosts, int numForums, int numForumPosts)
- throws DbException {
- List contacts = createContacts(numContacts);
+ int avatarPercent, int numBlogPosts, int numForums,
+ int numForumPosts) throws DbException {
+ List contacts = createContacts(numContacts, avatarPercent);
createPrivateMessages(contacts, numPrivateMsgs);
createBlogPosts(contacts, numBlogPosts);
- List forums = createForums(contacts, numForums, numForumPosts);
-
+ List forums = createForums(contacts, numForums);
for (Forum forum : forums) {
createRandomForumPosts(forum, contacts, numForumPosts);
}
}
- private List createContacts(int numContacts) throws DbException {
+ private List createContacts(int numContacts, int avatarPercent)
+ throws DbException {
List contacts = new ArrayList<>(numContacts);
LocalAuthor localAuthor = identityManager.getLocalAuthor();
for (int i = 0; i < numContacts; i++) {
LocalAuthor remote = getRandomAuthor();
- Contact contact = addContact(localAuthor.getId(), remote);
+ Contact contact =
+ addContact(localAuthor.getId(), remote, avatarPercent);
contacts.add(contact);
}
return contacts;
}
- private Contact addContact(AuthorId localAuthorId, LocalAuthor remote)
- throws DbException {
-
+ private Contact addContact(AuthorId localAuthorId, LocalAuthor remote,
+ int avatarPercent) throws DbException {
// prepare to add contact
SecretKey secretKey = getSecretKey();
long timestamp = clock.currentTimeMillis();
@@ -164,36 +176,32 @@ public class TestDataCreatorImpl implements TestDataCreator {
Map props =
getRandomTransportProperties();
- Contact contact;
- Transaction txn = db.startTransaction(false);
- try {
- ContactId contactId = contactManager
- .addContact(txn, remote, localAuthorId, secretKey,
- timestamp, true, verified, true);
+ Contact contact = db.transactionWithResult(false, txn -> {
+ ContactId contactId = contactManager.addContact(txn, remote,
+ localAuthorId, secretKey, timestamp, true, verified, true);
if (random.nextBoolean()) {
- contactManager
- .setContactAlias(txn, contactId, getRandomAuthorName());
+ contactManager.setContactAlias(txn, contactId,
+ getRandomAuthorName());
}
transportPropertyManager.addRemoteProperties(txn, contactId, props);
- contact = db.getContact(txn, contactId);
- db.commitTransaction(txn);
- } finally {
- db.endTransaction(txn);
- }
+ return db.getContact(txn, contactId);
+ });
+ if (random.nextInt(100) + 1 <= avatarPercent) addAvatar(contact);
if (LOG.isLoggable(INFO)) {
- LOG.info("Added contact " + remote.getName());
- LOG.info("with transport properties: " + props.toString());
+ LOG.info("Added contact " + remote.getName() +
+ " with transport properties: " + props);
}
localAuthors.put(contact, remote);
return contact;
}
@Override
- public Contact addContact(String name) throws DbException {
+ public Contact addContact(String name, boolean avatar) throws DbException {
LocalAuthor localAuthor = identityManager.getLocalAuthor();
LocalAuthor remote = authorFactory.createLocalAuthor(name);
- return addContact(localAuthor.getId(), remote);
+ int avatarPercent = avatar ? 100 : 0;
+ return addContact(localAuthor.getId(), remote, avatarPercent);
}
private String getRandomAuthorName() {
@@ -213,7 +221,6 @@ public class TestDataCreatorImpl implements TestDataCreator {
private Map getRandomTransportProperties() {
Map props = new HashMap<>();
-
// Bluetooth
TransportProperties bt = new TransportProperties();
String btAddress = getRandomBluetoothAddress();
@@ -267,13 +274,11 @@ public class TestDataCreatorImpl implements TestDataCreator {
if (random.nextInt(5) == 0) {
sb.append("10.");
sb.append(random.nextInt(2)).append('.');
- sb.append(random.nextInt(2)).append('.');
- sb.append(random.nextInt(255));
} else {
sb.append("192.168.");
- sb.append(random.nextInt(2)).append('.');
- sb.append(random.nextInt(255));
}
+ sb.append(random.nextInt(2)).append('.');
+ sb.append(random.nextInt(255));
// port
sb.append(':').append(getRandomPortNumber());
return sb.toString();
@@ -293,16 +298,46 @@ public class TestDataCreatorImpl implements TestDataCreator {
return sb.toString();
}
+ private void addAvatar(Contact c) throws DbException {
+ AuthorId authorId = c.getAuthor().getId();
+ GroupId groupId = groupFactory.createGroup(AvatarManager.CLIENT_ID,
+ AvatarManager.MAJOR_VERSION, authorId.getBytes()).getId();
+ InputStream is;
+ try {
+ is = testAvatarCreator.getAvatarInputStream();
+ } catch (IOException e) {
+ logException(LOG, WARNING, e);
+ return;
+ }
+ if (is == null) return;
+ Message m;
+ try {
+ m = avatarMessageEncoder.encodeUpdateMessage(groupId, 0,
+ "image/jpeg", is).getFirst();
+ } catch (IOException e) {
+ throw new DbException(e);
+ }
+ db.transaction(false, txn -> {
+ // TODO: Do this properly via clients without breaking encapsulation
+ db.setGroupVisibility(txn, c.getId(), groupId, SHARED);
+ db.receiveMessage(txn, c.getId(), m);
+ });
+ }
+
+ // TODO: Do this properly via clients without breaking encapsulation
+ private void shareGroup(ContactId contactId, GroupId groupId)
+ throws DbException {
+ db.transaction(false, txn ->
+ db.setGroupVisibility(txn, contactId, groupId, SHARED));
+ }
+
private void createPrivateMessages(List contacts,
int numPrivateMsgs) throws DbException {
for (Contact contact : contacts) {
Group group = messagingManager.getContactGroup(contact);
+ shareGroup(contact.getId(), group.getId());
for (int i = 0; i < numPrivateMsgs; i++) {
- try {
- createRandomPrivateMessage(group.getId(), i);
- } catch (FormatException e) {
- throw new RuntimeException(e);
- }
+ createRandomPrivateMessage(contact.getId(), group.getId(), i);
}
}
if (LOG.isLoggable(INFO)) {
@@ -311,62 +346,66 @@ public class TestDataCreatorImpl implements TestDataCreator {
}
}
- private void createRandomPrivateMessage(GroupId groupId, int num)
- throws DbException, FormatException {
+ private void createRandomPrivateMessage(ContactId contactId,
+ GroupId groupId, int num) throws DbException {
long timestamp = clock.currentTimeMillis() - num * 60 * 1000;
String text = getRandomText();
boolean local = random.nextBoolean();
- createPrivateMessage(groupId, text, timestamp, local);
+ createPrivateMessage(contactId, groupId, text, timestamp, local);
}
- private void createPrivateMessage(GroupId groupId, String text,
- long timestamp, boolean local) throws DbException, FormatException {
- PrivateMessage m = privateMessageFactory
- .createPrivateMessage(groupId, timestamp, text, emptyList());
- BdfDictionary meta = new BdfDictionary();
- meta.put("timestamp", timestamp);
- meta.put("local", local);
- meta.put("read", local); // all local messages are read
-
- Transaction txn = db.startTransaction(false);
+ private void createPrivateMessage(ContactId contactId, GroupId groupId,
+ String text, long timestamp, boolean local) throws DbException {
try {
- clientHelper.addLocalMessage(txn, m.getMessage(), meta, true,
- false);
- if (local) messageTracker.trackOutgoingMessage(txn, m.getMessage());
- else messageTracker.trackIncomingMessage(txn, m.getMessage());
- db.commitTransaction(txn);
- } finally {
- db.endTransaction(txn);
+ PrivateMessage m = privateMessageFactory.createPrivateMessage(
+ groupId, timestamp, text, emptyList());
+ if (local) {
+ messagingManager.addLocalMessage(m);
+ } else {
+ db.transaction(false, txn ->
+ db.receiveMessage(txn, contactId, m.getMessage()));
+ }
+ } catch (FormatException e) {
+ throw new AssertionError(e);
}
}
private void createBlogPosts(List contacts, int numBlogPosts)
throws DbException {
+ LocalAuthor localAuthor = identityManager.getLocalAuthor();
+ Blog ours = blogManager.getPersonalBlog(localAuthor);
+ for (Contact contact : contacts) {
+ Blog theirs = blogManager.getPersonalBlog(contact.getAuthor());
+ shareGroup(contact.getId(), ours.getId());
+ shareGroup(contact.getId(), theirs.getId());
+ }
for (int i = 0; i < numBlogPosts; i++) {
Contact contact = contacts.get(random.nextInt(contacts.size()));
LocalAuthor author = localAuthors.get(contact);
- addBlogPost(author, i);
+ addBlogPost(contact.getId(), author, i);
}
if (LOG.isLoggable(INFO)) {
LOG.info("Created " + numBlogPosts + " blog posts.");
}
}
- private void addBlogPost(LocalAuthor author, int num) throws DbException {
+ private void addBlogPost(ContactId contactId, LocalAuthor author, int num)
+ throws DbException {
Blog blog = blogManager.getPersonalBlog(author);
long timestamp = clock.currentTimeMillis() - num * 60 * 1000;
String text = getRandomText();
try {
BlogPost blogPost = blogPostFactory.createBlogPost(blog.getId(),
timestamp, null, author, text);
- blogManager.addLocalPost(blogPost);
+ db.transaction(false, txn ->
+ db.receiveMessage(txn, contactId, blogPost.getMessage()));
} catch (FormatException | GeneralSecurityException e) {
- throw new RuntimeException(e);
+ throw new AssertionError(e);
}
}
- private List createForums(List contacts, int numForums,
- int numForumPosts) throws DbException {
+ private List createForums(List contacts, int numForums)
+ throws DbException {
List forums = new ArrayList<>(numForums);
for (int i = 0; i < numForums; i++) {
// create forum
@@ -374,21 +413,13 @@ public class TestDataCreatorImpl implements TestDataCreator {
Forum forum = forumManager.addForum(name);
// share with all contacts
- Transaction txn = db.startTransaction(false);
- try {
- for (Contact c : contacts) {
- db.setGroupVisibility(txn, c.getId(), forum.getId(),
- SHARED);
- }
- db.commitTransaction(txn);
- } finally {
- db.endTransaction(txn);
+ for (Contact contact : contacts) {
+ shareGroup(contact.getId(), forum.getId());
}
forums.add(forum);
}
if (LOG.isLoggable(INFO)) {
- LOG.info("Created " + numForums + " forums with " +
- numForumPosts + " posts each.");
+ LOG.info("Created " + numForums + " forums.");
}
return forums;
}
@@ -403,18 +434,17 @@ public class TestDataCreatorImpl implements TestDataCreator {
String text = getRandomText();
MessageId parent = null;
if (random.nextBoolean() && posts.size() > 0) {
- ForumPost parentPost =
- posts.get(random.nextInt(posts.size()));
+ ForumPost parentPost = posts.get(random.nextInt(posts.size()));
parent = parentPost.getMessage().getId();
}
ForumPost post = forumManager.createLocalPost(forum.getId(), text,
timestamp, parent, author);
posts.add(post);
- forumManager.addLocalPost(post);
- if (random.nextBoolean()) {
- forumManager.setReadFlag(forum.getId(),
- post.getMessage().getId(), false);
- }
+ db.transaction(false, txn ->
+ db.receiveMessage(txn, contact.getId(), post.getMessage()));
+ }
+ if (LOG.isLoggable(INFO)) {
+ LOG.info("Created " + numForumPosts + " forum posts.");
}
}
diff --git a/briar-core/src/test/java/org/briarproject/briar/attachment/AttachmentReaderImplTest.java b/briar-core/src/test/java/org/briarproject/briar/attachment/AttachmentReaderImplTest.java
new file mode 100644
index 000000000..9d21221a0
--- /dev/null
+++ b/briar-core/src/test/java/org/briarproject/briar/attachment/AttachmentReaderImplTest.java
@@ -0,0 +1,113 @@
+package org.briarproject.briar.attachment;
+
+import org.briarproject.bramble.api.client.ClientHelper;
+import org.briarproject.bramble.api.data.BdfDictionary;
+import org.briarproject.bramble.api.data.BdfEntry;
+import org.briarproject.bramble.api.db.NoSuchMessageException;
+import org.briarproject.bramble.api.sync.GroupId;
+import org.briarproject.bramble.api.sync.Message;
+import org.briarproject.bramble.test.BrambleMockTestCase;
+import org.briarproject.briar.api.attachment.Attachment;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.jmock.Expectations;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+
+import static java.lang.System.arraycopy;
+import static org.briarproject.bramble.test.TestUtils.getMessage;
+import static org.briarproject.bramble.test.TestUtils.getRandomId;
+import static org.briarproject.bramble.util.IoUtils.copyAndClose;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_CONTENT_TYPE;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH;
+import static org.junit.Assert.assertArrayEquals;
+
+public class AttachmentReaderImplTest extends BrambleMockTestCase {
+
+ private final ClientHelper clientHelper = context.mock(ClientHelper.class);
+
+ private final GroupId groupId = new GroupId(getRandomId());
+ private final Message message = getMessage(groupId, 1234);
+ private final String contentType = "image/jpeg";
+ private final AttachmentHeader header = new AttachmentHeader(groupId,
+ message.getId(), contentType);
+
+ private final AttachmentReaderImpl attachmentReader =
+ new AttachmentReaderImpl(clientHelper);
+
+ @Test(expected = NoSuchMessageException.class)
+ public void testWrongGroup() throws Exception {
+ GroupId wrongGroupId = new GroupId(getRandomId());
+ AttachmentHeader wrongGroup = new AttachmentHeader(wrongGroupId,
+ message.getId(), contentType);
+
+ context.checking(new Expectations() {{
+ oneOf(clientHelper).getMessage(message.getId());
+ will(returnValue(message));
+ }});
+
+ attachmentReader.getAttachment(wrongGroup);
+ }
+
+ @Test(expected = NoSuchMessageException.class)
+ public void testMissingContentType() throws Exception {
+ BdfDictionary meta = new BdfDictionary();
+
+ testInvalidMetadata(meta);
+ }
+
+ @Test(expected = NoSuchMessageException.class)
+ public void testWrongContentType() throws Exception {
+ BdfDictionary meta = BdfDictionary.of(
+ new BdfEntry(MSG_KEY_CONTENT_TYPE, "image/png"));
+
+ testInvalidMetadata(meta);
+ }
+
+ @Test(expected = NoSuchMessageException.class)
+ public void testMissingDescriptorLength() throws Exception {
+ BdfDictionary meta = BdfDictionary.of(
+ new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType));
+
+ testInvalidMetadata(meta);
+ }
+
+ private void testInvalidMetadata(BdfDictionary meta) throws Exception {
+ context.checking(new Expectations() {{
+ oneOf(clientHelper).getMessage(message.getId());
+ will(returnValue(message));
+ oneOf(clientHelper).getMessageMetadataAsDictionary(message.getId());
+ will(returnValue(meta));
+ }});
+
+ attachmentReader.getAttachment(header);
+ }
+
+ @Test
+ public void testSkipsDescriptor() throws Exception {
+ int descriptorLength = 123;
+ BdfDictionary meta = BdfDictionary.of(
+ new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType),
+ new BdfEntry(MSG_KEY_DESCRIPTOR_LENGTH, descriptorLength));
+
+ byte[] body = message.getBody();
+ byte[] expectedData = new byte[body.length - descriptorLength];
+ arraycopy(body, descriptorLength, expectedData, 0, expectedData.length);
+
+ context.checking(new Expectations() {{
+ oneOf(clientHelper).getMessage(message.getId());
+ will(returnValue(message));
+ oneOf(clientHelper).getMessageMetadataAsDictionary(message.getId());
+ will(returnValue(meta));
+ }});
+
+ Attachment attachment = attachmentReader.getAttachment(header);
+ InputStream in = attachment.getStream();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ copyAndClose(in, out);
+ byte[] data = out.toByteArray();
+
+ assertArrayEquals(expectedData, data);
+ }
+}
diff --git a/briar-core/src/test/java/org/briarproject/briar/messaging/CountingInputStreamTest.java b/briar-core/src/test/java/org/briarproject/briar/attachment/CountingInputStreamTest.java
similarity index 99%
rename from briar-core/src/test/java/org/briarproject/briar/messaging/CountingInputStreamTest.java
rename to briar-core/src/test/java/org/briarproject/briar/attachment/CountingInputStreamTest.java
index 7c7d2b294..4bc3c8488 100644
--- a/briar-core/src/test/java/org/briarproject/briar/messaging/CountingInputStreamTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/attachment/CountingInputStreamTest.java
@@ -1,4 +1,4 @@
-package org.briarproject.briar.messaging;
+package org.briarproject.briar.attachment;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test;
diff --git a/briar-core/src/test/java/org/briarproject/briar/avatar/AvatarManagerImplTest.java b/briar-core/src/test/java/org/briarproject/briar/avatar/AvatarManagerImplTest.java
new file mode 100644
index 000000000..e8c32f13e
--- /dev/null
+++ b/briar-core/src/test/java/org/briarproject/briar/avatar/AvatarManagerImplTest.java
@@ -0,0 +1,417 @@
+package org.briarproject.briar.avatar;
+
+import org.briarproject.bramble.api.FormatException;
+import org.briarproject.bramble.api.Pair;
+import org.briarproject.bramble.api.client.ClientHelper;
+import org.briarproject.bramble.api.contact.Contact;
+import org.briarproject.bramble.api.contact.ContactId;
+import org.briarproject.bramble.api.data.BdfDictionary;
+import org.briarproject.bramble.api.data.BdfEntry;
+import org.briarproject.bramble.api.data.MetadataParser;
+import org.briarproject.bramble.api.db.DatabaseComponent;
+import org.briarproject.bramble.api.db.DbException;
+import org.briarproject.bramble.api.db.EventAction;
+import org.briarproject.bramble.api.db.Metadata;
+import org.briarproject.bramble.api.db.Transaction;
+import org.briarproject.bramble.api.event.Event;
+import org.briarproject.bramble.api.identity.AuthorId;
+import org.briarproject.bramble.api.identity.IdentityManager;
+import org.briarproject.bramble.api.identity.LocalAuthor;
+import org.briarproject.bramble.api.sync.Group;
+import org.briarproject.bramble.api.sync.Group.Visibility;
+import org.briarproject.bramble.api.sync.GroupFactory;
+import org.briarproject.bramble.api.sync.GroupId;
+import org.briarproject.bramble.api.sync.InvalidMessageException;
+import org.briarproject.bramble.api.sync.Message;
+import org.briarproject.bramble.api.sync.MessageId;
+import org.briarproject.bramble.api.versioning.ClientVersioningManager;
+import org.briarproject.bramble.test.BrambleMockTestCase;
+import org.briarproject.bramble.test.DbExpectations;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.briarproject.briar.api.avatar.AvatarMessageEncoder;
+import org.briarproject.briar.api.avatar.event.AvatarUpdatedEvent;
+import org.jmock.Expectations;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
+import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
+import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
+import static org.briarproject.bramble.test.TestUtils.getContact;
+import static org.briarproject.bramble.test.TestUtils.getGroup;
+import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
+import static org.briarproject.bramble.test.TestUtils.getMessage;
+import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
+import static org.briarproject.bramble.test.TestUtils.getRandomId;
+import static org.briarproject.bramble.util.StringUtils.getRandomString;
+import static org.briarproject.briar.api.attachment.MediaConstants.MAX_CONTENT_TYPE_BYTES;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_CONTENT_TYPE;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH;
+import static org.briarproject.briar.api.avatar.AvatarManager.CLIENT_ID;
+import static org.briarproject.briar.api.avatar.AvatarManager.MAJOR_VERSION;
+import static org.briarproject.briar.avatar.AvatarConstants.GROUP_KEY_CONTACT_ID;
+import static org.briarproject.briar.avatar.AvatarConstants.MSG_KEY_VERSION;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+public class AvatarManagerImplTest extends BrambleMockTestCase {
+
+ private final DatabaseComponent db = context.mock(DatabaseComponent.class);
+ private final IdentityManager identityManager =
+ context.mock(IdentityManager.class);
+ private final ClientHelper clientHelper = context.mock(ClientHelper.class);
+ private final ClientVersioningManager clientVersioningManager =
+ context.mock(ClientVersioningManager.class);
+ private final MetadataParser metadataParser =
+ context.mock(MetadataParser.class);
+ private final GroupFactory groupFactory = context.mock(GroupFactory.class);
+ private final AvatarMessageEncoder avatarMessageEncoder =
+ context.mock(AvatarMessageEncoder.class);
+
+ private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
+ private final GroupId localGroupId = localGroup.getId();
+ private final LocalAuthor localAuthor = getLocalAuthor();
+ private final Contact contact = getContact();
+ private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION, 32);
+ private final GroupId contactGroupId = contactGroup.getId();
+ private final Message ourMsg = getMessage(localGroupId);
+ private final Message contactMsg = getMessage(contactGroupId);
+ private final Metadata meta = new Metadata();
+ private final String contentType = getRandomString(MAX_CONTENT_TYPE_BYTES);
+ private final BdfDictionary metaDict = BdfDictionary.of(
+ new BdfEntry(MSG_KEY_VERSION, 1),
+ new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType)
+ );
+
+ private final AvatarManagerImpl avatarManager =
+ new AvatarManagerImpl(db, identityManager, clientHelper,
+ clientVersioningManager, metadataParser, groupFactory,
+ avatarMessageEncoder);
+
+ @Test
+ public void testOpenDatabaseHookWhenGroupExists() throws DbException {
+ Transaction txn = new Transaction(null, false);
+
+ // local group already exists, so nothing more to do
+ expectCreateGroup(localAuthor.getId(), localGroup);
+ context.checking(new Expectations() {{
+ oneOf(identityManager).getLocalAuthor(txn);
+ will(returnValue(localAuthor));
+ oneOf(db).containsGroup(txn, localGroupId);
+ will(returnValue(true));
+ }});
+ avatarManager.onDatabaseOpened(txn);
+ }
+
+ @Test
+ public void testOpenDatabaseHook() throws DbException, FormatException {
+ Transaction txn = new Transaction(null, false);
+
+ // local group does not exist, so we need to set things up for contacts
+ expectCreateGroup(localAuthor.getId(), localGroup);
+ context.checking(new Expectations() {{
+ oneOf(identityManager).getLocalAuthor(txn);
+ will(returnValue(localAuthor));
+ oneOf(db).containsGroup(txn, localGroupId);
+ will(returnValue(false));
+ oneOf(db).addGroup(txn, localGroup);
+ oneOf(db).getContacts(txn);
+ will(returnValue(Collections.singletonList(contact)));
+ }});
+ expectAddingContact(txn, contact, SHARED);
+ avatarManager.onDatabaseOpened(txn);
+ }
+
+ @Test
+ public void testAddingContact() throws DbException, FormatException {
+ Transaction txn = new Transaction(null, false);
+
+ expectAddingContact(txn, contact, INVISIBLE);
+ avatarManager.addingContact(txn, contact);
+
+ Contact contact2 = getContact();
+ expectAddingContact(txn, contact2, VISIBLE);
+ avatarManager.addingContact(txn, contact2);
+ }
+
+ @Test
+ public void testRemovingContact() throws DbException {
+ Transaction txn = new Transaction(null, false);
+
+ expectCreateGroup(contact.getAuthor().getId(), contactGroup);
+ context.checking(new Expectations() {{
+ oneOf(db).removeGroup(txn, contactGroup);
+ }});
+
+ avatarManager.removingContact(txn, contact);
+ }
+
+ @Test
+ public void testOnClientVisibilityChangingVisible() throws DbException {
+ testOnClientVisibilityChanging(VISIBLE);
+ }
+
+ @Test
+ public void testOnClientVisibilityChangingShared() throws DbException {
+ testOnClientVisibilityChanging(SHARED);
+ }
+
+ @Test
+ public void testOnClientVisibilityChangingInvisible() throws DbException {
+ testOnClientVisibilityChanging(INVISIBLE);
+ }
+
+ private void testOnClientVisibilityChanging(Visibility v)
+ throws DbException {
+ Transaction txn = new Transaction(null, false);
+
+ expectGetOurGroup(txn);
+ expectCreateGroup(contact.getAuthor().getId(), contactGroup);
+ expectSetGroupVisibility(txn, contact.getId(), localGroupId, v);
+ expectSetGroupVisibility(txn, contact.getId(), contactGroupId, v);
+ avatarManager.onClientVisibilityChanging(txn, contact, v);
+ }
+
+ @Test
+ public void testFirstIncomingMessage()
+ throws DbException, InvalidMessageException, FormatException {
+ Transaction txn = new Transaction(null, false);
+ BdfDictionary d = BdfDictionary.of(
+ new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType)
+ );
+
+ expectGetOurGroup(txn);
+ context.checking(new Expectations() {{
+ oneOf(metadataParser).parse(meta);
+ will(returnValue(d));
+ }});
+ expectFindLatest(txn, contactGroupId, new MessageId(getRandomId()),
+ null);
+ expectGetContactId(txn, contactGroupId, contact.getId());
+
+ assertFalse(avatarManager.incomingMessage(txn, contactMsg, meta));
+ assertEquals(1, txn.getActions().size());
+ Event event = ((EventAction) txn.getActions().get(0)).getEvent();
+ AvatarUpdatedEvent avatarUpdatedEvent = (AvatarUpdatedEvent) event;
+ assertEquals(contactMsg.getId(),
+ avatarUpdatedEvent.getAttachmentHeader().getMessageId());
+ assertEquals(contact.getId(), avatarUpdatedEvent.getContactId());
+ }
+
+ @Test
+ public void testNewerIncomingMessage()
+ throws DbException, InvalidMessageException, FormatException {
+ Transaction txn = new Transaction(null, false);
+ BdfDictionary d = BdfDictionary.of(
+ new BdfEntry(MSG_KEY_VERSION, 1),
+ new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType)
+ );
+ MessageId latestMsgId = new MessageId(getRandomId());
+ BdfDictionary latest = BdfDictionary.of(
+ new BdfEntry(MSG_KEY_VERSION, 0),
+ new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType)
+ );
+
+ expectGetOurGroup(txn);
+ context.checking(new Expectations() {{
+ oneOf(metadataParser).parse(meta);
+ will(returnValue(d));
+ // delete old "latest" message
+ oneOf(db).deleteMessage(txn, latestMsgId);
+ oneOf(db).deleteMessageMetadata(txn, latestMsgId);
+ }});
+ expectFindLatest(txn, contactGroupId, latestMsgId, latest);
+ expectGetContactId(txn, contactGroupId, contact.getId());
+
+ assertFalse(avatarManager.incomingMessage(txn, contactMsg, meta));
+
+ // event to broadcast
+ assertEquals(1, txn.getActions().size());
+ }
+
+ @Test
+ public void testDeleteOlderIncomingMessage()
+ throws DbException, InvalidMessageException, FormatException {
+ Transaction txn = new Transaction(null, false);
+ BdfDictionary d = BdfDictionary.of(
+ new BdfEntry(MSG_KEY_VERSION, 0),
+ new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType)
+ );
+ MessageId latestMsgId = new MessageId(getRandomId());
+ BdfDictionary latest = BdfDictionary.of(
+ new BdfEntry(MSG_KEY_VERSION, 1),
+ new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType)
+ );
+
+ expectGetOurGroup(txn);
+ context.checking(new Expectations() {{
+ oneOf(metadataParser).parse(meta);
+ will(returnValue(d));
+ // delete older incoming message
+ oneOf(db).deleteMessage(txn, contactMsg.getId());
+ oneOf(db).deleteMessageMetadata(txn, contactMsg.getId());
+ }});
+ expectFindLatest(txn, contactGroupId, latestMsgId, latest);
+
+ assertFalse(avatarManager.incomingMessage(txn, contactMsg, meta));
+
+ // no event to broadcast
+ assertEquals(0, txn.getActions().size());
+ }
+
+ @Test(expected = InvalidMessageException.class)
+ public void testIncomingMessageInOwnGroup()
+ throws DbException, InvalidMessageException {
+ Transaction txn = new Transaction(null, false);
+ expectGetOurGroup(txn);
+ avatarManager.incomingMessage(txn, ourMsg, meta);
+ }
+
+ @Test
+ public void testAddAvatar() throws Exception {
+ byte[] avatarBytes = getRandomBytes(42);
+ InputStream inputStream = new ByteArrayInputStream(avatarBytes);
+ Transaction txn = new Transaction(null, true);
+ Transaction txn2 = new Transaction(null, false);
+ long latestVersion = metaDict.getLong(MSG_KEY_VERSION);
+ long version = latestVersion + 1;
+ Message newMsg = getMessage(localGroupId);
+ BdfDictionary newMeta = BdfDictionary.of(
+ new BdfEntry(MSG_KEY_VERSION, version),
+ new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType),
+ new BdfEntry(MSG_KEY_DESCRIPTOR_LENGTH, 0)
+ );
+ context.checking(new DbExpectations() {{
+ oneOf(db).startTransaction(true);
+ will(returnValue(txn));
+ oneOf(db).commitTransaction(txn);
+ oneOf(db).endTransaction(txn);
+ oneOf(avatarMessageEncoder).encodeUpdateMessage(localGroupId,
+ version, contentType, inputStream);
+ will(returnValue(new Pair<>(newMsg, newMeta)));
+ oneOf(db).transactionWithResult(with(false), withDbCallable(txn2));
+ oneOf(db).deleteMessage(txn2, ourMsg.getId());
+ oneOf(db).deleteMessageMetadata(txn2, ourMsg.getId());
+ oneOf(clientHelper)
+ .addLocalMessage(txn2, newMsg, newMeta, true, false);
+ }});
+ expectGetOurGroup(txn);
+ expectFindLatest(txn, localGroupId, ourMsg.getId(), metaDict);
+ expectFindLatest(txn2, localGroupId, ourMsg.getId(), metaDict);
+
+ AttachmentHeader header =
+ avatarManager.addAvatar(contentType, inputStream);
+ assertEquals(newMsg.getId(), header.getMessageId());
+ assertEquals(contentType, header.getContentType());
+ }
+
+ @Test
+ public void testAddAvatarConcurrently() throws Exception {
+ byte[] avatarBytes = getRandomBytes(42);
+ InputStream inputStream = new ByteArrayInputStream(avatarBytes);
+ Transaction txn = new Transaction(null, true);
+ Transaction txn2 = new Transaction(null, false);
+ long latestVersion = metaDict.getLong(MSG_KEY_VERSION);
+ Message newMsg = getMessage(localGroupId);
+ BdfDictionary newMeta = BdfDictionary.of(
+ new BdfEntry(MSG_KEY_VERSION, latestVersion + 2),
+ new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType),
+ new BdfEntry(MSG_KEY_DESCRIPTOR_LENGTH, 0)
+ );
+ context.checking(new DbExpectations() {{
+ oneOf(db).startTransaction(true);
+ will(returnValue(txn));
+ oneOf(db).commitTransaction(txn);
+ oneOf(db).endTransaction(txn);
+ oneOf(avatarMessageEncoder).encodeUpdateMessage(localGroupId,
+ latestVersion + 1, contentType, inputStream);
+ will(returnValue(new Pair<>(newMsg, newMeta)));
+ oneOf(db).transactionWithResult(with(false), withDbCallable(txn2));
+ // no deletion or storing happening
+ }});
+ expectGetOurGroup(txn);
+ expectFindLatest(txn, localGroupId, ourMsg.getId(), metaDict);
+ // second query for latest update returns higher version
+ expectFindLatest(txn2, localGroupId, ourMsg.getId(), newMeta);
+
+ AttachmentHeader header =
+ avatarManager.addAvatar(contentType, inputStream);
+ assertEquals(ourMsg.getId(), header.getMessageId());
+ assertEquals(contentType, header.getContentType());
+ }
+
+ private void expectGetContactId(Transaction txn, GroupId groupId,
+ ContactId contactId) throws DbException, FormatException {
+ BdfDictionary d = BdfDictionary
+ .of(new BdfEntry(GROUP_KEY_CONTACT_ID, contactId.getInt()));
+ context.checking(new Expectations() {{
+ oneOf(clientHelper).getGroupMetadataAsDictionary(txn, groupId);
+ will(returnValue(d));
+ }});
+ }
+
+ private void expectFindLatest(Transaction txn, GroupId groupId,
+ MessageId messageId, @Nullable BdfDictionary d)
+ throws DbException, FormatException {
+ Map map = new HashMap<>();
+ if (d != null) map.put(messageId, d);
+ context.checking(new Expectations() {{
+ oneOf(clientHelper)
+ .getMessageMetadataAsDictionary(txn, groupId);
+ will(returnValue(map));
+ }});
+ }
+
+ private void expectSetGroupVisibility(Transaction txn, ContactId contactId,
+ GroupId groupId, Visibility v) throws DbException {
+ context.checking(new Expectations() {{
+ oneOf(db).setGroupVisibility(txn, contactId, groupId, v);
+ }});
+ }
+
+ private void expectAddingContact(Transaction txn, Contact c, Visibility v)
+ throws DbException, FormatException {
+ BdfDictionary groupMeta = BdfDictionary.of(
+ new BdfEntry(GROUP_KEY_CONTACT_ID, c.getId().getInt())
+ );
+ expectGetOurGroup(txn);
+ context.checking(new Expectations() {{
+ oneOf(groupFactory).createGroup(CLIENT_ID, MAJOR_VERSION,
+ c.getAuthor().getId().getBytes());
+ will(returnValue(contactGroup));
+ oneOf(db).addGroup(txn, contactGroup);
+ oneOf(clientHelper)
+ .mergeGroupMetadata(txn, contactGroupId, groupMeta);
+ oneOf(clientVersioningManager)
+ .getClientVisibility(txn, c.getId(), CLIENT_ID,
+ MAJOR_VERSION);
+ will(returnValue(v));
+ }});
+ expectSetGroupVisibility(txn, c.getId(), localGroupId, v);
+ expectSetGroupVisibility(txn, c.getId(), contactGroupId, v);
+ }
+
+ private void expectGetOurGroup(Transaction txn) throws DbException {
+ context.checking(new Expectations() {{
+ oneOf(identityManager).getLocalAuthor(txn);
+ will(returnValue(localAuthor));
+ }});
+ expectCreateGroup(localAuthor.getId(), localGroup);
+ }
+
+ private void expectCreateGroup(AuthorId authorId, Group group) {
+ context.checking(new Expectations() {{
+ oneOf(groupFactory)
+ .createGroup(CLIENT_ID, MAJOR_VERSION, authorId.getBytes());
+ will(returnValue(group));
+ }});
+ }
+
+}
diff --git a/briar-core/src/test/java/org/briarproject/briar/avatar/AvatarManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/avatar/AvatarManagerIntegrationTest.java
new file mode 100644
index 000000000..5dc50e45b
--- /dev/null
+++ b/briar-core/src/test/java/org/briarproject/briar/avatar/AvatarManagerIntegrationTest.java
@@ -0,0 +1,197 @@
+package org.briarproject.briar.avatar;
+
+import org.briarproject.bramble.test.TestDatabaseConfigModule;
+import org.briarproject.briar.api.attachment.Attachment;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.briarproject.briar.api.attachment.AttachmentReader;
+import org.briarproject.briar.api.avatar.AvatarManager;
+import org.briarproject.briar.test.BriarIntegrationTest;
+import org.briarproject.briar.test.BriarIntegrationTestComponent;
+import org.briarproject.briar.test.DaggerBriarIntegrationTestComponent;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+
+import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
+import static org.briarproject.bramble.util.IoUtils.copyAndClose;
+import static org.briarproject.bramble.util.StringUtils.getRandomString;
+import static org.briarproject.briar.api.attachment.MediaConstants.MAX_CONTENT_TYPE_BYTES;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+public class AvatarManagerIntegrationTest
+ extends BriarIntegrationTest {
+
+ private AvatarManager avatarManager0, avatarManager1;
+ private AttachmentReader attachmentReader0, attachmentReader1;
+
+ private final String contentType = getRandomString(MAX_CONTENT_TYPE_BYTES);
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ avatarManager0 = c0.getAvatarManager();
+ avatarManager1 = c1.getAvatarManager();
+ attachmentReader0 = c0.getAttachmentReader();
+ attachmentReader1 = c1.getAttachmentReader();
+ }
+
+ @Override
+ protected void createComponents() {
+ BriarIntegrationTestComponent component =
+ DaggerBriarIntegrationTestComponent.builder().build();
+ BriarIntegrationTestComponent.Helper.injectEagerSingletons(component);
+ component.inject(this);
+
+ c0 = DaggerBriarIntegrationTestComponent.builder()
+ .testDatabaseConfigModule(new TestDatabaseConfigModule(t0Dir))
+ .build();
+ BriarIntegrationTestComponent.Helper.injectEagerSingletons(c0);
+
+ c1 = DaggerBriarIntegrationTestComponent.builder()
+ .testDatabaseConfigModule(new TestDatabaseConfigModule(t1Dir))
+ .build();
+ BriarIntegrationTestComponent.Helper.injectEagerSingletons(c1);
+
+ c2 = DaggerBriarIntegrationTestComponent.builder()
+ .testDatabaseConfigModule(new TestDatabaseConfigModule(t2Dir))
+ .build();
+ BriarIntegrationTestComponent.Helper.injectEagerSingletons(c2);
+ }
+
+ @Test
+ public void testAddingAndSyncAvatars() throws Exception {
+ // Both contacts don't have avatars
+ assertNull(db0.transactionWithNullableResult(true,
+ txn -> avatarManager0.getMyAvatarHeader(txn)));
+ assertNull(db1.transactionWithNullableResult(true,
+ txn -> avatarManager1.getMyAvatarHeader(txn)));
+
+ // Both contacts don't see avatars for each other
+ assertNull(db0.transactionWithNullableResult(true,
+ txn -> avatarManager0.getAvatarHeader(txn, contact1From0)));
+ assertNull(db1.transactionWithNullableResult(true,
+ txn -> avatarManager1.getAvatarHeader(txn, contact0From1)));
+
+ // 0 adds avatar
+ byte[] avatar0bytes = getRandomBytes(42);
+ InputStream avatar0inputStream = new ByteArrayInputStream(avatar0bytes);
+ AttachmentHeader header0 =
+ avatarManager0.addAvatar(contentType, avatar0inputStream);
+ assertEquals(contentType, header0.getContentType());
+
+ // 0 sees their own avatar
+ header0 = db0.transactionWithResult(true,
+ txn -> avatarManager0.getMyAvatarHeader(txn));
+ assertNotNull(header0);
+ assertEquals(contentType, header0.getContentType());
+ assertNotNull(header0.getMessageId());
+
+ // 0 can retrieve their own avatar
+ Attachment attachment0 = attachmentReader0.getAttachment(header0);
+ assertEquals(contentType, attachment0.getHeader().getContentType());
+ assertStreamMatches(avatar0bytes, attachment0.getStream());
+
+ // send the avatar from 0 to 1
+ sync0To1(1, true);
+
+ // 1 also sees 0's avatar now
+ AttachmentHeader header0From1 = db1.transactionWithResult(true,
+ txn -> avatarManager1.getAvatarHeader(txn, contact0From1));
+ assertNotNull(header0From1);
+ assertEquals(contentType, header0From1.getContentType());
+ assertNotNull(header0From1.getMessageId());
+
+ // 1 can retrieve 0's avatar
+ Attachment attachment0From1 =
+ attachmentReader1.getAttachment(header0From1);
+ assertEquals(contentType,
+ attachment0From1.getHeader().getContentType());
+ assertStreamMatches(avatar0bytes, attachment0From1.getStream());
+
+ // 1 also adds avatar
+ String contentType1 = getRandomString(MAX_CONTENT_TYPE_BYTES);
+ byte[] avatar1bytes = getRandomBytes(42);
+ InputStream avatar1inputStream = new ByteArrayInputStream(avatar1bytes);
+ avatarManager1.addAvatar(contentType1, avatar1inputStream);
+
+ // send the avatar from 1 to 0
+ sync1To0(1, true);
+
+ // 0 sees 1's avatar now
+ AttachmentHeader header1From0 = db0.transactionWithResult(true,
+ txn -> avatarManager0.getAvatarHeader(txn, contact1From0));
+ assertNotNull(header1From0);
+ assertEquals(contentType1, header1From0.getContentType());
+ assertNotNull(header1From0.getMessageId());
+
+ // 0 can retrieve 1's avatar
+ Attachment attachment1From0 =
+ attachmentReader0.getAttachment(header1From0);
+ assertEquals(contentType1,
+ attachment1From0.getHeader().getContentType());
+ assertStreamMatches(avatar1bytes, attachment1From0.getStream());
+ }
+
+ @Test
+ public void testUpdatingAvatars() throws Exception {
+ // 0 adds avatar
+ byte[] avatar0bytes = getRandomBytes(42);
+ InputStream avatar0inputStream = new ByteArrayInputStream(avatar0bytes);
+ avatarManager0.addAvatar(contentType, avatar0inputStream);
+
+ // 0 can retrieve their own avatar
+ AttachmentHeader header0 = db0.transactionWithResult(true,
+ txn -> avatarManager0.getMyAvatarHeader(txn));
+ assertNotNull(header0);
+ Attachment attachment0 = attachmentReader0.getAttachment(header0);
+ assertStreamMatches(avatar0bytes, attachment0.getStream());
+
+ // send the avatar from 0 to 1
+ sync0To1(1, true);
+
+ // 1 only sees 0's avatar
+ AttachmentHeader header0From1 = db1.transactionWithNullableResult(true,
+ txn -> avatarManager1.getAvatarHeader(txn, contact0From1));
+ assertNotNull(header0From1);
+ Attachment attachment0From1 =
+ attachmentReader1.getAttachment(header0From1);
+ assertStreamMatches(avatar0bytes, attachment0From1.getStream());
+
+ // 0 adds a new avatar
+ byte[] avatar0bytes2 = getRandomBytes(42);
+ InputStream avatar0inputStream2 =
+ new ByteArrayInputStream(avatar0bytes2);
+ avatarManager0.addAvatar(contentType, avatar0inputStream2);
+
+ // 0 now only sees their new avatar
+ header0 = db0.transactionWithResult(true,
+ txn -> avatarManager0.getMyAvatarHeader(txn));
+ assertNotNull(header0);
+ attachment0 = attachmentReader0.getAttachment(header0);
+ assertStreamMatches(avatar0bytes2, attachment0.getStream());
+
+ // send the new avatar from 0 to 1
+ sync0To1(1, true);
+
+ // 1 only sees 0's new avatar
+ header0From1 = db1.transactionWithNullableResult(true,
+ txn -> avatarManager1.getAvatarHeader(txn, contact0From1));
+ assertNotNull(header0From1);
+ attachment0From1 = attachmentReader1.getAttachment(header0From1);
+ assertStreamMatches(avatar0bytes2, attachment0From1.getStream());
+ }
+
+ private void assertStreamMatches(byte[] bytes, InputStream inputStream) {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ copyAndClose(inputStream, outputStream);
+ assertArrayEquals(bytes, outputStream.toByteArray());
+ }
+
+}
diff --git a/briar-core/src/test/java/org/briarproject/briar/avatar/AvatarValidatorTest.java b/briar-core/src/test/java/org/briarproject/briar/avatar/AvatarValidatorTest.java
new file mode 100644
index 000000000..c0c3abe76
--- /dev/null
+++ b/briar-core/src/test/java/org/briarproject/briar/avatar/AvatarValidatorTest.java
@@ -0,0 +1,188 @@
+package org.briarproject.briar.avatar;
+
+import org.briarproject.bramble.api.data.BdfDictionary;
+import org.briarproject.bramble.api.data.BdfEntry;
+import org.briarproject.bramble.api.data.BdfList;
+import org.briarproject.bramble.api.data.BdfReader;
+import org.briarproject.bramble.api.data.BdfReaderFactory;
+import org.briarproject.bramble.api.data.MetadataEncoder;
+import org.briarproject.bramble.api.db.Metadata;
+import org.briarproject.bramble.api.sync.Group;
+import org.briarproject.bramble.api.sync.InvalidMessageException;
+import org.briarproject.bramble.api.sync.Message;
+import org.briarproject.bramble.api.sync.MessageContext;
+import org.briarproject.bramble.api.system.Clock;
+import org.briarproject.bramble.test.BrambleMockTestCase;
+import org.jmock.Expectations;
+import org.junit.Test;
+
+import java.io.InputStream;
+
+import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
+import static org.briarproject.bramble.test.TestUtils.getClientId;
+import static org.briarproject.bramble.test.TestUtils.getGroup;
+import static org.briarproject.bramble.test.TestUtils.getMessage;
+import static org.briarproject.bramble.util.StringUtils.getRandomString;
+import static org.briarproject.briar.api.attachment.MediaConstants.MAX_CONTENT_TYPE_BYTES;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_CONTENT_TYPE;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH;
+import static org.briarproject.briar.avatar.AvatarConstants.MSG_KEY_VERSION;
+import static org.briarproject.briar.avatar.AvatarConstants.MSG_TYPE_UPDATE;
+import static org.junit.Assert.assertEquals;
+
+public class AvatarValidatorTest extends BrambleMockTestCase {
+
+ private final BdfReaderFactory bdfReaderFactory =
+ context.mock(BdfReaderFactory.class);
+ private final MetadataEncoder metadataEncoder =
+ context.mock(MetadataEncoder.class);
+ private final Clock clock = context.mock(Clock.class);
+ private final BdfReader reader = context.mock(BdfReader.class);
+
+ private final Group group = getGroup(getClientId(), 123);
+ private final Message message = getMessage(group.getId());
+ private final long now = message.getTimestamp() + 1000;
+ private final String contentType = getRandomString(MAX_CONTENT_TYPE_BYTES);
+ private final long version = System.currentTimeMillis();
+ private final BdfDictionary meta = BdfDictionary.of(
+ new BdfEntry(MSG_KEY_VERSION, version),
+ new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType),
+ // Descriptor length is zero as the test doesn't read from the
+ // counting input stream
+ new BdfEntry(MSG_KEY_DESCRIPTOR_LENGTH, 0L)
+ );
+
+ private final AvatarValidator validator =
+ new AvatarValidator(bdfReaderFactory, metadataEncoder, clock);
+
+ @Test(expected = InvalidMessageException.class)
+ public void testRejectsFarFutureTimestamp() throws Exception {
+ expectCheckTimestamp(message.getTimestamp() - MAX_CLOCK_DIFFERENCE - 1);
+
+ validator.validateMessage(message, group);
+ }
+
+ @Test(expected = InvalidMessageException.class)
+ public void testRejectsEmptyBody() throws Exception {
+ expectCheckTimestamp(now);
+ expectParseList(new BdfList());
+
+ validator.validateMessage(message, group);
+ }
+
+ @Test(expected = InvalidMessageException.class)
+ public void testRejectsTooShortBody() throws Exception {
+ expectCheckTimestamp(now);
+ expectParseList(BdfList.of(MSG_TYPE_UPDATE, version));
+
+ validator.validateMessage(message, group);
+ }
+
+ @Test(expected = InvalidMessageException.class)
+ public void testRejectsUnknownMessageType() throws Exception {
+ expectCheckTimestamp(now);
+ expectParseList(BdfList.of(MSG_TYPE_UPDATE + 1, version, contentType));
+
+ validator.validateMessage(message, group);
+ }
+
+ @Test(expected = InvalidMessageException.class)
+ public void testRejectsNonLongVersion() throws Exception {
+ expectCheckTimestamp(now);
+ expectParseList(BdfList.of(MSG_TYPE_UPDATE, "foo", contentType));
+
+ validator.validateMessage(message, group);
+ }
+
+ @Test(expected = InvalidMessageException.class)
+ public void testRejectsNonStringContentType() throws Exception {
+ expectCheckTimestamp(now);
+ expectParseList(BdfList.of(MSG_TYPE_UPDATE, version, 1337));
+
+ validator.validateMessage(message, group);
+ }
+
+ @Test(expected = InvalidMessageException.class)
+ public void testRejectsEmptyContentType() throws Exception {
+ expectCheckTimestamp(now);
+ expectParseList(BdfList.of(MSG_TYPE_UPDATE, version, ""));
+
+ validator.validateMessage(message, group);
+ }
+
+ @Test(expected = InvalidMessageException.class)
+ public void testRejectsTooLongContentType() throws Exception {
+ String contentType = getRandomString(MAX_CONTENT_TYPE_BYTES + 1);
+
+ expectCheckTimestamp(now);
+ expectParseList(BdfList.of(MSG_TYPE_UPDATE, version, contentType));
+
+ validator.validateMessage(message, group);
+ }
+
+ @Test(expected = InvalidMessageException.class)
+ public void testRejectsTooLongBody() throws Exception {
+ expectCheckTimestamp(now);
+ expectParseList(BdfList.of(MSG_TYPE_UPDATE, version, contentType, 1));
+
+ validator.validateMessage(message, group);
+ }
+
+ @Test(expected = InvalidMessageException.class)
+ public void testRejectsNegativeVersion() throws Exception {
+ expectCheckTimestamp(now);
+ expectParseList(BdfList.of(MSG_TYPE_UPDATE, -1, contentType));
+
+ validator.validateMessage(message, group);
+ }
+
+ @Test
+ public void testAcceptsUpdateMessage() throws Exception {
+ testAcceptsUpdateMessage(
+ BdfList.of(MSG_TYPE_UPDATE, version, contentType), meta);
+ }
+
+ @Test
+ public void testAcceptsZeroVersion() throws Exception {
+ BdfList body = BdfList.of(MSG_TYPE_UPDATE, 0L, contentType);
+ BdfDictionary meta = BdfDictionary.of(
+ new BdfEntry(MSG_KEY_VERSION, 0L),
+ new BdfEntry(MSG_KEY_CONTENT_TYPE, contentType),
+ new BdfEntry(MSG_KEY_DESCRIPTOR_LENGTH, 0L)
+ );
+ testAcceptsUpdateMessage(body, meta);
+ }
+
+ private void testAcceptsUpdateMessage(BdfList body, BdfDictionary meta)
+ throws Exception {
+ expectCheckTimestamp(now);
+ expectParseList(body);
+ expectEncodeMetadata(meta);
+
+ MessageContext result = validator.validateMessage(message, group);
+ assertEquals(0, result.getDependencies().size());
+ }
+
+ private void expectCheckTimestamp(long now) {
+ context.checking(new Expectations() {{
+ oneOf(clock).currentTimeMillis();
+ will(returnValue(now));
+ }});
+ }
+
+ private void expectParseList(BdfList body) throws Exception {
+ context.checking(new Expectations() {{
+ oneOf(bdfReaderFactory).createReader(with(any(InputStream.class)));
+ will(returnValue(reader));
+ oneOf(reader).readList();
+ will(returnValue(body));
+ }});
+ }
+
+ private void expectEncodeMetadata(BdfDictionary meta) throws Exception {
+ context.checking(new Expectations() {{
+ oneOf(metadataEncoder).encode(meta);
+ will(returnValue(new Metadata()));
+ }});
+ }
+}
diff --git a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java
index 6922337fc..fb700e667 100644
--- a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java
@@ -3,7 +3,6 @@ package org.briarproject.briar.blog;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.Contact;
-import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
@@ -15,7 +14,6 @@ import org.briarproject.bramble.api.db.EventAction;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.identity.Author;
-import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.sync.Group;
@@ -28,14 +26,13 @@ import org.briarproject.briar.api.blog.BlogPost;
import org.briarproject.briar.api.blog.BlogPostFactory;
import org.briarproject.briar.api.blog.BlogPostHeader;
import org.briarproject.briar.api.blog.event.BlogPostAddedEvent;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.briarproject.briar.api.identity.AuthorManager;
import org.briarproject.briar.test.BriarTestCase;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.NONE;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
@@ -59,6 +56,9 @@ import static org.briarproject.briar.api.blog.MessageType.COMMENT;
import static org.briarproject.briar.api.blog.MessageType.POST;
import static org.briarproject.briar.api.blog.MessageType.WRAPPED_COMMENT;
import static org.briarproject.briar.api.blog.MessageType.WRAPPED_POST;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.NONE;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.OURSELVES;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.VERIFIED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -69,8 +69,8 @@ public class BlogManagerImplTest extends BriarTestCase {
private final Mockery context = new Mockery();
private final BlogManagerImpl blogManager;
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
- private final ContactManager contactManager =
- context.mock(ContactManager.class);
+ private final AuthorManager authorManager =
+ context.mock(AuthorManager.class);
private final IdentityManager identityManager =
context.mock(IdentityManager.class);
private final ClientHelper clientHelper = context.mock(ClientHelper.class);
@@ -90,7 +90,7 @@ public class BlogManagerImplTest extends BriarTestCase {
public BlogManagerImplTest() {
MetadataParser metadataParser = context.mock(MetadataParser.class);
- blogManager = new BlogManagerImpl(db, contactManager, identityManager,
+ blogManager = new BlogManagerImpl(db, identityManager, authorManager,
clientHelper, metadataParser, blogFactory, blogPostFactory);
localAuthor1 = getLocalAuthor();
@@ -180,7 +180,7 @@ public class BlogManagerImplTest extends BriarTestCase {
context.checking(new Expectations() {{
oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1));
- oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
+ oneOf(authorManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(verifiedInfo));
}});
@@ -291,7 +291,7 @@ public class BlogManagerImplTest extends BriarTestCase {
false);
oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1));
- oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
+ oneOf(authorManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(ourselvesInfo));
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
@@ -413,13 +413,13 @@ public class BlogManagerImplTest extends BriarTestCase {
// Create the headers for the comment and its parent
oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1));
- oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
+ oneOf(authorManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(ourselvesInfo));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, messageId);
will(returnValue(postMeta));
oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1));
- oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
+ oneOf(authorManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(ourselvesInfo));
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
@@ -523,14 +523,14 @@ public class BlogManagerImplTest extends BriarTestCase {
// Create the headers for the comment and the wrapped post
oneOf(clientHelper).parseAndValidateAuthor(authorList2);
will(returnValue(localAuthor2));
- oneOf(contactManager).getAuthorInfo(txn, localAuthor2.getId());
+ oneOf(authorManager).getAuthorInfo(txn, localAuthor2.getId());
will(returnValue(ourselvesInfo));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
wrappedPostId);
will(returnValue(wrappedPostMeta));
oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1));
- oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
+ oneOf(authorManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(verifiedInfo));
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
@@ -634,7 +634,7 @@ public class BlogManagerImplTest extends BriarTestCase {
// Create the headers for the comment and the wrapped post
oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1));
- oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
+ oneOf(authorManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(ourselvesInfo));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
wrappedPostId);
@@ -775,14 +775,14 @@ public class BlogManagerImplTest extends BriarTestCase {
// the rewrapped post
oneOf(clientHelper).parseAndValidateAuthor(authorList2);
will(returnValue(localAuthor2));
- oneOf(contactManager).getAuthorInfo(txn, localAuthor2.getId());
+ oneOf(authorManager).getAuthorInfo(txn, localAuthor2.getId());
will(returnValue(ourselvesInfo));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
wrappedCommentId);
will(returnValue(wrappedCommentMeta));
oneOf(clientHelper).parseAndValidateAuthor(authorList1);
will(returnValue(localAuthor1));
- oneOf(contactManager).getAuthorInfo(txn, localAuthor1.getId());
+ oneOf(authorManager).getAuthorInfo(txn, localAuthor1.getId());
will(returnValue(verifiedInfo));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
rewrappedPostId);
diff --git a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java
index 3140a5aad..94fad0819 100644
--- a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java
@@ -21,7 +21,7 @@ import java.util.Iterator;
import static junit.framework.Assert.assertNotNull;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.NONE;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.NONE;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.api.blog.MessageType.COMMENT;
import static org.briarproject.briar.api.blog.MessageType.POST;
diff --git a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java
index c323504fe..78b6c0f1c 100644
--- a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java
+++ b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java
@@ -8,8 +8,10 @@ import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
import org.briarproject.bramble.test.TestSocksModule;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.feed.FeedManager;
+import org.briarproject.briar.avatar.AvatarModule;
import org.briarproject.briar.blog.BlogModule;
import org.briarproject.briar.client.BriarClientModule;
+import org.briarproject.briar.identity.IdentityModule;
import org.briarproject.briar.test.TestDnsModule;
import javax.inject.Singleton;
@@ -20,9 +22,11 @@ import dagger.Component;
@Component(modules = {
BrambleCoreIntegrationTestModule.class,
BrambleCoreModule.class,
+ AvatarModule.class,
BlogModule.class,
BriarClientModule.class,
FeedModule.class,
+ IdentityModule.class,
TestDnsModule.class,
TestSocksModule.class,
})
@@ -31,6 +35,8 @@ interface FeedManagerIntegrationTestComponent
void inject(FeedManagerIntegrationTest testCase);
+ void inject(AvatarModule.EagerSingletons init);
+
void inject(BlogModule.EagerSingletons init);
void inject(FeedModule.EagerSingletons init);
@@ -49,6 +55,7 @@ interface FeedManagerIntegrationTestComponent
FeedManagerIntegrationTestComponent c) {
BrambleCoreIntegrationTestEagerSingletons.Helper
.injectEagerSingletons(c);
+ c.inject(new AvatarModule.EagerSingletons());
c.inject(new BlogModule.EagerSingletons());
c.inject(new FeedModule.EagerSingletons());
}
diff --git a/briar-core/src/test/java/org/briarproject/briar/identity/AuthorManagerImplTest.java b/briar-core/src/test/java/org/briarproject/briar/identity/AuthorManagerImplTest.java
new file mode 100644
index 000000000..ad19ec868
--- /dev/null
+++ b/briar-core/src/test/java/org/briarproject/briar/identity/AuthorManagerImplTest.java
@@ -0,0 +1,152 @@
+package org.briarproject.briar.identity;
+
+import org.briarproject.bramble.api.contact.Contact;
+import org.briarproject.bramble.api.db.DatabaseComponent;
+import org.briarproject.bramble.api.db.DbException;
+import org.briarproject.bramble.api.db.Transaction;
+import org.briarproject.bramble.api.identity.Author;
+import org.briarproject.bramble.api.identity.AuthorId;
+import org.briarproject.bramble.api.identity.IdentityManager;
+import org.briarproject.bramble.api.identity.LocalAuthor;
+import org.briarproject.bramble.api.sync.GroupId;
+import org.briarproject.bramble.api.sync.MessageId;
+import org.briarproject.bramble.test.BrambleMockTestCase;
+import org.briarproject.bramble.test.DbExpectations;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
+import org.briarproject.briar.api.avatar.AvatarManager;
+import org.briarproject.briar.api.identity.AuthorInfo;
+import org.jmock.Expectations;
+import org.junit.Test;
+
+import java.util.Collection;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+import static org.briarproject.bramble.test.TestUtils.getAuthor;
+import static org.briarproject.bramble.test.TestUtils.getContact;
+import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
+import static org.briarproject.bramble.test.TestUtils.getRandomId;
+import static org.briarproject.bramble.util.StringUtils.getRandomString;
+import static org.briarproject.briar.api.attachment.MediaConstants.MAX_CONTENT_TYPE_BYTES;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.OURSELVES;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.UNKNOWN;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.UNVERIFIED;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.VERIFIED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+public class AuthorManagerImplTest extends BrambleMockTestCase {
+
+ private final DatabaseComponent db = context.mock(DatabaseComponent.class);
+ private final IdentityManager identityManager =
+ context.mock(IdentityManager.class);
+ private final AvatarManager avatarManager =
+ context.mock(AvatarManager.class);
+
+ private final Author remote = getAuthor();
+ private final LocalAuthor localAuthor = getLocalAuthor();
+ private final AuthorId local = localAuthor.getId();
+ private final boolean verified = false;
+ private final Contact contact = getContact(remote, local, verified);
+ private final String contentType = getRandomString(MAX_CONTENT_TYPE_BYTES);
+ private final AttachmentHeader avatarHeader =
+ new AttachmentHeader(new GroupId(getRandomId()),
+ new MessageId(getRandomId()), contentType);
+
+ private final AuthorManagerImpl authorManager =
+ new AuthorManagerImpl(db, identityManager, avatarManager);
+
+ @Test
+ public void testGetAuthorInfoUnverified() throws Exception {
+ Transaction txn = new Transaction(null, true);
+
+ checkAuthorInfoContext(txn, remote.getId(), singletonList(contact));
+ context.checking(new DbExpectations() {{
+ oneOf(avatarManager).getAvatarHeader(txn, contact);
+ will(returnValue(avatarHeader));
+ }});
+
+ AuthorInfo authorInfo =
+ authorManager.getAuthorInfo(txn, remote.getId());
+ assertEquals(UNVERIFIED, authorInfo.getStatus());
+ assertEquals(contact.getAlias(), authorInfo.getAlias());
+ assertEquals(avatarHeader, authorInfo.getAvatarHeader());
+ }
+
+ @Test
+ public void testGetAuthorInfoUnknown() throws DbException {
+ Transaction txn = new Transaction(null, true);
+
+ checkAuthorInfoContext(txn, remote.getId(), emptyList());
+
+ AuthorInfo authorInfo =
+ authorManager.getAuthorInfo(txn, remote.getId());
+ assertEquals(UNKNOWN, authorInfo.getStatus());
+ assertNull(authorInfo.getAlias());
+ assertNull(authorInfo.getAvatarHeader());
+ }
+
+ @Test
+ public void testGetAuthorInfoVerified() throws DbException {
+ Transaction txn = new Transaction(null, true);
+
+ Contact verified = getContact(remote, local, true);
+ checkAuthorInfoContext(txn, remote.getId(), singletonList(verified));
+ context.checking(new DbExpectations() {{
+ oneOf(avatarManager).getAvatarHeader(txn, verified);
+ will(returnValue(avatarHeader));
+ }});
+
+ AuthorInfo authorInfo =
+ authorManager.getAuthorInfo(txn, remote.getId());
+ assertEquals(VERIFIED, authorInfo.getStatus());
+ assertEquals(verified.getAlias(), authorInfo.getAlias());
+ assertEquals(avatarHeader, authorInfo.getAvatarHeader());
+ }
+
+ @Test
+ public void testGetAuthorInfoOurselves() throws DbException {
+ Transaction txn = new Transaction(null, true);
+
+ context.checking(new Expectations() {{
+ oneOf(identityManager).getLocalAuthor(txn);
+ will(returnValue(localAuthor));
+ never(db).getContactsByAuthorId(txn, remote.getId());
+ oneOf(avatarManager).getMyAvatarHeader(txn);
+ will(returnValue(avatarHeader));
+ }});
+
+ AuthorInfo authorInfo =
+ authorManager.getAuthorInfo(txn, localAuthor.getId());
+ assertEquals(OURSELVES, authorInfo.getStatus());
+ assertNull(authorInfo.getAlias());
+ assertEquals(avatarHeader, authorInfo.getAvatarHeader());
+ }
+
+ @Test
+ public void testGetMyAuthorInfo() throws DbException {
+ Transaction txn = new Transaction(null, true);
+
+ context.checking(new Expectations() {{
+ oneOf(avatarManager).getMyAvatarHeader(txn);
+ will(returnValue(avatarHeader));
+ }});
+
+ AuthorInfo authorInfo =
+ authorManager.getMyAuthorInfo(txn);
+ assertEquals(OURSELVES, authorInfo.getStatus());
+ assertNull(authorInfo.getAlias());
+ assertEquals(avatarHeader, authorInfo.getAvatarHeader());
+ }
+
+ private void checkAuthorInfoContext(Transaction txn, AuthorId authorId,
+ Collection contacts) throws DbException {
+ context.checking(new Expectations() {{
+ oneOf(identityManager).getLocalAuthor(txn);
+ will(returnValue(localAuthor));
+ oneOf(db).getContactsByAuthorId(txn, authorId);
+ will(returnValue(contacts));
+ }});
+ }
+
+}
diff --git a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java
index cb0051089..ce4e455e4 100644
--- a/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java
+++ b/briar-core/src/test/java/org/briarproject/briar/introduction/IntroductionIntegrationTestComponent.java
@@ -2,9 +2,12 @@ package org.briarproject.briar.introduction;
import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
+import org.briarproject.briar.attachment.AttachmentModule;
+import org.briarproject.briar.avatar.AvatarModule;
import org.briarproject.briar.blog.BlogModule;
import org.briarproject.briar.client.BriarClientModule;
import org.briarproject.briar.forum.ForumModule;
+import org.briarproject.briar.identity.IdentityModule;
import org.briarproject.briar.messaging.MessagingModule;
import org.briarproject.briar.privategroup.PrivateGroupModule;
import org.briarproject.briar.privategroup.invitation.GroupInvitationModule;
@@ -19,11 +22,14 @@ import dagger.Component;
@Component(modules = {
BrambleCoreIntegrationTestModule.class,
BrambleCoreModule.class,
+ AvatarModule.class,
BlogModule.class,
BriarClientModule.class,
ForumModule.class,
GroupInvitationModule.class,
+ IdentityModule.class,
IntroductionModule.class,
+ AttachmentModule.class,
MessagingModule.class,
PrivateGroupModule.class,
SharingModule.class
diff --git a/briar-core/src/test/java/org/briarproject/briar/messaging/MessageSizeIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/messaging/MessageSizeIntegrationTest.java
index d07067887..8184cc168 100644
--- a/briar-core/src/test/java/org/briarproject/briar/messaging/MessageSizeIntegrationTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/messaging/MessageSizeIntegrationTest.java
@@ -6,9 +6,9 @@ import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import org.briarproject.briar.api.forum.ForumPost;
import org.briarproject.briar.api.forum.ForumPostFactory;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
import org.briarproject.briar.api.messaging.PrivateMessage;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
import org.briarproject.briar.test.BriarTestCase;
@@ -24,9 +24,9 @@ import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_K
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
+import static org.briarproject.briar.api.attachment.MediaConstants.MAX_CONTENT_TYPE_BYTES;
import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_POST_TEXT_LENGTH;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE;
-import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_BYTES;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH;
import static org.junit.Assert.assertTrue;
@@ -73,7 +73,8 @@ public class MessageSizeIntegrationTest extends BriarTestCase {
// Create the maximum number of maximum-length attachment headers
List headers = new ArrayList<>();
for (int i = 0; i < MAX_ATTACHMENTS_PER_MESSAGE; i++) {
- headers.add(new AttachmentHeader(new MessageId(getRandomId()),
+ headers.add(new AttachmentHeader(groupId,
+ new MessageId(getRandomId()),
getRandomString(MAX_CONTENT_TYPE_BYTES)));
}
PrivateMessage message = privateMessageFactory.createPrivateMessage(
diff --git a/briar-core/src/test/java/org/briarproject/briar/messaging/MessageSizeIntegrationTestComponent.java b/briar-core/src/test/java/org/briarproject/briar/messaging/MessageSizeIntegrationTestComponent.java
index 634bd69f7..4aa2e44e6 100644
--- a/briar-core/src/test/java/org/briarproject/briar/messaging/MessageSizeIntegrationTestComponent.java
+++ b/briar-core/src/test/java/org/briarproject/briar/messaging/MessageSizeIntegrationTestComponent.java
@@ -3,8 +3,10 @@ package org.briarproject.briar.messaging;
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons;
import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
+import org.briarproject.briar.avatar.AvatarModule;
import org.briarproject.briar.client.BriarClientModule;
import org.briarproject.briar.forum.ForumModule;
+import org.briarproject.briar.identity.IdentityModule;
import javax.inject.Singleton;
@@ -15,7 +17,9 @@ import dagger.Component;
BrambleCoreIntegrationTestModule.class,
BrambleCoreModule.class,
BriarClientModule.class,
+ AvatarModule.class,
ForumModule.class,
+ IdentityModule.class,
MessagingModule.class
})
interface MessageSizeIntegrationTestComponent
@@ -23,6 +27,8 @@ interface MessageSizeIntegrationTestComponent
void inject(MessageSizeIntegrationTest testCase);
+ void inject(AvatarModule.EagerSingletons init);
+
void inject(ForumModule.EagerSingletons init);
void inject(MessagingModule.EagerSingletons init);
@@ -33,6 +39,7 @@ interface MessageSizeIntegrationTestComponent
MessageSizeIntegrationTestComponent c) {
BrambleCoreIntegrationTestEagerSingletons.Helper
.injectEagerSingletons(c);
+ c.inject(new AvatarModule.EagerSingletons());
c.inject(new ForumModule.EagerSingletons());
c.inject(new MessagingModule.EagerSingletons());
}
diff --git a/briar-core/src/test/java/org/briarproject/briar/messaging/MessagingManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/messaging/MessagingManagerIntegrationTest.java
index 352154e7b..30c0e1d8f 100644
--- a/briar-core/src/test/java/org/briarproject/briar/messaging/MessagingManagerIntegrationTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/messaging/MessagingManagerIntegrationTest.java
@@ -6,9 +6,9 @@ import org.briarproject.bramble.api.db.MessageDeletedException;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.test.TestDatabaseConfigModule;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
import org.briarproject.briar.api.conversation.DeletionResult;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessage;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
diff --git a/briar-core/src/test/java/org/briarproject/briar/messaging/PrivateMessageValidatorTest.java b/briar-core/src/test/java/org/briarproject/briar/messaging/PrivateMessageValidatorTest.java
index 1f0503f2b..74ed605d3 100644
--- a/briar-core/src/test/java/org/briarproject/briar/messaging/PrivateMessageValidatorTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/messaging/PrivateMessageValidatorTest.java
@@ -27,15 +27,15 @@ import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
+import static org.briarproject.briar.api.attachment.MediaConstants.MAX_CONTENT_TYPE_BYTES;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_CONTENT_TYPE;
+import static org.briarproject.briar.api.attachment.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE;
-import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_BYTES;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH;
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
import static org.briarproject.briar.messaging.MessageTypes.ATTACHMENT;
import static org.briarproject.briar.messaging.MessageTypes.PRIVATE_MESSAGE;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_ATTACHMENT_HEADERS;
-import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_CONTENT_TYPE;
-import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_DESCRIPTOR_LENGTH;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_HAS_TEXT;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.messaging.MessagingConstants.MSG_KEY_MSG_TYPE;
diff --git a/briar-core/src/test/java/org/briarproject/briar/messaging/SimplexMessagingIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/messaging/SimplexMessagingIntegrationTest.java
index 3671c01e1..88f1530a3 100644
--- a/briar-core/src/test/java/org/briarproject/briar/messaging/SimplexMessagingIntegrationTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/messaging/SimplexMessagingIntegrationTest.java
@@ -15,7 +15,7 @@ import org.briarproject.bramble.api.sync.event.MessageStateChangedEvent;
import org.briarproject.bramble.test.TestDatabaseConfigModule;
import org.briarproject.bramble.test.TestTransportConnectionReader;
import org.briarproject.bramble.test.TestTransportConnectionWriter;
-import org.briarproject.briar.api.messaging.AttachmentHeader;
+import org.briarproject.briar.api.attachment.AttachmentHeader;
import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessage;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
diff --git a/briar-core/src/test/java/org/briarproject/briar/privategroup/PrivateGroupIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/privategroup/PrivateGroupIntegrationTest.java
index de3ea5400..51e7dcc57 100644
--- a/briar-core/src/test/java/org/briarproject/briar/privategroup/PrivateGroupIntegrationTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/privategroup/PrivateGroupIntegrationTest.java
@@ -24,7 +24,7 @@ import java.util.Collection;
import javax.annotation.Nullable;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.briar.api.privategroup.Visibility.INVISIBLE;
import static org.briarproject.briar.api.privategroup.Visibility.REVEALED_BY_CONTACT;
import static org.briarproject.briar.api.privategroup.Visibility.REVEALED_BY_US;
diff --git a/briar-core/src/test/java/org/briarproject/briar/privategroup/PrivateGroupManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/privategroup/PrivateGroupManagerIntegrationTest.java
index 52dd29b2e..8a87fbe20 100644
--- a/briar-core/src/test/java/org/briarproject/briar/privategroup/PrivateGroupManagerIntegrationTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/privategroup/PrivateGroupManagerIntegrationTest.java
@@ -20,7 +20,7 @@ import org.junit.Test;
import java.util.Collection;
-import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
+import static org.briarproject.briar.api.identity.AuthorInfo.Status.VERIFIED;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
diff --git a/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTestComponent.java b/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTestComponent.java
index cc074a97a..f28cb8ef0 100644
--- a/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTestComponent.java
+++ b/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTestComponent.java
@@ -12,6 +12,8 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
+import org.briarproject.briar.api.attachment.AttachmentReader;
+import org.briarproject.briar.api.avatar.AvatarManager;
import org.briarproject.briar.api.blog.BlogFactory;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogSharingManager;
@@ -24,9 +26,12 @@ import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
+import org.briarproject.briar.attachment.AttachmentModule;
+import org.briarproject.briar.avatar.AvatarModule;
import org.briarproject.briar.blog.BlogModule;
import org.briarproject.briar.client.BriarClientModule;
import org.briarproject.briar.forum.ForumModule;
+import org.briarproject.briar.identity.IdentityModule;
import org.briarproject.briar.introduction.IntroductionModule;
import org.briarproject.briar.messaging.MessagingModule;
import org.briarproject.briar.privategroup.PrivateGroupModule;
@@ -41,11 +46,14 @@ import dagger.Component;
@Component(modules = {
BrambleCoreIntegrationTestModule.class,
BrambleCoreModule.class,
+ AvatarModule.class,
BlogModule.class,
BriarClientModule.class,
ForumModule.class,
GroupInvitationModule.class,
+ IdentityModule.class,
IntroductionModule.class,
+ AttachmentModule.class,
MessagingModule.class,
PrivateGroupModule.class,
SharingModule.class
@@ -55,12 +63,16 @@ public interface BriarIntegrationTestComponent
void inject(BriarIntegrationTest init);
+ void inject(AvatarModule.EagerSingletons init);
+
void inject(BlogModule.EagerSingletons init);
void inject(ForumModule.EagerSingletons init);
void inject(GroupInvitationModule.EagerSingletons init);
+ void inject(IdentityModule.EagerSingletons init);
+
void inject(IntroductionModule.EagerSingletons init);
void inject(MessagingModule.EagerSingletons init);
@@ -75,6 +87,10 @@ public interface BriarIntegrationTestComponent
IdentityManager getIdentityManager();
+ AttachmentReader getAttachmentReader();
+
+ AvatarManager getAvatarManager();
+
ClientHelper getClientHelper();
ContactManager getContactManager();
@@ -117,9 +133,11 @@ public interface BriarIntegrationTestComponent
BriarIntegrationTestComponent c) {
BrambleCoreIntegrationTestEagerSingletons.Helper
.injectEagerSingletons(c);
+ c.inject(new AvatarModule.EagerSingletons());
c.inject(new BlogModule.EagerSingletons());
c.inject(new ForumModule.EagerSingletons());
c.inject(new GroupInvitationModule.EagerSingletons());
+ c.inject(new IdentityModule.EagerSingletons());
c.inject(new IntroductionModule.EagerSingletons());
c.inject(new MessagingModule.EagerSingletons());
c.inject(new PrivateGroupModule.EagerSingletons());
diff --git a/briar-headless/src/main/java/org/briarproject/bramble/identity/OutputAuthor.kt b/briar-headless/src/main/java/org/briarproject/bramble/identity/OutputAuthor.kt
index cbdb0387e..baff61c9a 100644
--- a/briar-headless/src/main/java/org/briarproject/bramble/identity/OutputAuthor.kt
+++ b/briar-headless/src/main/java/org/briarproject/bramble/identity/OutputAuthor.kt
@@ -1,7 +1,7 @@
package org.briarproject.bramble.identity
import org.briarproject.bramble.api.identity.Author
-import org.briarproject.bramble.api.identity.AuthorInfo
+import org.briarproject.briar.api.identity.AuthorInfo
import org.briarproject.briar.headless.json.JsonDict
fun Author.output() = JsonDict(
diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt b/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt
index 27d185c5b..5b0007816 100644
--- a/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt
+++ b/briar-headless/src/main/java/org/briarproject/briar/headless/HeadlessModule.kt
@@ -88,5 +88,8 @@ internal class HeadlessModule(private val appDir: File) {
internal fun provideObjectMapper() = ObjectMapper()
@Provides
- internal fun provideFeatureFlags() = FeatureFlags { false }
+ internal fun provideFeatureFlags() = object : FeatureFlags {
+ override fun shouldEnableImageAttachments() = false
+ override fun shouldEnableProfilePictures() = false
+ }
}
diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt
index 24c3e07a0..70ea2323b 100644
--- a/briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt
+++ b/briar-headless/src/test/java/org/briarproject/briar/headless/HeadlessTestModule.kt
@@ -18,6 +18,7 @@ import org.briarproject.bramble.system.DefaultTaskSchedulerModule
import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule
import org.briarproject.bramble.system.JavaSystemModule
import org.briarproject.bramble.test.TestSecureRandomModule
+import org.briarproject.briar.api.test.TestAvatarCreator
import org.briarproject.briar.headless.blogs.HeadlessBlogModule
import org.briarproject.briar.headless.contact.HeadlessContactModule
import org.briarproject.briar.headless.event.HeadlessEventModule
@@ -75,5 +76,11 @@ internal class HeadlessTestModule(private val appDir: File) {
internal fun provideObjectMapper() = ObjectMapper()
@Provides
- internal fun provideFeatureFlags() = FeatureFlags { false }
+ internal fun provideFeatureFlags() = object : FeatureFlags {
+ override fun shouldEnableImageAttachments() = false
+ override fun shouldEnableProfilePictures() = false
+ }
+
+ @Provides
+ internal fun provideTestAvatarCreator() = TestAvatarCreator { null }
}
diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/blogs/BlogControllerTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/blogs/BlogControllerTest.kt
index da84fb47e..aae2aa100 100644
--- a/briar-headless/src/test/java/org/briarproject/briar/headless/blogs/BlogControllerTest.kt
+++ b/briar-headless/src/test/java/org/briarproject/briar/headless/blogs/BlogControllerTest.kt
@@ -6,8 +6,8 @@ import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
-import org.briarproject.bramble.api.identity.AuthorInfo
-import org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES
+import org.briarproject.briar.api.identity.AuthorInfo
+import org.briarproject.briar.api.identity.AuthorInfo.Status.OURSELVES
import org.briarproject.bramble.api.sync.MessageId
import org.briarproject.bramble.identity.output
import org.briarproject.bramble.util.StringUtils.getRandomString
diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerIntegrationTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerIntegrationTest.kt
index bea7b950c..2515eba10 100644
--- a/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerIntegrationTest.kt
+++ b/briar-headless/src/test/java/org/briarproject/briar/headless/contact/ContactControllerIntegrationTest.kt
@@ -25,7 +25,7 @@ class ContactControllerIntegrationTest: IntegrationTest() {
// add one test contact
val testContactName= "testContactName"
- testDataCreator.addContact(testContactName)
+ testDataCreator.addContact(testContactName, false)
// retrieve list with one test contact
response = get("$url/contacts")
diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/event/WebSocketControllerTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/event/WebSocketControllerTest.kt
index 2dbc6614c..221b95001 100644
--- a/briar-headless/src/test/java/org/briarproject/briar/headless/event/WebSocketControllerTest.kt
+++ b/briar-headless/src/test/java/org/briarproject/briar/headless/event/WebSocketControllerTest.kt
@@ -6,8 +6,8 @@ import io.mockk.CapturingSlot
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
-import org.briarproject.bramble.api.identity.AuthorInfo
-import org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED
+import org.briarproject.briar.api.identity.AuthorInfo
+import org.briarproject.briar.api.identity.AuthorInfo.Status.VERIFIED
import org.briarproject.bramble.test.ImmediateExecutor
import org.briarproject.bramble.test.TestUtils.getRandomId
import org.briarproject.briar.api.client.SessionId
diff --git a/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt b/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt
index 84c7c5ec2..a3a9d49c4 100644
--- a/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt
+++ b/briar-headless/src/test/java/org/briarproject/briar/headless/messaging/MessagingControllerImplTest.kt
@@ -7,9 +7,9 @@ import io.javalin.plugin.json.JavalinJson.toJson
import io.mockk.*
import org.briarproject.bramble.api.contact.ContactId
import org.briarproject.bramble.api.db.NoSuchContactException
-import org.briarproject.bramble.api.identity.AuthorInfo
-import org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED
-import org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED
+import org.briarproject.briar.api.identity.AuthorInfo
+import org.briarproject.briar.api.identity.AuthorInfo.Status.UNVERIFIED
+import org.briarproject.briar.api.identity.AuthorInfo.Status.VERIFIED
import org.briarproject.bramble.api.sync.MessageId
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent
import org.briarproject.bramble.api.sync.event.MessagesSentEvent