mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-15 12:19:54 +01:00
Refactor attachment creation
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.briar.android.conversation;
|
package org.briarproject.briar.android.attachment;
|
||||||
|
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.support.test.InstrumentationRegistry;
|
import android.support.test.InstrumentationRegistry;
|
||||||
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertFalse;
|
|||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class AttachmentControllerIntegrationTest {
|
public class AttachmentRetrieverIntegrationTest {
|
||||||
|
|
||||||
private static final String smallKitten =
|
private static final String smallKitten =
|
||||||
"https://upload.wikimedia.org/wikipedia/commons/thumb/0/06/Kitten_in_Rizal_Park%2C_Manila.jpg/160px-Kitten_in_Rizal_Park%2C_Manila.jpg";
|
"https://upload.wikimedia.org/wikipedia/commons/thumb/0/06/Kitten_in_Rizal_Park%2C_Manila.jpg/160px-Kitten_in_Rizal_Park%2C_Manila.jpg";
|
||||||
@@ -47,15 +47,15 @@ public class AttachmentControllerIntegrationTest {
|
|||||||
);
|
);
|
||||||
private final MessageId msgId = new MessageId(getRandomId());
|
private final MessageId msgId = new MessageId(getRandomId());
|
||||||
|
|
||||||
private final AttachmentController controller =
|
private final AttachmentRetriever retriever =
|
||||||
new AttachmentController(null, dimensions);
|
new AttachmentRetriever(null, dimensions);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSmallJpegImage() throws Exception {
|
public void testSmallJpegImage() throws Exception {
|
||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getUrlInputStream(smallKitten);
|
InputStream is = getUrlInputStream(smallKitten);
|
||||||
Attachment a = new Attachment(is);
|
Attachment a = new Attachment(is);
|
||||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||||
assertEquals(msgId, item.getMessageId());
|
assertEquals(msgId, item.getMessageId());
|
||||||
assertEquals(160, item.getWidth());
|
assertEquals(160, item.getWidth());
|
||||||
assertEquals(240, item.getHeight());
|
assertEquals(240, item.getHeight());
|
||||||
@@ -71,7 +71,7 @@ public class AttachmentControllerIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getUrlInputStream(originalKitten);
|
InputStream is = getUrlInputStream(originalKitten);
|
||||||
Attachment a = new Attachment(is);
|
Attachment a = new Attachment(is);
|
||||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||||
assertEquals(msgId, item.getMessageId());
|
assertEquals(msgId, item.getMessageId());
|
||||||
assertEquals(1728, item.getWidth());
|
assertEquals(1728, item.getWidth());
|
||||||
assertEquals(2592, item.getHeight());
|
assertEquals(2592, item.getHeight());
|
||||||
@@ -87,7 +87,7 @@ public class AttachmentControllerIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/png");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/png");
|
||||||
InputStream is = getUrlInputStream(pngKitten);
|
InputStream is = getUrlInputStream(pngKitten);
|
||||||
Attachment a = new Attachment(is);
|
Attachment a = new Attachment(is);
|
||||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||||
assertEquals(msgId, item.getMessageId());
|
assertEquals(msgId, item.getMessageId());
|
||||||
assertEquals(737, item.getWidth());
|
assertEquals(737, item.getWidth());
|
||||||
assertEquals(510, item.getHeight());
|
assertEquals(510, item.getHeight());
|
||||||
@@ -103,7 +103,7 @@ public class AttachmentControllerIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getUrlInputStream(uberGif);
|
InputStream is = getUrlInputStream(uberGif);
|
||||||
Attachment a = new Attachment(is);
|
Attachment a = new Attachment(is);
|
||||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||||
assertEquals(1, item.getWidth());
|
assertEquals(1, item.getWidth());
|
||||||
assertEquals(1, item.getHeight());
|
assertEquals(1, item.getHeight());
|
||||||
assertEquals(dimensions.minHeight, item.getThumbnailWidth());
|
assertEquals(dimensions.minHeight, item.getThumbnailWidth());
|
||||||
@@ -118,7 +118,7 @@ public class AttachmentControllerIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getUrlInputStream(lottaPixel);
|
InputStream is = getUrlInputStream(lottaPixel);
|
||||||
Attachment a = new Attachment(is);
|
Attachment a = new Attachment(is);
|
||||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||||
assertEquals(64250, item.getWidth());
|
assertEquals(64250, item.getWidth());
|
||||||
assertEquals(64250, item.getHeight());
|
assertEquals(64250, item.getHeight());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||||
@@ -133,7 +133,7 @@ public class AttachmentControllerIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getUrlInputStream(imageIoCrash);
|
InputStream is = getUrlInputStream(imageIoCrash);
|
||||||
Attachment a = new Attachment(is);
|
Attachment a = new Attachment(is);
|
||||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||||
assertEquals(1184, item.getWidth());
|
assertEquals(1184, item.getWidth());
|
||||||
assertEquals(448, item.getHeight());
|
assertEquals(448, item.getHeight());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||||
@@ -148,7 +148,7 @@ public class AttachmentControllerIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getUrlInputStream(gimpCrash);
|
InputStream is = getUrlInputStream(gimpCrash);
|
||||||
Attachment a = new Attachment(is);
|
Attachment a = new Attachment(is);
|
||||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||||
assertEquals(1, item.getWidth());
|
assertEquals(1, item.getWidth());
|
||||||
assertEquals(1, item.getHeight());
|
assertEquals(1, item.getHeight());
|
||||||
assertEquals(dimensions.minHeight, item.getThumbnailWidth());
|
assertEquals(dimensions.minHeight, item.getThumbnailWidth());
|
||||||
@@ -163,7 +163,7 @@ public class AttachmentControllerIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getUrlInputStream(optiPngAfl);
|
InputStream is = getUrlInputStream(optiPngAfl);
|
||||||
Attachment a = new Attachment(is);
|
Attachment a = new Attachment(is);
|
||||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||||
assertEquals(32, item.getWidth());
|
assertEquals(32, item.getWidth());
|
||||||
assertEquals(32, item.getHeight());
|
assertEquals(32, item.getHeight());
|
||||||
assertEquals(dimensions.minHeight, item.getThumbnailWidth());
|
assertEquals(dimensions.minHeight, item.getThumbnailWidth());
|
||||||
@@ -178,7 +178,7 @@ public class AttachmentControllerIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getUrlInputStream(librawError);
|
InputStream is = getUrlInputStream(librawError);
|
||||||
Attachment a = new Attachment(is);
|
Attachment a = new Attachment(is);
|
||||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||||
assertTrue(item.hasError());
|
assertTrue(item.hasError());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ public class AttachmentControllerIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
||||||
InputStream is = getAssetInputStream("animated.gif");
|
InputStream is = getAssetInputStream("animated.gif");
|
||||||
Attachment a = new Attachment(is);
|
Attachment a = new Attachment(is);
|
||||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||||
assertEquals(65535, item.getWidth());
|
assertEquals(65535, item.getWidth());
|
||||||
assertEquals(65535, item.getHeight());
|
assertEquals(65535, item.getHeight());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||||
@@ -202,7 +202,7 @@ public class AttachmentControllerIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
||||||
InputStream is = getAssetInputStream("animated2.gif");
|
InputStream is = getAssetInputStream("animated2.gif");
|
||||||
Attachment a = new Attachment(is);
|
Attachment a = new Attachment(is);
|
||||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||||
assertEquals(10000, item.getWidth());
|
assertEquals(10000, item.getWidth());
|
||||||
assertEquals(10000, item.getHeight());
|
assertEquals(10000, item.getHeight());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||||
@@ -217,7 +217,7 @@ public class AttachmentControllerIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
||||||
InputStream is = getAssetInputStream("error_large.gif");
|
InputStream is = getAssetInputStream("error_large.gif");
|
||||||
Attachment a = new Attachment(is);
|
Attachment a = new Attachment(is);
|
||||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||||
assertEquals(16384, item.getWidth());
|
assertEquals(16384, item.getWidth());
|
||||||
assertEquals(16384, item.getHeight());
|
assertEquals(16384, item.getHeight());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||||
@@ -232,7 +232,7 @@ public class AttachmentControllerIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getAssetInputStream("error_high.jpg");
|
InputStream is = getAssetInputStream("error_high.jpg");
|
||||||
Attachment a = new Attachment(is);
|
Attachment a = new Attachment(is);
|
||||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||||
assertEquals(1, item.getWidth());
|
assertEquals(1, item.getWidth());
|
||||||
assertEquals(10000, item.getHeight());
|
assertEquals(10000, item.getHeight());
|
||||||
assertEquals(dimensions.minWidth, item.getThumbnailWidth());
|
assertEquals(dimensions.minWidth, item.getThumbnailWidth());
|
||||||
@@ -247,7 +247,7 @@ public class AttachmentControllerIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getAssetInputStream("error_wide.jpg");
|
InputStream is = getAssetInputStream("error_wide.jpg");
|
||||||
Attachment a = new Attachment(is);
|
Attachment a = new Attachment(is);
|
||||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||||
assertEquals(1920, item.getWidth());
|
assertEquals(1920, item.getWidth());
|
||||||
assertEquals(1, item.getHeight());
|
assertEquals(1, item.getHeight());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package org.briarproject.briar.android.attachment;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
|
import org.jsoup.UnsupportedMimeTypeException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
|
import static org.briarproject.briar.api.messaging.MessagingConstants.IMAGE_MIME_TYPES;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class AttachmentCreationTask {
|
||||||
|
|
||||||
|
private static Logger LOG =
|
||||||
|
getLogger(AttachmentCreationTask.class.getName());
|
||||||
|
|
||||||
|
private final MessagingManager messagingManager;
|
||||||
|
private final ContentResolver contentResolver;
|
||||||
|
private final GroupId groupId;
|
||||||
|
private final List<Uri> uris;
|
||||||
|
private final boolean needsSize;
|
||||||
|
@Nullable
|
||||||
|
private AttachmentCreator attachmentCreator;
|
||||||
|
|
||||||
|
private volatile boolean canceled = false;
|
||||||
|
|
||||||
|
AttachmentCreationTask(MessagingManager messagingManager,
|
||||||
|
ContentResolver contentResolver,
|
||||||
|
AttachmentCreator attachmentCreator, GroupId groupId,
|
||||||
|
List<Uri> uris, boolean needsSize) {
|
||||||
|
this.messagingManager = messagingManager;
|
||||||
|
this.contentResolver = contentResolver;
|
||||||
|
this.groupId = groupId;
|
||||||
|
this.uris = uris;
|
||||||
|
this.needsSize = needsSize;
|
||||||
|
this.attachmentCreator = attachmentCreator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
canceled = true;
|
||||||
|
attachmentCreator = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@IoExecutor
|
||||||
|
public void storeAttachments() {
|
||||||
|
for (Uri uri: uris) processUri(uri);
|
||||||
|
if (!canceled && attachmentCreator != null)
|
||||||
|
attachmentCreator.onAttachmentCreationFinished();
|
||||||
|
attachmentCreator = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@IoExecutor
|
||||||
|
private void processUri(Uri uri) {
|
||||||
|
if (canceled) return;
|
||||||
|
try {
|
||||||
|
AttachmentHeader h = storeAttachment(uri);
|
||||||
|
if (attachmentCreator != null) {
|
||||||
|
attachmentCreator
|
||||||
|
.onAttachmentHeaderReceived(uri, h, needsSize);
|
||||||
|
}
|
||||||
|
} catch (DbException | IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
if (attachmentCreator != null) {
|
||||||
|
attachmentCreator.onAttachmentError(uri, e);
|
||||||
|
canceled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IoExecutor
|
||||||
|
private AttachmentHeader storeAttachment(Uri uri)
|
||||||
|
throws IOException, DbException {
|
||||||
|
long start = now();
|
||||||
|
String contentType = contentResolver.getType(uri);
|
||||||
|
if (contentType == null) throw new IOException("null content type");
|
||||||
|
if (!isValidMimeType(contentType))
|
||||||
|
throw new UnsupportedMimeTypeException("", contentType,
|
||||||
|
uri.toString());
|
||||||
|
InputStream is = contentResolver.openInputStream(uri);
|
||||||
|
if (is == null) throw new IOException();
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
AttachmentHeader h = messagingManager
|
||||||
|
.addLocalAttachment(groupId, timestamp, contentType, is);
|
||||||
|
tryToClose(is, LOG, WARNING);
|
||||||
|
logDuration(LOG, "Storing attachment", start);
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidMimeType(@Nullable String mimeType) {
|
||||||
|
if (mimeType == null) return false;
|
||||||
|
for (String supportedType : IMAGE_MIME_TYPES) {
|
||||||
|
if (supportedType.equals(mimeType)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
package org.briarproject.briar.android.attachment;
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.arch.lifecycle.LiveData;
|
||||||
|
import android.arch.lifecycle.MutableLiveData;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
|
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.R;
|
||||||
|
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.MessagingManager;
|
||||||
|
import org.jsoup.UnsupportedMimeTypeException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_IMAGE_SIZE;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public class AttachmentCreator {
|
||||||
|
|
||||||
|
private static Logger LOG = getLogger(AttachmentCreator.class.getName());
|
||||||
|
|
||||||
|
private final Application app;
|
||||||
|
@IoExecutor
|
||||||
|
private final Executor ioExecutor;
|
||||||
|
private final MessagingManager messagingManager;
|
||||||
|
private final AttachmentRetriever controller;
|
||||||
|
|
||||||
|
private final Map<Uri, AttachmentItem> unsentItems =
|
||||||
|
new ConcurrentHashMap<>();
|
||||||
|
private final Map<Uri, MutableLiveData<AttachmentItemResult>>
|
||||||
|
liveDataResult = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private MutableLiveData<Boolean> liveDataFinished = null;
|
||||||
|
@Nullable
|
||||||
|
private AttachmentCreationTask task;
|
||||||
|
|
||||||
|
public AttachmentCreator(Application app, @IoExecutor Executor ioExecutor,
|
||||||
|
MessagingManager messagingManager,
|
||||||
|
AttachmentRetriever controller) {
|
||||||
|
this.app = app;
|
||||||
|
this.ioExecutor = ioExecutor;
|
||||||
|
this.messagingManager = messagingManager;
|
||||||
|
this.controller = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
public AttachmentResult storeAttachments(GroupId groupId,
|
||||||
|
Collection<Uri> uris) {
|
||||||
|
if (task != null && !isStoring()) throw new AssertionError();
|
||||||
|
List<LiveData<AttachmentItemResult>> itemResults = new ArrayList<>();
|
||||||
|
List<Uri> urisToStore = new ArrayList<>();
|
||||||
|
for (Uri uri : uris) {
|
||||||
|
MutableLiveData<AttachmentItemResult> liveData =
|
||||||
|
new MutableLiveData<>();
|
||||||
|
itemResults.add(liveData);
|
||||||
|
liveDataResult.put(uri, liveData);
|
||||||
|
if (unsentItems.containsKey(uri)) {
|
||||||
|
// This can happen due to configuration changes.
|
||||||
|
// So don't create a new attachment, if we have one already.
|
||||||
|
AttachmentItem item = requireNonNull(unsentItems.get(uri));
|
||||||
|
AttachmentItemResult result =
|
||||||
|
new AttachmentItemResult(uri, item);
|
||||||
|
liveData.setValue(result);
|
||||||
|
} else {
|
||||||
|
urisToStore.add(uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boolean needsSize = uris.size() == 1;
|
||||||
|
task = new AttachmentCreationTask(messagingManager,
|
||||||
|
app.getContentResolver(), this, groupId, urisToStore,
|
||||||
|
needsSize);
|
||||||
|
ioExecutor.execute(() -> task.storeAttachments());
|
||||||
|
liveDataFinished = new MutableLiveData<>();
|
||||||
|
return new AttachmentResult(itemResults, liveDataFinished);
|
||||||
|
}
|
||||||
|
|
||||||
|
@IoExecutor
|
||||||
|
void onAttachmentHeaderReceived(Uri uri, AttachmentHeader h,
|
||||||
|
boolean needsSize) {
|
||||||
|
// get and cache AttachmentItem for ImagePreview
|
||||||
|
try {
|
||||||
|
Attachment a = controller.getMessageAttachment(h);
|
||||||
|
AttachmentItem item = controller.getAttachmentItem(h, a, needsSize);
|
||||||
|
if (item.hasError()) throw new IOException();
|
||||||
|
unsentItems.put(uri, item);
|
||||||
|
MutableLiveData<AttachmentItemResult> result =
|
||||||
|
liveDataResult.get(uri);
|
||||||
|
if (result != null) { // might have been cleared on UiThread
|
||||||
|
result.postValue(new AttachmentItemResult(uri, item));
|
||||||
|
}
|
||||||
|
} catch (IOException | DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
onAttachmentError(uri, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IoExecutor
|
||||||
|
void onAttachmentError(Uri uri, Throwable t) {
|
||||||
|
String errorMsg;
|
||||||
|
if (t instanceof UnsupportedMimeTypeException) {
|
||||||
|
String mimeType = ((UnsupportedMimeTypeException) t).getMimeType();
|
||||||
|
errorMsg = app.getString(
|
||||||
|
R.string.image_attach_error_invalid_mime_type, mimeType);
|
||||||
|
} else if (t instanceof FileTooBigException) {
|
||||||
|
int mb = MAX_IMAGE_SIZE / 1024 / 1024;
|
||||||
|
errorMsg = app.getString(R.string.image_attach_error_too_big, mb);
|
||||||
|
} else {
|
||||||
|
errorMsg = null; // generic error
|
||||||
|
}
|
||||||
|
MutableLiveData<AttachmentItemResult> result = liveDataResult.get(uri);
|
||||||
|
if (result != null)
|
||||||
|
result.postValue(new AttachmentItemResult(errorMsg));
|
||||||
|
// expect to receive a cancel from the UI
|
||||||
|
}
|
||||||
|
|
||||||
|
@IoExecutor
|
||||||
|
void onAttachmentCreationFinished() {
|
||||||
|
if (liveDataFinished != null) liveDataFinished.postValue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
public List<AttachmentHeader> getAttachmentHeadersForSending() {
|
||||||
|
List<AttachmentHeader> headers =
|
||||||
|
new ArrayList<>(unsentItems.values().size());
|
||||||
|
for (AttachmentItem item : unsentItems.values()) {
|
||||||
|
headers.add(item.getHeader());
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the attachments as sent and adds the items to the cache for display
|
||||||
|
*
|
||||||
|
* @param id The MessageId of the sent message.
|
||||||
|
*/
|
||||||
|
public void onAttachmentsSent(MessageId id) {
|
||||||
|
controller.cachePut(id, new ArrayList<>(unsentItems.values()));
|
||||||
|
resetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
public void cancel() {
|
||||||
|
if (task == null) throw new AssertionError();
|
||||||
|
task.cancel();
|
||||||
|
// let observers know that they can remove themselves
|
||||||
|
for (MutableLiveData<AttachmentItemResult> liveData : liveDataResult
|
||||||
|
.values()) {
|
||||||
|
if (liveData.getValue() == null) {
|
||||||
|
liveData.setValue(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (liveDataFinished != null) liveDataFinished.setValue(false);
|
||||||
|
deleteUnsentAttachments();
|
||||||
|
resetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void resetState() {
|
||||||
|
task = null;
|
||||||
|
liveDataResult.clear();
|
||||||
|
liveDataFinished = null;
|
||||||
|
unsentItems.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
public void deleteUnsentAttachments() {
|
||||||
|
List<AttachmentItem> itemsToDelete =
|
||||||
|
new ArrayList<>(unsentItems.values());
|
||||||
|
ioExecutor.execute(() -> {
|
||||||
|
for (AttachmentItem item : itemsToDelete) {
|
||||||
|
try {
|
||||||
|
messagingManager.removeAttachment(item.getHeader());
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isStoring() {
|
||||||
|
return liveDataFinished != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,11 +1,16 @@
|
|||||||
package org.briarproject.briar.android.conversation;
|
package org.briarproject.briar.android.attachment;
|
||||||
|
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.support.annotation.VisibleForTesting;
|
import android.support.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
|
||||||
class AttachmentDimensions {
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
public class AttachmentDimensions {
|
||||||
|
|
||||||
final int defaultSize;
|
final int defaultSize;
|
||||||
final int minWidth, maxWidth;
|
final int minWidth, maxWidth;
|
||||||
@@ -21,7 +26,7 @@ class AttachmentDimensions {
|
|||||||
this.maxHeight = maxHeight;
|
this.maxHeight = maxHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
static AttachmentDimensions getAttachmentDimensions(Resources res) {
|
public static AttachmentDimensions getAttachmentDimensions(Resources res) {
|
||||||
int defaultSize =
|
int defaultSize =
|
||||||
res.getDimensionPixelSize(R.dimen.message_bubble_image_default);
|
res.getDimensionPixelSize(R.dimen.message_bubble_image_default);
|
||||||
int minWidth = res.getDimensionPixelSize(
|
int minWidth = res.getDimensionPixelSize(
|
||||||
@@ -33,7 +38,7 @@ class AttachmentDimensions {
|
|||||||
int maxHeight = res.getDimensionPixelSize(
|
int maxHeight = res.getDimensionPixelSize(
|
||||||
R.dimen.message_bubble_image_max_height);
|
R.dimen.message_bubble_image_max_height);
|
||||||
return new AttachmentDimensions(defaultSize, minWidth, maxWidth,
|
return new AttachmentDimensions(defaultSize, minWidth, maxWidth,
|
||||||
minHeight, minHeight);
|
minHeight, maxHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.briar.android.conversation;
|
package org.briarproject.briar.android.attachment;
|
||||||
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
@@ -12,6 +12,8 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class AttachmentItem implements Parcelable {
|
public class AttachmentItem implements Parcelable {
|
||||||
@@ -57,8 +59,8 @@ public class AttachmentItem implements Parcelable {
|
|||||||
MessageId messageId = new MessageId(messageIdByte);
|
MessageId messageId = new MessageId(messageIdByte);
|
||||||
width = in.readInt();
|
width = in.readInt();
|
||||||
height = in.readInt();
|
height = in.readInt();
|
||||||
String mimeType = in.readString();
|
String mimeType = requireNonNull(in.readString());
|
||||||
extension = in.readString();
|
extension = requireNonNull(in.readString());
|
||||||
thumbnailWidth = in.readInt();
|
thumbnailWidth = in.readInt();
|
||||||
thumbnailHeight = in.readInt();
|
thumbnailHeight = in.readInt();
|
||||||
hasError = in.readByte() != 0;
|
hasError = in.readByte() != 0;
|
||||||
@@ -82,27 +84,27 @@ public class AttachmentItem implements Parcelable {
|
|||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getMimeType() {
|
public String getMimeType() {
|
||||||
return header.getContentType();
|
return header.getContentType();
|
||||||
}
|
}
|
||||||
|
|
||||||
String getExtension() {
|
public String getExtension() {
|
||||||
return extension;
|
return extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getThumbnailWidth() {
|
public int getThumbnailWidth() {
|
||||||
return thumbnailWidth;
|
return thumbnailWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getThumbnailHeight() {
|
public int getThumbnailHeight() {
|
||||||
return thumbnailHeight;
|
return thumbnailHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasError() {
|
public boolean hasError() {
|
||||||
return hasError;
|
return hasError;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getTransitionName() {
|
public String getTransitionName() {
|
||||||
return String.valueOf(instanceId);
|
return String.valueOf(instanceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.briar.android.conversation;
|
package org.briarproject.briar.android.attachment;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class AttachmentResult {
|
public class AttachmentItemResult {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final Uri uri;
|
private final Uri uri;
|
||||||
@@ -18,13 +18,13 @@ public class AttachmentResult {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private final String errorMsg;
|
private final String errorMsg;
|
||||||
|
|
||||||
public AttachmentResult(Uri uri, AttachmentItem item) {
|
public AttachmentItemResult(Uri uri, AttachmentItem item) {
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
this.item = item;
|
this.item = item;
|
||||||
this.errorMsg = null;
|
this.errorMsg = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AttachmentResult(@Nullable String errorMsg) {
|
public AttachmentItemResult(@Nullable String errorMsg) {
|
||||||
this.uri = null;
|
this.uri = null;
|
||||||
this.item = null;
|
this.item = null;
|
||||||
this.errorMsg = errorMsg;
|
this.errorMsg = errorMsg;
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package org.briarproject.briar.android.attachment;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
public interface AttachmentManager{
|
||||||
|
|
||||||
|
AttachmentResult storeAttachments(Collection<Uri> uri);
|
||||||
|
|
||||||
|
List<AttachmentHeader> getAttachmentHeadersForSending();
|
||||||
|
|
||||||
|
void cancel();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package org.briarproject.briar.android.attachment;
|
||||||
|
|
||||||
|
import android.arch.lifecycle.LiveData;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
public class AttachmentResult {
|
||||||
|
|
||||||
|
private final Collection<LiveData<AttachmentItemResult>> itemResults;
|
||||||
|
private final LiveData<Boolean> finished;
|
||||||
|
|
||||||
|
public AttachmentResult(
|
||||||
|
Collection<LiveData<AttachmentItemResult>> itemResults,
|
||||||
|
LiveData<Boolean> finished) {
|
||||||
|
this.itemResults = itemResults;
|
||||||
|
this.finished = finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<LiveData<AttachmentItemResult>> getItemResults() {
|
||||||
|
return itemResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<Boolean> getFinished() {
|
||||||
|
return finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
package org.briarproject.briar.android.conversation;
|
package org.briarproject.briar.android.attachment;
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.BitmapFactory.Options;
|
import android.graphics.BitmapFactory.Options;
|
||||||
import android.net.Uri;
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.VisibleForTesting;
|
import android.support.annotation.VisibleForTesting;
|
||||||
import android.support.media.ExifInterface;
|
import android.support.media.ExifInterface;
|
||||||
@@ -15,9 +13,8 @@ import org.briarproject.bramble.api.Pair;
|
|||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.android.conversation.ImageHelper.DecodeResult;
|
import org.briarproject.briar.android.attachment.ImageHelper.DecodeResult;
|
||||||
import org.briarproject.briar.api.messaging.Attachment;
|
import org.briarproject.briar.api.messaging.Attachment;
|
||||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
@@ -38,20 +35,18 @@ import static android.support.media.ExifInterface.ORIENTATION_TRANSVERSE;
|
|||||||
import static android.support.media.ExifInterface.TAG_IMAGE_LENGTH;
|
import static android.support.media.ExifInterface.TAG_IMAGE_LENGTH;
|
||||||
import static android.support.media.ExifInterface.TAG_IMAGE_WIDTH;
|
import static android.support.media.ExifInterface.TAG_IMAGE_WIDTH;
|
||||||
import static android.support.media.ExifInterface.TAG_ORIENTATION;
|
import static android.support.media.ExifInterface.TAG_ORIENTATION;
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
import static org.briarproject.briar.api.messaging.MessagingConstants.IMAGE_MIME_TYPES;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class AttachmentController {
|
public class AttachmentRetriever {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(AttachmentController.class.getName());
|
getLogger(AttachmentRetriever.class.getName());
|
||||||
private static final int READ_LIMIT = 1024 * 8192;
|
private static final int READ_LIMIT = 1024 * 8192;
|
||||||
|
|
||||||
private final MessagingManager messagingManager;
|
private final MessagingManager messagingManager;
|
||||||
@@ -60,12 +55,11 @@ class AttachmentController {
|
|||||||
private final int minWidth, maxWidth;
|
private final int minWidth, maxWidth;
|
||||||
private final int minHeight, maxHeight;
|
private final int minHeight, maxHeight;
|
||||||
|
|
||||||
private final Map<Uri, AttachmentItem> unsentItems =
|
|
||||||
new ConcurrentHashMap<>();
|
|
||||||
private final Map<MessageId, List<AttachmentItem>> attachmentCache =
|
private final Map<MessageId, List<AttachmentItem>> attachmentCache =
|
||||||
new ConcurrentHashMap<>();
|
new ConcurrentHashMap<>();
|
||||||
|
|
||||||
AttachmentController(MessagingManager messagingManager,
|
@VisibleForTesting
|
||||||
|
AttachmentRetriever(MessagingManager messagingManager,
|
||||||
AttachmentDimensions dimensions, ImageHelper imageHelper) {
|
AttachmentDimensions dimensions, ImageHelper imageHelper) {
|
||||||
this.messagingManager = messagingManager;
|
this.messagingManager = messagingManager;
|
||||||
this.imageHelper = imageHelper;
|
this.imageHelper = imageHelper;
|
||||||
@@ -76,7 +70,7 @@ class AttachmentController {
|
|||||||
maxHeight = dimensions.maxHeight;
|
maxHeight = dimensions.maxHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
AttachmentController(MessagingManager messagingManager,
|
public AttachmentRetriever(MessagingManager messagingManager,
|
||||||
AttachmentDimensions dimensions) {
|
AttachmentDimensions dimensions) {
|
||||||
this(messagingManager, dimensions, new ImageHelper() {
|
this(messagingManager, dimensions, new ImageHelper() {
|
||||||
@Override
|
@Override
|
||||||
@@ -99,17 +93,17 @@ class AttachmentController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void put(MessageId messageId, List<AttachmentItem> attachments) {
|
public void cachePut(MessageId messageId, List<AttachmentItem> attachments) {
|
||||||
attachmentCache.put(messageId, attachments);
|
attachmentCache.put(messageId, attachments);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
List<AttachmentItem> get(MessageId messageId) {
|
public List<AttachmentItem> cacheGet(MessageId messageId) {
|
||||||
return attachmentCache.get(messageId);
|
return attachmentCache.get(messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
List<Pair<AttachmentHeader, Attachment>> getMessageAttachments(
|
public List<Pair<AttachmentHeader, Attachment>> getMessageAttachments(
|
||||||
List<AttachmentHeader> headers) throws DbException {
|
List<AttachmentHeader> headers) throws DbException {
|
||||||
long start = now();
|
long start = now();
|
||||||
List<Pair<AttachmentHeader, Attachment>> attachments =
|
List<Pair<AttachmentHeader, Attachment>> attachments =
|
||||||
@@ -118,86 +112,13 @@ class AttachmentController {
|
|||||||
Attachment a = messagingManager.getAttachment(h.getMessageId());
|
Attachment a = messagingManager.getAttachment(h.getMessageId());
|
||||||
attachments.add(new Pair<>(h, a));
|
attachments.add(new Pair<>(h, a));
|
||||||
}
|
}
|
||||||
logDuration(LOG, "Loading attachment", start);
|
logDuration(LOG, "Loading attachments", start);
|
||||||
return attachments;
|
return attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
AttachmentItem createAttachmentHeader(ContentResolver contentResolver,
|
Attachment getMessageAttachment(AttachmentHeader h) throws DbException {
|
||||||
GroupId groupId, Uri uri, boolean needsSize)
|
return messagingManager.getAttachment(h.getMessageId());
|
||||||
throws IOException, DbException {
|
|
||||||
if (unsentItems.containsKey(uri)) {
|
|
||||||
// This can happen due to configuration (screen orientation) change.
|
|
||||||
// So don't create a new attachment, if we have one already.
|
|
||||||
return requireNonNull(unsentItems.get(uri));
|
|
||||||
}
|
|
||||||
long start = now();
|
|
||||||
InputStream is = contentResolver.openInputStream(uri);
|
|
||||||
if (is == null) throw new IOException();
|
|
||||||
String contentType = contentResolver.getType(uri);
|
|
||||||
if (contentType == null) throw new IOException("null content type");
|
|
||||||
long timestamp = System.currentTimeMillis();
|
|
||||||
AttachmentHeader h = messagingManager
|
|
||||||
.addLocalAttachment(groupId, timestamp, contentType, is);
|
|
||||||
tryToClose(is, LOG, WARNING);
|
|
||||||
logDuration(LOG, "Storing attachment", start);
|
|
||||||
// get and store AttachmentItem for ImagePreview
|
|
||||||
AttachmentItem item =
|
|
||||||
getAttachmentItem(contentResolver, uri, h, needsSize);
|
|
||||||
if (item.hasError()) throw new IOException();
|
|
||||||
unsentItems.put(uri, item);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isValidMimeType(@Nullable String mimeType) {
|
|
||||||
if (mimeType == null) return false;
|
|
||||||
for (String supportedType : IMAGE_MIME_TYPES) {
|
|
||||||
if (supportedType.equals(mimeType)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@DatabaseExecutor
|
|
||||||
void deleteUnsentAttachments() {
|
|
||||||
for (AttachmentItem item : unsentItems.values()) {
|
|
||||||
try {
|
|
||||||
messagingManager.removeAttachment(item.getHeader());
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unsentItems.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<AttachmentHeader> getUnsentAttachmentHeaders() {
|
|
||||||
List<AttachmentHeader> headers =
|
|
||||||
new ArrayList<>(unsentItems.values().size());
|
|
||||||
for (AttachmentItem item : unsentItems.values()) {
|
|
||||||
headers.add(item.getHeader());
|
|
||||||
}
|
|
||||||
return headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks the attachments as sent and adds the items to the cache for display
|
|
||||||
* @param id The MessageId of the sent message.
|
|
||||||
*/
|
|
||||||
void onAttachmentsSent(MessageId id) {
|
|
||||||
attachmentCache.put(id, new ArrayList<>(unsentItems.values()));
|
|
||||||
unsentItems.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@DatabaseExecutor
|
|
||||||
private AttachmentItem getAttachmentItem(ContentResolver contentResolver,
|
|
||||||
Uri uri, AttachmentHeader h, boolean needsSize) throws IOException {
|
|
||||||
InputStream is = null;
|
|
||||||
try {
|
|
||||||
is = contentResolver.openInputStream(uri);
|
|
||||||
if (is == null) throw new IOException();
|
|
||||||
return getAttachmentItem(h, new Attachment(is), needsSize);
|
|
||||||
} finally {
|
|
||||||
if (is != null) tryToClose(is, LOG, WARNING);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -205,7 +126,7 @@ class AttachmentController {
|
|||||||
* <p>
|
* <p>
|
||||||
* Note: This closes the {@link Attachment}'s {@link InputStream}.
|
* Note: This closes the {@link Attachment}'s {@link InputStream}.
|
||||||
*/
|
*/
|
||||||
List<AttachmentItem> getAttachmentItems(
|
public List<AttachmentItem> getAttachmentItems(
|
||||||
List<Pair<AttachmentHeader, Attachment>> attachments) {
|
List<Pair<AttachmentHeader, Attachment>> attachments) {
|
||||||
boolean needsSize = attachments.size() == 1;
|
boolean needsSize = attachments.size() == 1;
|
||||||
List<AttachmentItem> items = new ArrayList<>(attachments.size());
|
List<AttachmentItem> items = new ArrayList<>(attachments.size());
|
||||||
@@ -221,7 +142,6 @@ class AttachmentController {
|
|||||||
* Creates an {@link AttachmentItem} from the {@link Attachment}'s
|
* Creates an {@link AttachmentItem} from the {@link Attachment}'s
|
||||||
* {@link InputStream} which will be closed when this method returns.
|
* {@link InputStream} which will be closed when this method returns.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
|
||||||
AttachmentItem getAttachmentItem(AttachmentHeader h, Attachment a,
|
AttachmentItem getAttachmentItem(AttachmentHeader h, Attachment a,
|
||||||
boolean needsSize) {
|
boolean needsSize) {
|
||||||
if (!needsSize) {
|
if (!needsSize) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.briar.android.conversation;
|
package org.briarproject.briar.android.attachment;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
@@ -52,6 +52,8 @@ import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
|
|||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
|
import org.briarproject.briar.android.attachment.AttachmentRetriever;
|
||||||
import org.briarproject.briar.android.blog.BlogActivity;
|
import org.briarproject.briar.android.blog.BlogActivity;
|
||||||
import org.briarproject.briar.android.conversation.ConversationVisitor.AttachmentCache;
|
import org.briarproject.briar.android.conversation.ConversationVisitor.AttachmentCache;
|
||||||
import org.briarproject.briar.android.conversation.ConversationVisitor.TextCache;
|
import org.briarproject.briar.android.conversation.ConversationVisitor.TextCache;
|
||||||
@@ -183,7 +185,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
loadMessages();
|
loadMessages();
|
||||||
};
|
};
|
||||||
|
|
||||||
private AttachmentController attachmentController;
|
private AttachmentRetriever attachmentRetriever;
|
||||||
private ConversationViewModel viewModel;
|
private ConversationViewModel viewModel;
|
||||||
private ConversationVisitor visitor;
|
private ConversationVisitor visitor;
|
||||||
private ConversationAdapter adapter;
|
private ConversationAdapter adapter;
|
||||||
@@ -218,7 +220,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
|
|
||||||
viewModel = ViewModelProviders.of(this, viewModelFactory)
|
viewModel = ViewModelProviders.of(this, viewModelFactory)
|
||||||
.get(ConversationViewModel.class);
|
.get(ConversationViewModel.class);
|
||||||
attachmentController = viewModel.getAttachmentController();
|
attachmentRetriever = viewModel.getAttachmentRetriever();
|
||||||
|
|
||||||
setContentView(R.layout.activity_conversation);
|
setContentView(R.layout.activity_conversation);
|
||||||
|
|
||||||
@@ -456,13 +458,13 @@ public class ConversationActivity extends BriarActivity
|
|||||||
// If the message has a single image, load its size - for multiple
|
// If the message has a single image, load its size - for multiple
|
||||||
// images we use a grid so the size is fixed
|
// images we use a grid so the size is fixed
|
||||||
if (h.getAttachmentHeaders().size() == 1) {
|
if (h.getAttachmentHeaders().size() == 1) {
|
||||||
List<AttachmentItem> items = attachmentController.get(id);
|
List<AttachmentItem> items = attachmentRetriever.cacheGet(id);
|
||||||
if (items == null) {
|
if (items == null) {
|
||||||
LOG.info("Eagerly loading image size for latest message");
|
LOG.info("Eagerly loading image size for latest message");
|
||||||
items = attachmentController.getAttachmentItems(
|
items = attachmentRetriever.getAttachmentItems(
|
||||||
attachmentController.getMessageAttachments(
|
attachmentRetriever.getMessageAttachments(
|
||||||
h.getAttachmentHeaders()));
|
h.getAttachmentHeaders()));
|
||||||
attachmentController.put(id, items);
|
attachmentRetriever.cachePut(id, items);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -544,10 +546,10 @@ public class ConversationActivity extends BriarActivity
|
|||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
List<Pair<AttachmentHeader, Attachment>> attachments =
|
List<Pair<AttachmentHeader, Attachment>> attachments =
|
||||||
attachmentController.getMessageAttachments(headers);
|
attachmentRetriever.getMessageAttachments(headers);
|
||||||
// TODO move getting the items off to IoExecutor, if size == 1
|
// TODO move getting the items off to IoExecutor, if size == 1
|
||||||
List<AttachmentItem> items =
|
List<AttachmentItem> items =
|
||||||
attachmentController.getAttachmentItems(attachments);
|
attachmentRetriever.getAttachmentItems(attachments);
|
||||||
displayMessageAttachments(messageId, items);
|
displayMessageAttachments(messageId, items);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
@@ -558,7 +560,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
private void displayMessageAttachments(MessageId m,
|
private void displayMessageAttachments(MessageId m,
|
||||||
List<AttachmentItem> items) {
|
List<AttachmentItem> items) {
|
||||||
runOnUiThreadUnlessDestroyed(() -> {
|
runOnUiThreadUnlessDestroyed(() -> {
|
||||||
attachmentController.put(m, items);
|
attachmentRetriever.cachePut(m, items);
|
||||||
Pair<Integer, ConversationMessageItem> pair =
|
Pair<Integer, ConversationMessageItem> pair =
|
||||||
adapter.getMessageItem(m);
|
adapter.getMessageItem(m);
|
||||||
if (pair != null) {
|
if (pair != null) {
|
||||||
@@ -903,7 +905,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
@Override
|
@Override
|
||||||
public List<AttachmentItem> getAttachmentItems(MessageId m,
|
public List<AttachmentItem> getAttachmentItems(MessageId m,
|
||||||
List<AttachmentHeader> headers) {
|
List<AttachmentHeader> headers) {
|
||||||
List<AttachmentItem> attachments = attachmentController.get(m);
|
List<AttachmentItem> attachments = attachmentRetriever.cacheGet(m);
|
||||||
if (attachments == null) {
|
if (attachments == null) {
|
||||||
loadMessageAttachments(m, headers);
|
loadMessageAttachments(m, headers);
|
||||||
return emptyList();
|
return emptyList();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.support.annotation.UiThread;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.briar.android.conversation;
|
|||||||
import android.support.annotation.LayoutRes;
|
import android.support.annotation.LayoutRes;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
|
|
||||||
import static android.support.constraint.ConstraintSet.WRAP_CONTENT;
|
import static android.support.constraint.ConstraintSet.WRAP_CONTENT;
|
||||||
import static android.support.v4.content.ContextCompat.getColor;
|
import static android.support.v4.content.ContextCompat.getColor;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import android.arch.lifecycle.AndroidViewModel;
|
|||||||
import android.arch.lifecycle.LiveData;
|
import android.arch.lifecycle.LiveData;
|
||||||
import android.arch.lifecycle.MutableLiveData;
|
import android.arch.lifecycle.MutableLiveData;
|
||||||
import android.arch.lifecycle.Transformations;
|
import android.arch.lifecycle.Transformations;
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
@@ -20,26 +19,26 @@ import org.briarproject.bramble.api.db.DbException;
|
|||||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||||
import org.briarproject.bramble.api.db.TransactionManager;
|
import org.briarproject.bramble.api.db.TransactionManager;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.briar.android.attachment.AttachmentCreator;
|
||||||
import org.briarproject.briar.R;
|
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.util.UiUtils;
|
import org.briarproject.briar.android.util.UiUtils;
|
||||||
import org.briarproject.briar.android.view.TextAttachmentController.AttachmentManager;
|
|
||||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
import org.briarproject.briar.api.messaging.FileTooBigException;
|
|
||||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
import org.briarproject.briar.api.messaging.PrivateMessage;
|
import org.briarproject.briar.api.messaging.PrivateMessage;
|
||||||
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
|
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
|
||||||
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -53,14 +52,12 @@ import static java.util.logging.Logger.getLogger;
|
|||||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
import static org.briarproject.briar.android.conversation.AttachmentDimensions.getAttachmentDimensions;
|
import static org.briarproject.briar.android.attachment.AttachmentDimensions.getAttachmentDimensions;
|
||||||
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce;
|
|
||||||
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_IMAGE_SIZE;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class ConversationViewModel extends AndroidViewModel implements
|
public class ConversationViewModel extends AndroidViewModel
|
||||||
AttachmentManager {
|
implements AttachmentManager {
|
||||||
|
|
||||||
private static Logger LOG =
|
private static Logger LOG =
|
||||||
getLogger(ConversationViewModel.class.getName());
|
getLogger(ConversationViewModel.class.getName());
|
||||||
@@ -78,10 +75,13 @@ public class ConversationViewModel extends AndroidViewModel implements
|
|||||||
private final ContactManager contactManager;
|
private final ContactManager contactManager;
|
||||||
private final SettingsManager settingsManager;
|
private final SettingsManager settingsManager;
|
||||||
private final PrivateMessageFactory privateMessageFactory;
|
private final PrivateMessageFactory privateMessageFactory;
|
||||||
private final AttachmentController attachmentController;
|
private final AttachmentRetriever attachmentRetriever;
|
||||||
|
private final AttachmentCreator attachmentCreator;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private ContactId contactId = null;
|
private ContactId contactId = null;
|
||||||
|
@Nullable
|
||||||
|
private volatile GroupId messagingGroupId = null;
|
||||||
private final MutableLiveData<Contact> contact = new MutableLiveData<>();
|
private final MutableLiveData<Contact> contact = new MutableLiveData<>();
|
||||||
private final LiveData<AuthorId> contactAuthorId =
|
private final LiveData<AuthorId> contactAuthorId =
|
||||||
Transformations.map(contact, c -> c.getAuthor().getId());
|
Transformations.map(contact, c -> c.getAuthor().getId());
|
||||||
@@ -97,15 +97,14 @@ public class ConversationViewModel extends AndroidViewModel implements
|
|||||||
new MutableLiveData<>();
|
new MutableLiveData<>();
|
||||||
private final MutableLiveData<Boolean> contactDeleted =
|
private final MutableLiveData<Boolean> contactDeleted =
|
||||||
new MutableLiveData<>();
|
new MutableLiveData<>();
|
||||||
private final MutableLiveData<GroupId> messagingGroupId =
|
|
||||||
new MutableLiveData<>();
|
|
||||||
private final MutableLiveData<PrivateMessageHeader> addedHeader =
|
private final MutableLiveData<PrivateMessageHeader> addedHeader =
|
||||||
new MutableLiveData<>();
|
new MutableLiveData<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ConversationViewModel(Application application,
|
ConversationViewModel(Application application,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
@CryptoExecutor Executor cryptoExecutor, TransactionManager db,
|
@CryptoExecutor Executor cryptoExecutor,
|
||||||
|
@IoExecutor Executor ioExecutor, TransactionManager db,
|
||||||
MessagingManager messagingManager, ContactManager contactManager,
|
MessagingManager messagingManager, ContactManager contactManager,
|
||||||
SettingsManager settingsManager,
|
SettingsManager settingsManager,
|
||||||
PrivateMessageFactory privateMessageFactory) {
|
PrivateMessageFactory privateMessageFactory) {
|
||||||
@@ -117,15 +116,17 @@ public class ConversationViewModel extends AndroidViewModel implements
|
|||||||
this.contactManager = contactManager;
|
this.contactManager = contactManager;
|
||||||
this.settingsManager = settingsManager;
|
this.settingsManager = settingsManager;
|
||||||
this.privateMessageFactory = privateMessageFactory;
|
this.privateMessageFactory = privateMessageFactory;
|
||||||
this.attachmentController = new AttachmentController(messagingManager,
|
this.attachmentRetriever = new AttachmentRetriever(messagingManager,
|
||||||
getAttachmentDimensions(application.getResources()));
|
getAttachmentDimensions(application.getResources()));
|
||||||
|
this.attachmentCreator = new AttachmentCreator(getApplication(),
|
||||||
|
ioExecutor, messagingManager, attachmentRetriever);
|
||||||
contactDeleted.setValue(false);
|
contactDeleted.setValue(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCleared() {
|
protected void onCleared() {
|
||||||
super.onCleared();
|
super.onCleared();
|
||||||
attachmentController.deleteUnsentAttachments();
|
attachmentCreator.deleteUnsentAttachments();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -149,6 +150,10 @@ public class ConversationViewModel extends AndroidViewModel implements
|
|||||||
contact.postValue(c);
|
contact.postValue(c);
|
||||||
logDuration(LOG, "Loading contact", start);
|
logDuration(LOG, "Loading contact", start);
|
||||||
start = now();
|
start = now();
|
||||||
|
messagingGroupId =
|
||||||
|
messagingManager.getConversationId(contactId);
|
||||||
|
logDuration(LOG, "Load conversation GroupId", start);
|
||||||
|
start = now();
|
||||||
checkFeaturesAndOnboarding(contactId);
|
checkFeaturesAndOnboarding(contactId);
|
||||||
logDuration(LOG, "Checking for image support", start);
|
logDuration(LOG, "Checking for image support", start);
|
||||||
} catch (NoSuchContactException e) {
|
} catch (NoSuchContactException e) {
|
||||||
@@ -185,73 +190,29 @@ public class ConversationViewModel extends AndroidViewModel implements
|
|||||||
|
|
||||||
void sendMessage(@Nullable String text,
|
void sendMessage(@Nullable String text,
|
||||||
List<AttachmentHeader> attachmentHeaders, long timestamp) {
|
List<AttachmentHeader> attachmentHeaders, long timestamp) {
|
||||||
if (messagingGroupId.getValue() == null) loadGroupId();
|
GroupId groupId = messagingGroupId;
|
||||||
observeForeverOnce(messagingGroupId, groupId -> {
|
if (groupId == null) throw new IllegalStateException();
|
||||||
if (groupId == null) return;
|
createMessage(groupId, text, attachmentHeaders, timestamp);
|
||||||
createMessage(groupId, text, attachmentHeaders, timestamp);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LiveData<AttachmentResult> storeAttachment(Uri uri,
|
@UiThread
|
||||||
boolean needsSize) {
|
public AttachmentResult storeAttachments(Collection<Uri> uris) {
|
||||||
// use LiveData to not keep references to view scope
|
GroupId groupId = messagingGroupId;
|
||||||
MutableLiveData<AttachmentResult> result = new MutableLiveData<>();
|
if (groupId == null) throw new IllegalStateException();
|
||||||
// check first if mime type is supported
|
return attachmentCreator.storeAttachments(groupId, uris);
|
||||||
ContentResolver contentResolver =
|
|
||||||
getApplication().getContentResolver();
|
|
||||||
String mimeType = contentResolver.getType(uri);
|
|
||||||
if (!attachmentController.isValidMimeType(mimeType)) {
|
|
||||||
String errorMsg = getApplication().getString(
|
|
||||||
R.string.image_attach_error_invalid_mime_type, mimeType);
|
|
||||||
result.setValue(new AttachmentResult(errorMsg));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (messagingGroupId.getValue() == null) loadGroupId();
|
|
||||||
observeForeverOnce(messagingGroupId, groupId -> dbExecutor.execute(()
|
|
||||||
-> {
|
|
||||||
if (groupId == null) throw new IllegalStateException();
|
|
||||||
long start = now();
|
|
||||||
try {
|
|
||||||
AttachmentItem item = attachmentController
|
|
||||||
.createAttachmentHeader(contentResolver, groupId, uri,
|
|
||||||
needsSize);
|
|
||||||
result.postValue(new AttachmentResult(uri, item));
|
|
||||||
} catch(FileTooBigException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
int mb = MAX_IMAGE_SIZE / 1024 / 1024;
|
|
||||||
String errorMsg = getApplication()
|
|
||||||
.getString(R.string.image_attach_error_too_big, mb);
|
|
||||||
result.postValue(new AttachmentResult(errorMsg));
|
|
||||||
} catch (DbException | IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
result.postValue(new AttachmentResult(null));
|
|
||||||
}
|
|
||||||
logDuration(LOG, "Storing attachment", start);
|
|
||||||
}));
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AttachmentHeader> getAttachmentHeaders() {
|
@UiThread
|
||||||
return attachmentController.getUnsentAttachmentHeaders();
|
public List<AttachmentHeader> getAttachmentHeadersForSending() {
|
||||||
|
return attachmentCreator.getAttachmentHeadersForSending();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeAttachments() {
|
@UiThread
|
||||||
dbExecutor.execute(attachmentController::deleteUnsentAttachments);
|
public void cancel() {
|
||||||
}
|
attachmentCreator.cancel();
|
||||||
|
|
||||||
private void loadGroupId() {
|
|
||||||
if (contactId == null) throw new IllegalStateException();
|
|
||||||
dbExecutor.execute(() -> {
|
|
||||||
try {
|
|
||||||
messagingGroupId.postValue(
|
|
||||||
messagingManager.getConversationId(contactId));
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
@@ -337,7 +298,7 @@ public class ConversationViewModel extends AndroidViewModel implements
|
|||||||
message.getId(), message.getGroupId(),
|
message.getId(), message.getGroupId(),
|
||||||
message.getTimestamp(), true, true, false, false,
|
message.getTimestamp(), true, true, false, false,
|
||||||
text != null, attachments);
|
text != null, attachments);
|
||||||
attachmentController.onAttachmentsSent(m.getMessage().getId());
|
attachmentCreator.onAttachmentsSent(m.getMessage().getId());
|
||||||
// TODO add text to cache when available here
|
// TODO add text to cache when available here
|
||||||
addedHeader.postValue(h);
|
addedHeader.postValue(h);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
@@ -351,8 +312,8 @@ public class ConversationViewModel extends AndroidViewModel implements
|
|||||||
addedHeader.setValue(null);
|
addedHeader.setValue(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
AttachmentController getAttachmentController() {
|
AttachmentRetriever getAttachmentRetriever() {
|
||||||
return attachmentController;
|
return attachmentRetriever;
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveData<Contact> getContact() {
|
LiveData<Contact> getContact() {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.support.annotation.UiThread;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
import org.briarproject.briar.api.blog.BlogInvitationRequest;
|
import org.briarproject.briar.api.blog.BlogInvitationRequest;
|
||||||
import org.briarproject.briar.api.blog.BlogInvitationResponse;
|
import org.briarproject.briar.api.blog.BlogInvitationResponse;
|
||||||
import org.briarproject.briar.api.conversation.ConversationMessageVisitor;
|
import org.briarproject.briar.api.conversation.ConversationMessageVisitor;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
|||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
|
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
|
||||||
import org.briarproject.briar.android.view.PullDownLayout;
|
import org.briarproject.briar.android.view.PullDownLayout;
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import android.view.WindowManager;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
import org.briarproject.briar.android.conversation.glide.Radii;
|
import org.briarproject.briar.android.conversation.glide.Radii;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import com.github.chrisbanes.photoview.PhotoView;
|
|||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.BaseActivity;
|
import org.briarproject.briar.android.activity.BaseActivity;
|
||||||
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
import org.briarproject.briar.android.conversation.glide.GlideApp;
|
import org.briarproject.briar.android.conversation.glide.GlideApp;
|
||||||
|
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
import javax.annotation.ParametersAreNonnullByDefault;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import com.bumptech.glide.load.Transformation;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
import org.briarproject.briar.android.conversation.glide.BriarImageTransformation;
|
import org.briarproject.briar.android.conversation.glide.BriarImageTransformation;
|
||||||
import org.briarproject.briar.android.conversation.glide.GlideApp;
|
import org.briarproject.briar.android.conversation.glide.GlideApp;
|
||||||
import org.briarproject.briar.android.conversation.glide.Radii;
|
import org.briarproject.briar.android.conversation.glide.Radii;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.briarproject.bramble.api.db.DbException;
|
|||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||||
import org.briarproject.briar.api.messaging.Attachment;
|
import org.briarproject.briar.api.messaging.Attachment;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import org.briarproject.bramble.api.db.DatabaseExecutor;
|
|||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.android.conversation.AttachmentItem;
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package org.briarproject.briar.android.conversation.glide;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.android.conversation.AttachmentItem;
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import com.bumptech.glide.module.AppGlideModule;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.android.BriarApplication;
|
import org.briarproject.briar.android.BriarApplication;
|
||||||
import org.briarproject.briar.android.conversation.AttachmentItem;
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import com.bumptech.glide.signature.ObjectKey;
|
|||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.briar.android.BriarApplication;
|
import org.briarproject.briar.android.BriarApplication;
|
||||||
import org.briarproject.briar.android.conversation.AttachmentItem;
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import com.bumptech.glide.load.model.MultiModelLoaderFactory;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.android.BriarApplication;
|
import org.briarproject.briar.android.BriarApplication;
|
||||||
import org.briarproject.briar.android.conversation.AttachmentItem;
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import android.view.LayoutInflater;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.conversation.AttachmentResult;
|
import org.briarproject.briar.android.attachment.AttachmentItemResult;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ public class ImagePreview extends ConstraintLayout {
|
|||||||
imageList.setAdapter(adapter);
|
imageList.setAdapter(adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadPreviewImage(AttachmentResult result) {
|
void loadPreviewImage(AttachmentItemResult result) {
|
||||||
ImagePreviewAdapter adapter =
|
ImagePreviewAdapter adapter =
|
||||||
((ImagePreviewAdapter) imageList.getAdapter());
|
((ImagePreviewAdapter) imageList.getAdapter());
|
||||||
requireNonNull(adapter).loadItemPreview(result);
|
requireNonNull(adapter).loadItemPreview(result);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.conversation.AttachmentResult;
|
import org.briarproject.briar.android.attachment.AttachmentItemResult;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -50,7 +50,7 @@ class ImagePreviewAdapter extends Adapter<ImagePreviewViewHolder> {
|
|||||||
return items.size();
|
return items.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadItemPreview(AttachmentResult result) {
|
void loadItemPreview(AttachmentItemResult result) {
|
||||||
ImagePreviewItem newItem =
|
ImagePreviewItem newItem =
|
||||||
new ImagePreviewItem(requireNonNull(result.getUri()));
|
new ImagePreviewItem(requireNonNull(result.getUri()));
|
||||||
int pos = items.indexOf(newItem);
|
int pos = items.indexOf(newItem);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import android.net.Uri;
|
|||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.android.conversation.AttachmentItem;
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -30,10 +30,6 @@ class ImagePreviewItem {
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
Uri getUri() {
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setItem(AttachmentItem item) {
|
public void setItem(AttachmentItem item) {
|
||||||
this.item = item;
|
this.item = item;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ class ImagePreviewViewHolder extends ViewHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void bind(ImagePreviewItem item) {
|
void bind(ImagePreviewItem item) {
|
||||||
if (item.getItem() == null) return;
|
if (item.getItem() == null) return; // shows progress bar
|
||||||
GlideApp.with(imageView)
|
GlideApp.with(imageView)
|
||||||
.load(item.getUri())
|
.load(item.getItem())
|
||||||
.diskCacheStrategy(NONE)
|
.diskCacheStrategy(NONE)
|
||||||
.error(ERROR_RES)
|
.error(ERROR_RES)
|
||||||
.downsample(FIT_CENTER)
|
.downsample(FIT_CENTER)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.briar.android.view;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.arch.lifecycle.LifecycleOwner;
|
import android.arch.lifecycle.LifecycleOwner;
|
||||||
import android.arch.lifecycle.LiveData;
|
import android.arch.lifecycle.LiveData;
|
||||||
|
import android.arch.lifecycle.Observer;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -17,9 +18,10 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.conversation.AttachmentResult;
|
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.view.ImagePreview.ImagePreviewListener;
|
import org.briarproject.briar.android.view.ImagePreview.ImagePreviewListener;
|
||||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -38,7 +40,6 @@ import static android.support.v4.content.ContextCompat.getColor;
|
|||||||
import static android.support.v4.view.AbsSavedState.EMPTY_STATE;
|
import static android.support.v4.view.AbsSavedState.EMPTY_STATE;
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.widget.Toast.LENGTH_LONG;
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
import static java.util.Collections.emptyList;
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute;
|
import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute;
|
||||||
import static org.briarproject.briar.api.messaging.MessagingConstants.IMAGE_MIME_TYPES;
|
import static org.briarproject.briar.api.messaging.MessagingConstants.IMAGE_MIME_TYPES;
|
||||||
@@ -55,9 +56,8 @@ public class TextAttachmentController extends TextSendController
|
|||||||
private final CompositeSendButton sendButton;
|
private final CompositeSendButton sendButton;
|
||||||
private final AttachmentManager attachmentManager;
|
private final AttachmentManager attachmentManager;
|
||||||
|
|
||||||
private CharSequence textHint;
|
private final List<Uri> imageUris = new ArrayList<>();
|
||||||
private List<Uri> imageUris = emptyList();
|
private final CharSequence textHint;
|
||||||
private int urisLoaded = 0;
|
|
||||||
private boolean loadingUris = false;
|
private boolean loadingUris = false;
|
||||||
|
|
||||||
public TextAttachmentController(TextInputView v, ImagePreview imagePreview,
|
public TextAttachmentController(TextInputView v, ImagePreview imagePreview,
|
||||||
@@ -96,7 +96,7 @@ public class TextAttachmentController extends TextSendController
|
|||||||
if (canSend()) {
|
if (canSend()) {
|
||||||
if (loadingUris) throw new AssertionError();
|
if (loadingUris) throw new AssertionError();
|
||||||
listener.onSendClick(textInput.getText(),
|
listener.onSendClick(textInput.getText(),
|
||||||
attachmentManager.getAttachmentHeaders());
|
attachmentManager.getAttachmentHeadersForSending());
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,14 +140,12 @@ public class TextAttachmentController extends TextSendController
|
|||||||
|
|
||||||
public void onImageReceived(@Nullable Intent resultData) {
|
public void onImageReceived(@Nullable Intent resultData) {
|
||||||
if (resultData == null) return;
|
if (resultData == null) return;
|
||||||
if (loadingUris) throw new AssertionError();
|
if (loadingUris || !imageUris.isEmpty()) throw new AssertionError();
|
||||||
if (resultData.getData() != null) {
|
if (resultData.getData() != null) {
|
||||||
imageUris = new ArrayList<>(1);
|
|
||||||
imageUris.add(resultData.getData());
|
imageUris.add(resultData.getData());
|
||||||
onNewUris();
|
onNewUris();
|
||||||
} else if (SDK_INT >= 18 && resultData.getClipData() != null) {
|
} else if (SDK_INT >= 18 && resultData.getClipData() != null) {
|
||||||
ClipData clipData = resultData.getClipData();
|
ClipData clipData = resultData.getClipData();
|
||||||
imageUris = new ArrayList<>(clipData.getItemCount());
|
|
||||||
for (int i = 0; i < clipData.getItemCount(); i++) {
|
for (int i = 0; i < clipData.getItemCount(); i++) {
|
||||||
imageUris.add(clipData.getItemAt(i).getUri());
|
imageUris.add(clipData.getItemAt(i).getUri());
|
||||||
}
|
}
|
||||||
@@ -157,40 +155,62 @@ public class TextAttachmentController extends TextSendController
|
|||||||
|
|
||||||
private void onNewUris() {
|
private void onNewUris() {
|
||||||
if (imageUris.isEmpty()) return;
|
if (imageUris.isEmpty()) return;
|
||||||
if (loadingUris || urisLoaded != 0) throw new AssertionError();
|
if (loadingUris) throw new AssertionError();
|
||||||
loadingUris = true;
|
loadingUris = true;
|
||||||
updateViewState();
|
updateViewState();
|
||||||
textInput.setHint(R.string.image_caption_hint);
|
textInput.setHint(R.string.image_caption_hint);
|
||||||
List<ImagePreviewItem> items = ImagePreviewItem.fromUris(imageUris);
|
List<ImagePreviewItem> items = ImagePreviewItem.fromUris(imageUris);
|
||||||
imagePreview.showPreview(items);
|
imagePreview.showPreview(items);
|
||||||
// store attachments and show preview when successful
|
// store attachments and show preview when successful
|
||||||
boolean needsSize = items.size() == 1;
|
AttachmentResult result = attachmentManager.storeAttachments(imageUris);
|
||||||
for (ImagePreviewItem item : items) {
|
for (LiveData<AttachmentItemResult> liveData : result
|
||||||
attachmentManager.storeAttachment(item.getUri(), needsSize)
|
.getItemResults()) {
|
||||||
.observe(imageListener, this::onAttachmentResultReceived);
|
onLiveDataReturned(liveData);
|
||||||
}
|
}
|
||||||
|
result.getFinished().observe(imageListener, new Observer<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void onChanged(@Nullable Boolean finished) {
|
||||||
|
if (finished != null && finished) onAllAttachmentsCreated();
|
||||||
|
result.getFinished().removeObserver(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onAttachmentResultReceived(AttachmentResult result) {
|
private void onLiveDataReturned(LiveData<AttachmentItemResult> liveData) {
|
||||||
if (!loadingUris) return; // if this is false, the user cancelled
|
liveData.observe(imageListener, new Observer<AttachmentItemResult>() {
|
||||||
|
@Override
|
||||||
|
public void onChanged(@Nullable AttachmentItemResult result) {
|
||||||
|
if (result != null) {
|
||||||
|
onAttachmentResultReceived(result);
|
||||||
|
}
|
||||||
|
liveData.removeObserver(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onAttachmentResultReceived(AttachmentItemResult result) {
|
||||||
|
if (!loadingUris) throw new AssertionError();
|
||||||
if (result.isError() || result.getUri() == null) {
|
if (result.isError() || result.getUri() == null) {
|
||||||
onError(result.getErrorMsg());
|
onError(result.getErrorMsg());
|
||||||
} else {
|
} else {
|
||||||
imagePreview.loadPreviewImage(result);
|
imagePreview.loadPreviewImage(result);
|
||||||
urisLoaded++;
|
|
||||||
checkAllUrisLoaded();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onAllAttachmentsCreated() {
|
||||||
|
if (!loadingUris) throw new AssertionError();
|
||||||
|
loadingUris = false;
|
||||||
|
updateViewState();
|
||||||
|
}
|
||||||
|
|
||||||
private void reset() {
|
private void reset() {
|
||||||
// restore hint
|
// restore hint
|
||||||
textInput.setHint(textHint);
|
textInput.setHint(textHint);
|
||||||
// hide image layout
|
// hide image layout
|
||||||
imagePreview.setVisibility(GONE);
|
imagePreview.setVisibility(GONE);
|
||||||
// reset image URIs
|
// reset image URIs
|
||||||
imageUris = emptyList();
|
imageUris.clear();
|
||||||
// no URIs has been loaded
|
// definitely not loading anymore
|
||||||
urisLoaded = 0;
|
|
||||||
loadingUris = false;
|
loadingUris = false;
|
||||||
// show the image button again, so images can get attached
|
// show the image button again, so images can get attached
|
||||||
updateViewState();
|
updateViewState();
|
||||||
@@ -208,7 +228,8 @@ public class TextAttachmentController extends TextSendController
|
|||||||
@Nullable
|
@Nullable
|
||||||
public Parcelable onRestoreInstanceState(Parcelable inState) {
|
public Parcelable onRestoreInstanceState(Parcelable inState) {
|
||||||
SavedState state = (SavedState) inState;
|
SavedState state = (SavedState) inState;
|
||||||
imageUris = requireNonNull(state.imageUris);
|
if (!imageUris.isEmpty()) throw new AssertionError();
|
||||||
|
if (state.imageUris != null) imageUris.addAll(state.imageUris);
|
||||||
onNewUris();
|
onNewUris();
|
||||||
return state.getSuperState();
|
return state.getSuperState();
|
||||||
}
|
}
|
||||||
@@ -226,19 +247,10 @@ public class TextAttachmentController extends TextSendController
|
|||||||
@Override
|
@Override
|
||||||
public void onCancel() {
|
public void onCancel() {
|
||||||
textInput.clearText();
|
textInput.clearText();
|
||||||
attachmentManager.removeAttachments();
|
attachmentManager.cancel();
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkAllUrisLoaded() {
|
|
||||||
if (!loadingUris) throw new AssertionError();
|
|
||||||
if (urisLoaded == imageUris.size()) {
|
|
||||||
loadingUris = false;
|
|
||||||
// all images were turned into attachments
|
|
||||||
updateViewState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showImageOnboarding(Activity activity,
|
public void showImageOnboarding(Activity activity,
|
||||||
Runnable onOnboardingSeen) {
|
Runnable onOnboardingSeen) {
|
||||||
PromptStateChangeListener listener = (prompt, state) -> {
|
PromptStateChangeListener listener = (prompt, state) -> {
|
||||||
@@ -297,19 +309,4 @@ public class TextAttachmentController extends TextSendController
|
|||||||
void onAttachImage(Intent intent);
|
void onAttachImage(Intent intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface AttachmentManager {
|
|
||||||
/**
|
|
||||||
* Stores a new attachment in the database.
|
|
||||||
*
|
|
||||||
* @param uri The Uri of the attachment to store.
|
|
||||||
* @param needsSize true if this is the only image in the message
|
|
||||||
* and therefore needs to know its size.
|
|
||||||
*/
|
|
||||||
LiveData<AttachmentResult> storeAttachment(Uri uri, boolean needsSize);
|
|
||||||
|
|
||||||
List<AttachmentHeader> getAttachmentHeaders();
|
|
||||||
|
|
||||||
void removeAttachments();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.briarproject.briar.android.conversation;
|
package org.briarproject.briar.android.attachment;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.briar.android.conversation.ImageHelper.DecodeResult;
|
import org.briarproject.briar.android.attachment.ImageHelper.DecodeResult;
|
||||||
import org.briarproject.briar.api.messaging.Attachment;
|
import org.briarproject.briar.api.messaging.Attachment;
|
||||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
@@ -19,7 +19,7 @@ import static org.junit.Assert.assertEquals;
|
|||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class AttachmentControllerTest extends BrambleMockTestCase {
|
public class AttachmentRetrieverTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
private final AttachmentDimensions dimensions = new AttachmentDimensions(
|
private final AttachmentDimensions dimensions = new AttachmentDimensions(
|
||||||
100, 50, 200, 75, 300
|
100, 50, 200, 75, 300
|
||||||
@@ -32,8 +32,8 @@ public class AttachmentControllerTest extends BrambleMockTestCase {
|
|||||||
private final MessagingManager messagingManager =
|
private final MessagingManager messagingManager =
|
||||||
context.mock(MessagingManager.class);
|
context.mock(MessagingManager.class);
|
||||||
private final ImageHelper imageHelper = context.mock(ImageHelper.class);
|
private final ImageHelper imageHelper = context.mock(ImageHelper.class);
|
||||||
private final AttachmentController controller =
|
private final AttachmentRetriever controller =
|
||||||
new AttachmentController(
|
new AttachmentRetriever(
|
||||||
messagingManager,
|
messagingManager,
|
||||||
dimensions,
|
dimensions,
|
||||||
imageHelper
|
imageHelper
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.briar.android.conversation;
|
package org.briarproject.briar.android.attachment;
|
||||||
|
|
||||||
import com.bumptech.glide.util.MarkEnforcingInputStream;
|
import com.bumptech.glide.util.MarkEnforcingInputStream;
|
||||||
|
|
||||||
@@ -174,6 +174,10 @@ class MessagingManagerImpl extends ConversationClientImpl
|
|||||||
}
|
}
|
||||||
if (is.available() > 0) throw new FileTooBigException();
|
if (is.available() > 0) throw new FileTooBigException();
|
||||||
is.close();
|
is.close();
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
Message m = messageFactory.createMessage(groupId, timestamp, body);
|
Message m = messageFactory.createMessage(groupId, timestamp, body);
|
||||||
clientHelper.addLocalMessage(m, new BdfDictionary(), false);
|
clientHelper.addLocalMessage(m, new BdfDictionary(), false);
|
||||||
return new AttachmentHeader(m.getId(), contentType);
|
return new AttachmentHeader(m.getId(), contentType);
|
||||||
|
|||||||
Reference in New Issue
Block a user