mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +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.support.test.InstrumentationRegistry;
|
||||
@@ -21,7 +21,7 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AttachmentControllerIntegrationTest {
|
||||
public class AttachmentRetrieverIntegrationTest {
|
||||
|
||||
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";
|
||||
@@ -47,15 +47,15 @@ public class AttachmentControllerIntegrationTest {
|
||||
);
|
||||
private final MessageId msgId = new MessageId(getRandomId());
|
||||
|
||||
private final AttachmentController controller =
|
||||
new AttachmentController(null, dimensions);
|
||||
private final AttachmentRetriever retriever =
|
||||
new AttachmentRetriever(null, dimensions);
|
||||
|
||||
@Test
|
||||
public void testSmallJpegImage() throws Exception {
|
||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||
InputStream is = getUrlInputStream(smallKitten);
|
||||
Attachment a = new Attachment(is);
|
||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
||||
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||
assertEquals(msgId, item.getMessageId());
|
||||
assertEquals(160, item.getWidth());
|
||||
assertEquals(240, item.getHeight());
|
||||
@@ -71,7 +71,7 @@ public class AttachmentControllerIntegrationTest {
|
||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||
InputStream is = getUrlInputStream(originalKitten);
|
||||
Attachment a = new Attachment(is);
|
||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
||||
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||
assertEquals(msgId, item.getMessageId());
|
||||
assertEquals(1728, item.getWidth());
|
||||
assertEquals(2592, item.getHeight());
|
||||
@@ -87,7 +87,7 @@ public class AttachmentControllerIntegrationTest {
|
||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/png");
|
||||
InputStream is = getUrlInputStream(pngKitten);
|
||||
Attachment a = new Attachment(is);
|
||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
||||
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||
assertEquals(msgId, item.getMessageId());
|
||||
assertEquals(737, item.getWidth());
|
||||
assertEquals(510, item.getHeight());
|
||||
@@ -103,7 +103,7 @@ public class AttachmentControllerIntegrationTest {
|
||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||
InputStream is = getUrlInputStream(uberGif);
|
||||
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.getHeight());
|
||||
assertEquals(dimensions.minHeight, item.getThumbnailWidth());
|
||||
@@ -118,7 +118,7 @@ public class AttachmentControllerIntegrationTest {
|
||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||
InputStream is = getUrlInputStream(lottaPixel);
|
||||
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.getHeight());
|
||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||
@@ -133,7 +133,7 @@ public class AttachmentControllerIntegrationTest {
|
||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||
InputStream is = getUrlInputStream(imageIoCrash);
|
||||
Attachment a = new Attachment(is);
|
||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
||||
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||
assertEquals(1184, item.getWidth());
|
||||
assertEquals(448, item.getHeight());
|
||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||
@@ -148,7 +148,7 @@ public class AttachmentControllerIntegrationTest {
|
||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||
InputStream is = getUrlInputStream(gimpCrash);
|
||||
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.getHeight());
|
||||
assertEquals(dimensions.minHeight, item.getThumbnailWidth());
|
||||
@@ -163,7 +163,7 @@ public class AttachmentControllerIntegrationTest {
|
||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||
InputStream is = getUrlInputStream(optiPngAfl);
|
||||
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.getHeight());
|
||||
assertEquals(dimensions.minHeight, item.getThumbnailWidth());
|
||||
@@ -178,7 +178,7 @@ public class AttachmentControllerIntegrationTest {
|
||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||
InputStream is = getUrlInputStream(librawError);
|
||||
Attachment a = new Attachment(is);
|
||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
||||
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||
assertTrue(item.hasError());
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ public class AttachmentControllerIntegrationTest {
|
||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
||||
InputStream is = getAssetInputStream("animated.gif");
|
||||
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.getHeight());
|
||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||
@@ -202,7 +202,7 @@ public class AttachmentControllerIntegrationTest {
|
||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
||||
InputStream is = getAssetInputStream("animated2.gif");
|
||||
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.getHeight());
|
||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||
@@ -217,7 +217,7 @@ public class AttachmentControllerIntegrationTest {
|
||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
||||
InputStream is = getAssetInputStream("error_large.gif");
|
||||
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.getHeight());
|
||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||
@@ -232,7 +232,7 @@ public class AttachmentControllerIntegrationTest {
|
||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||
InputStream is = getAssetInputStream("error_high.jpg");
|
||||
Attachment a = new Attachment(is);
|
||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
||||
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||
assertEquals(1, item.getWidth());
|
||||
assertEquals(10000, item.getHeight());
|
||||
assertEquals(dimensions.minWidth, item.getThumbnailWidth());
|
||||
@@ -247,7 +247,7 @@ public class AttachmentControllerIntegrationTest {
|
||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||
InputStream is = getAssetInputStream("error_wide.jpg");
|
||||
Attachment a = new Attachment(is);
|
||||
AttachmentItem item = controller.getAttachmentItem(h, a, true);
|
||||
AttachmentItem item = retriever.getAttachmentItem(h, a, true);
|
||||
assertEquals(1920, item.getWidth());
|
||||
assertEquals(1, item.getHeight());
|
||||
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.support.annotation.VisibleForTesting;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
class AttachmentDimensions {
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class AttachmentDimensions {
|
||||
|
||||
final int defaultSize;
|
||||
final int minWidth, maxWidth;
|
||||
@@ -21,7 +26,7 @@ class AttachmentDimensions {
|
||||
this.maxHeight = maxHeight;
|
||||
}
|
||||
|
||||
static AttachmentDimensions getAttachmentDimensions(Resources res) {
|
||||
public static AttachmentDimensions getAttachmentDimensions(Resources res) {
|
||||
int defaultSize =
|
||||
res.getDimensionPixelSize(R.dimen.message_bubble_image_default);
|
||||
int minWidth = res.getDimensionPixelSize(
|
||||
@@ -33,7 +38,7 @@ class AttachmentDimensions {
|
||||
int maxHeight = res.getDimensionPixelSize(
|
||||
R.dimen.message_bubble_image_max_height);
|
||||
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.Parcelable;
|
||||
@@ -12,6 +12,8 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class AttachmentItem implements Parcelable {
|
||||
@@ -57,8 +59,8 @@ public class AttachmentItem implements Parcelable {
|
||||
MessageId messageId = new MessageId(messageIdByte);
|
||||
width = in.readInt();
|
||||
height = in.readInt();
|
||||
String mimeType = in.readString();
|
||||
extension = in.readString();
|
||||
String mimeType = requireNonNull(in.readString());
|
||||
extension = requireNonNull(in.readString());
|
||||
thumbnailWidth = in.readInt();
|
||||
thumbnailHeight = in.readInt();
|
||||
hasError = in.readByte() != 0;
|
||||
@@ -82,27 +84,27 @@ public class AttachmentItem implements Parcelable {
|
||||
return height;
|
||||
}
|
||||
|
||||
String getMimeType() {
|
||||
public String getMimeType() {
|
||||
return header.getContentType();
|
||||
}
|
||||
|
||||
String getExtension() {
|
||||
public String getExtension() {
|
||||
return extension;
|
||||
}
|
||||
|
||||
int getThumbnailWidth() {
|
||||
public int getThumbnailWidth() {
|
||||
return thumbnailWidth;
|
||||
}
|
||||
|
||||
int getThumbnailHeight() {
|
||||
public int getThumbnailHeight() {
|
||||
return thumbnailHeight;
|
||||
}
|
||||
|
||||
boolean hasError() {
|
||||
public boolean hasError() {
|
||||
return hasError;
|
||||
}
|
||||
|
||||
String getTransitionName() {
|
||||
public String getTransitionName() {
|
||||
return String.valueOf(instanceId);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.briar.android.conversation;
|
||||
package org.briarproject.briar.android.attachment;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
@@ -9,7 +9,7 @@ import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class AttachmentResult {
|
||||
public class AttachmentItemResult {
|
||||
|
||||
@Nullable
|
||||
private final Uri uri;
|
||||
@@ -18,13 +18,13 @@ public class AttachmentResult {
|
||||
@Nullable
|
||||
private final String errorMsg;
|
||||
|
||||
public AttachmentResult(Uri uri, AttachmentItem item) {
|
||||
public AttachmentItemResult(Uri uri, AttachmentItem item) {
|
||||
this.uri = uri;
|
||||
this.item = item;
|
||||
this.errorMsg = null;
|
||||
}
|
||||
|
||||
public AttachmentResult(@Nullable String errorMsg) {
|
||||
public AttachmentItemResult(@Nullable String errorMsg) {
|
||||
this.uri = null;
|
||||
this.item = null;
|
||||
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.Options;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
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.DbException;
|
||||
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.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.AttachmentHeader;
|
||||
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_WIDTH;
|
||||
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.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 AttachmentController {
|
||||
public class AttachmentRetriever {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(AttachmentController.class.getName());
|
||||
getLogger(AttachmentRetriever.class.getName());
|
||||
private static final int READ_LIMIT = 1024 * 8192;
|
||||
|
||||
private final MessagingManager messagingManager;
|
||||
@@ -60,12 +55,11 @@ class AttachmentController {
|
||||
private final int minWidth, maxWidth;
|
||||
private final int minHeight, maxHeight;
|
||||
|
||||
private final Map<Uri, AttachmentItem> unsentItems =
|
||||
new ConcurrentHashMap<>();
|
||||
private final Map<MessageId, List<AttachmentItem>> attachmentCache =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
AttachmentController(MessagingManager messagingManager,
|
||||
@VisibleForTesting
|
||||
AttachmentRetriever(MessagingManager messagingManager,
|
||||
AttachmentDimensions dimensions, ImageHelper imageHelper) {
|
||||
this.messagingManager = messagingManager;
|
||||
this.imageHelper = imageHelper;
|
||||
@@ -76,7 +70,7 @@ class AttachmentController {
|
||||
maxHeight = dimensions.maxHeight;
|
||||
}
|
||||
|
||||
AttachmentController(MessagingManager messagingManager,
|
||||
public AttachmentRetriever(MessagingManager messagingManager,
|
||||
AttachmentDimensions dimensions) {
|
||||
this(messagingManager, dimensions, new ImageHelper() {
|
||||
@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);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
List<AttachmentItem> get(MessageId messageId) {
|
||||
public List<AttachmentItem> cacheGet(MessageId messageId) {
|
||||
return attachmentCache.get(messageId);
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
List<Pair<AttachmentHeader, Attachment>> getMessageAttachments(
|
||||
public List<Pair<AttachmentHeader, Attachment>> getMessageAttachments(
|
||||
List<AttachmentHeader> headers) throws DbException {
|
||||
long start = now();
|
||||
List<Pair<AttachmentHeader, Attachment>> attachments =
|
||||
@@ -118,86 +112,13 @@ class AttachmentController {
|
||||
Attachment a = messagingManager.getAttachment(h.getMessageId());
|
||||
attachments.add(new Pair<>(h, a));
|
||||
}
|
||||
logDuration(LOG, "Loading attachment", start);
|
||||
logDuration(LOG, "Loading attachments", start);
|
||||
return attachments;
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
AttachmentItem createAttachmentHeader(ContentResolver contentResolver,
|
||||
GroupId groupId, Uri uri, boolean needsSize)
|
||||
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);
|
||||
}
|
||||
Attachment getMessageAttachment(AttachmentHeader h) throws DbException {
|
||||
return messagingManager.getAttachment(h.getMessageId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,7 +126,7 @@ class AttachmentController {
|
||||
* <p>
|
||||
* Note: This closes the {@link Attachment}'s {@link InputStream}.
|
||||
*/
|
||||
List<AttachmentItem> getAttachmentItems(
|
||||
public List<AttachmentItem> getAttachmentItems(
|
||||
List<Pair<AttachmentHeader, Attachment>> attachments) {
|
||||
boolean needsSize = attachments.size() == 1;
|
||||
List<AttachmentItem> items = new ArrayList<>(attachments.size());
|
||||
@@ -221,7 +142,6 @@ class AttachmentController {
|
||||
* Creates an {@link AttachmentItem} from the {@link Attachment}'s
|
||||
* {@link InputStream} which will be closed when this method returns.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
AttachmentItem getAttachmentItem(AttachmentHeader h, Attachment a,
|
||||
boolean needsSize) {
|
||||
if (!needsSize) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.briar.android.conversation;
|
||||
package org.briarproject.briar.android.attachment;
|
||||
|
||||
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.android.activity.ActivityComponent;
|
||||
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.conversation.ConversationVisitor.AttachmentCache;
|
||||
import org.briarproject.briar.android.conversation.ConversationVisitor.TextCache;
|
||||
@@ -183,7 +185,7 @@ public class ConversationActivity extends BriarActivity
|
||||
loadMessages();
|
||||
};
|
||||
|
||||
private AttachmentController attachmentController;
|
||||
private AttachmentRetriever attachmentRetriever;
|
||||
private ConversationViewModel viewModel;
|
||||
private ConversationVisitor visitor;
|
||||
private ConversationAdapter adapter;
|
||||
@@ -218,7 +220,7 @@ public class ConversationActivity extends BriarActivity
|
||||
|
||||
viewModel = ViewModelProviders.of(this, viewModelFactory)
|
||||
.get(ConversationViewModel.class);
|
||||
attachmentController = viewModel.getAttachmentController();
|
||||
attachmentRetriever = viewModel.getAttachmentRetriever();
|
||||
|
||||
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
|
||||
// images we use a grid so the size is fixed
|
||||
if (h.getAttachmentHeaders().size() == 1) {
|
||||
List<AttachmentItem> items = attachmentController.get(id);
|
||||
List<AttachmentItem> items = attachmentRetriever.cacheGet(id);
|
||||
if (items == null) {
|
||||
LOG.info("Eagerly loading image size for latest message");
|
||||
items = attachmentController.getAttachmentItems(
|
||||
attachmentController.getMessageAttachments(
|
||||
items = attachmentRetriever.getAttachmentItems(
|
||||
attachmentRetriever.getMessageAttachments(
|
||||
h.getAttachmentHeaders()));
|
||||
attachmentController.put(id, items);
|
||||
attachmentRetriever.cachePut(id, items);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -544,10 +546,10 @@ public class ConversationActivity extends BriarActivity
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
List<Pair<AttachmentHeader, Attachment>> attachments =
|
||||
attachmentController.getMessageAttachments(headers);
|
||||
attachmentRetriever.getMessageAttachments(headers);
|
||||
// TODO move getting the items off to IoExecutor, if size == 1
|
||||
List<AttachmentItem> items =
|
||||
attachmentController.getAttachmentItems(attachments);
|
||||
attachmentRetriever.getAttachmentItems(attachments);
|
||||
displayMessageAttachments(messageId, items);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
@@ -558,7 +560,7 @@ public class ConversationActivity extends BriarActivity
|
||||
private void displayMessageAttachments(MessageId m,
|
||||
List<AttachmentItem> items) {
|
||||
runOnUiThreadUnlessDestroyed(() -> {
|
||||
attachmentController.put(m, items);
|
||||
attachmentRetriever.cachePut(m, items);
|
||||
Pair<Integer, ConversationMessageItem> pair =
|
||||
adapter.getMessageItem(m);
|
||||
if (pair != null) {
|
||||
@@ -903,7 +905,7 @@ public class ConversationActivity extends BriarActivity
|
||||
@Override
|
||||
public List<AttachmentItem> getAttachmentItems(MessageId m,
|
||||
List<AttachmentHeader> headers) {
|
||||
List<AttachmentItem> attachments = attachmentController.get(m);
|
||||
List<AttachmentItem> attachments = attachmentRetriever.cacheGet(m);
|
||||
if (attachments == null) {
|
||||
loadMessageAttachments(m, headers);
|
||||
return emptyList();
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.support.annotation.UiThread;
|
||||
import android.view.View;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.briar.android.conversation;
|
||||
import android.support.annotation.LayoutRes;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -9,6 +9,7 @@ import android.view.ViewGroup;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||
|
||||
import static android.support.constraint.ConstraintSet.WRAP_CONTENT;
|
||||
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.MutableLiveData;
|
||||
import android.arch.lifecycle.Transformations;
|
||||
import android.content.ContentResolver;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
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.TransactionManager;
|
||||
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.settings.Settings;
|
||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.Message;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.attachment.AttachmentCreator;
|
||||
import org.briarproject.briar.android.attachment.AttachmentManager;
|
||||
import org.briarproject.briar.android.attachment.AttachmentResult;
|
||||
import org.briarproject.briar.android.attachment.AttachmentRetriever;
|
||||
import org.briarproject.briar.android.util.UiUtils;
|
||||
import org.briarproject.briar.android.view.TextAttachmentController.AttachmentManager;
|
||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||
import org.briarproject.briar.api.messaging.FileTooBigException;
|
||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||
import org.briarproject.briar.api.messaging.PrivateMessage;
|
||||
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
|
||||
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
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.logException;
|
||||
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.util.UiUtils.observeForeverOnce;
|
||||
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_IMAGE_SIZE;
|
||||
|
||||
@NotNullByDefault
|
||||
public class ConversationViewModel extends AndroidViewModel implements
|
||||
AttachmentManager {
|
||||
public class ConversationViewModel extends AndroidViewModel
|
||||
implements AttachmentManager {
|
||||
|
||||
private static Logger LOG =
|
||||
getLogger(ConversationViewModel.class.getName());
|
||||
@@ -78,10 +75,13 @@ public class ConversationViewModel extends AndroidViewModel implements
|
||||
private final ContactManager contactManager;
|
||||
private final SettingsManager settingsManager;
|
||||
private final PrivateMessageFactory privateMessageFactory;
|
||||
private final AttachmentController attachmentController;
|
||||
private final AttachmentRetriever attachmentRetriever;
|
||||
private final AttachmentCreator attachmentCreator;
|
||||
|
||||
@Nullable
|
||||
private ContactId contactId = null;
|
||||
@Nullable
|
||||
private volatile GroupId messagingGroupId = null;
|
||||
private final MutableLiveData<Contact> contact = new MutableLiveData<>();
|
||||
private final LiveData<AuthorId> contactAuthorId =
|
||||
Transformations.map(contact, c -> c.getAuthor().getId());
|
||||
@@ -97,15 +97,14 @@ public class ConversationViewModel extends AndroidViewModel implements
|
||||
new MutableLiveData<>();
|
||||
private final MutableLiveData<Boolean> contactDeleted =
|
||||
new MutableLiveData<>();
|
||||
private final MutableLiveData<GroupId> messagingGroupId =
|
||||
new MutableLiveData<>();
|
||||
private final MutableLiveData<PrivateMessageHeader> addedHeader =
|
||||
new MutableLiveData<>();
|
||||
|
||||
@Inject
|
||||
ConversationViewModel(Application application,
|
||||
@DatabaseExecutor Executor dbExecutor,
|
||||
@CryptoExecutor Executor cryptoExecutor, TransactionManager db,
|
||||
@CryptoExecutor Executor cryptoExecutor,
|
||||
@IoExecutor Executor ioExecutor, TransactionManager db,
|
||||
MessagingManager messagingManager, ContactManager contactManager,
|
||||
SettingsManager settingsManager,
|
||||
PrivateMessageFactory privateMessageFactory) {
|
||||
@@ -117,15 +116,17 @@ public class ConversationViewModel extends AndroidViewModel implements
|
||||
this.contactManager = contactManager;
|
||||
this.settingsManager = settingsManager;
|
||||
this.privateMessageFactory = privateMessageFactory;
|
||||
this.attachmentController = new AttachmentController(messagingManager,
|
||||
this.attachmentRetriever = new AttachmentRetriever(messagingManager,
|
||||
getAttachmentDimensions(application.getResources()));
|
||||
this.attachmentCreator = new AttachmentCreator(getApplication(),
|
||||
ioExecutor, messagingManager, attachmentRetriever);
|
||||
contactDeleted.setValue(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
super.onCleared();
|
||||
attachmentController.deleteUnsentAttachments();
|
||||
attachmentCreator.deleteUnsentAttachments();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,6 +150,10 @@ public class ConversationViewModel extends AndroidViewModel implements
|
||||
contact.postValue(c);
|
||||
logDuration(LOG, "Loading contact", start);
|
||||
start = now();
|
||||
messagingGroupId =
|
||||
messagingManager.getConversationId(contactId);
|
||||
logDuration(LOG, "Load conversation GroupId", start);
|
||||
start = now();
|
||||
checkFeaturesAndOnboarding(contactId);
|
||||
logDuration(LOG, "Checking for image support", start);
|
||||
} catch (NoSuchContactException e) {
|
||||
@@ -185,73 +190,29 @@ public class ConversationViewModel extends AndroidViewModel implements
|
||||
|
||||
void sendMessage(@Nullable String text,
|
||||
List<AttachmentHeader> attachmentHeaders, long timestamp) {
|
||||
if (messagingGroupId.getValue() == null) loadGroupId();
|
||||
observeForeverOnce(messagingGroupId, groupId -> {
|
||||
if (groupId == null) return;
|
||||
createMessage(groupId, text, attachmentHeaders, timestamp);
|
||||
});
|
||||
GroupId groupId = messagingGroupId;
|
||||
if (groupId == null) throw new IllegalStateException();
|
||||
createMessage(groupId, text, attachmentHeaders, timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveData<AttachmentResult> storeAttachment(Uri uri,
|
||||
boolean needsSize) {
|
||||
// use LiveData to not keep references to view scope
|
||||
MutableLiveData<AttachmentResult> result = new MutableLiveData<>();
|
||||
// check first if mime type is supported
|
||||
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;
|
||||
@UiThread
|
||||
public AttachmentResult storeAttachments(Collection<Uri> uris) {
|
||||
GroupId groupId = messagingGroupId;
|
||||
if (groupId == null) throw new IllegalStateException();
|
||||
return attachmentCreator.storeAttachments(groupId, uris);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AttachmentHeader> getAttachmentHeaders() {
|
||||
return attachmentController.getUnsentAttachmentHeaders();
|
||||
@UiThread
|
||||
public List<AttachmentHeader> getAttachmentHeadersForSending() {
|
||||
return attachmentCreator.getAttachmentHeadersForSending();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttachments() {
|
||||
dbExecutor.execute(attachmentController::deleteUnsentAttachments);
|
||||
}
|
||||
|
||||
private void loadGroupId() {
|
||||
if (contactId == null) throw new IllegalStateException();
|
||||
dbExecutor.execute(() -> {
|
||||
try {
|
||||
messagingGroupId.postValue(
|
||||
messagingManager.getConversationId(contactId));
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
});
|
||||
@UiThread
|
||||
public void cancel() {
|
||||
attachmentCreator.cancel();
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
@@ -337,7 +298,7 @@ public class ConversationViewModel extends AndroidViewModel implements
|
||||
message.getId(), message.getGroupId(),
|
||||
message.getTimestamp(), true, true, false, false,
|
||||
text != null, attachments);
|
||||
attachmentController.onAttachmentsSent(m.getMessage().getId());
|
||||
attachmentCreator.onAttachmentsSent(m.getMessage().getId());
|
||||
// TODO add text to cache when available here
|
||||
addedHeader.postValue(h);
|
||||
} catch (DbException e) {
|
||||
@@ -351,8 +312,8 @@ public class ConversationViewModel extends AndroidViewModel implements
|
||||
addedHeader.setValue(null);
|
||||
}
|
||||
|
||||
AttachmentController getAttachmentController() {
|
||||
return attachmentController;
|
||||
AttachmentRetriever getAttachmentRetriever() {
|
||||
return attachmentRetriever;
|
||||
}
|
||||
|
||||
LiveData<Contact> getContact() {
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.support.annotation.UiThread;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
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.BlogInvitationResponse;
|
||||
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.android.activity.ActivityComponent;
|
||||
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.view.PullDownLayout;
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import android.view.WindowManager;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||
import org.briarproject.briar.android.conversation.glide.Radii;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.github.chrisbanes.photoview.PhotoView;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.BaseActivity;
|
||||
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||
import org.briarproject.briar.android.conversation.glide.GlideApp;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.bumptech.glide.load.Transformation;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
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.GlideApp;
|
||||
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.nullsafety.NotNullByDefault;
|
||||
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.MutableLiveEvent;
|
||||
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.nullsafety.NotNullByDefault;
|
||||
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 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.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.android.conversation.AttachmentItem;
|
||||
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||
|
||||
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.briar.android.BriarApplication;
|
||||
import org.briarproject.briar.android.conversation.AttachmentItem;
|
||||
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||
|
||||
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.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.android.BriarApplication;
|
||||
import org.briarproject.briar.android.conversation.AttachmentItem;
|
||||
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||
|
||||
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.briar.android.BriarApplication;
|
||||
import org.briarproject.briar.android.conversation.AttachmentItem;
|
||||
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import android.view.LayoutInflater;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.conversation.AttachmentResult;
|
||||
import org.briarproject.briar.android.attachment.AttachmentItemResult;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -72,7 +72,7 @@ public class ImagePreview extends ConstraintLayout {
|
||||
imageList.setAdapter(adapter);
|
||||
}
|
||||
|
||||
void loadPreviewImage(AttachmentResult result) {
|
||||
void loadPreviewImage(AttachmentItemResult result) {
|
||||
ImagePreviewAdapter adapter =
|
||||
((ImagePreviewAdapter) imageList.getAdapter());
|
||||
requireNonNull(adapter).loadItemPreview(result);
|
||||
|
||||
@@ -8,7 +8,7 @@ import android.view.ViewGroup;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
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.Collection;
|
||||
@@ -50,7 +50,7 @@ class ImagePreviewAdapter extends Adapter<ImagePreviewViewHolder> {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
void loadItemPreview(AttachmentResult result) {
|
||||
void loadItemPreview(AttachmentItemResult result) {
|
||||
ImagePreviewItem newItem =
|
||||
new ImagePreviewItem(requireNonNull(result.getUri()));
|
||||
int pos = items.indexOf(newItem);
|
||||
|
||||
@@ -4,7 +4,7 @@ import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
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.Collection;
|
||||
@@ -30,10 +30,6 @@ class ImagePreviewItem {
|
||||
return items;
|
||||
}
|
||||
|
||||
Uri getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public void setItem(AttachmentItem item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
@@ -38,9 +38,9 @@ class ImagePreviewViewHolder extends ViewHolder {
|
||||
}
|
||||
|
||||
void bind(ImagePreviewItem item) {
|
||||
if (item.getItem() == null) return;
|
||||
if (item.getItem() == null) return; // shows progress bar
|
||||
GlideApp.with(imageView)
|
||||
.load(item.getUri())
|
||||
.load(item.getItem())
|
||||
.diskCacheStrategy(NONE)
|
||||
.error(ERROR_RES)
|
||||
.downsample(FIT_CENTER)
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.briar.android.view;
|
||||
import android.app.Activity;
|
||||
import android.arch.lifecycle.LifecycleOwner;
|
||||
import android.arch.lifecycle.LiveData;
|
||||
import android.arch.lifecycle.Observer;
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -17,9 +18,10 @@ import android.widget.Toast;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
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.api.messaging.AttachmentHeader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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.view.View.GONE;
|
||||
import static android.widget.Toast.LENGTH_LONG;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute;
|
||||
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 AttachmentManager attachmentManager;
|
||||
|
||||
private CharSequence textHint;
|
||||
private List<Uri> imageUris = emptyList();
|
||||
private int urisLoaded = 0;
|
||||
private final List<Uri> imageUris = new ArrayList<>();
|
||||
private final CharSequence textHint;
|
||||
private boolean loadingUris = false;
|
||||
|
||||
public TextAttachmentController(TextInputView v, ImagePreview imagePreview,
|
||||
@@ -96,7 +96,7 @@ public class TextAttachmentController extends TextSendController
|
||||
if (canSend()) {
|
||||
if (loadingUris) throw new AssertionError();
|
||||
listener.onSendClick(textInput.getText(),
|
||||
attachmentManager.getAttachmentHeaders());
|
||||
attachmentManager.getAttachmentHeadersForSending());
|
||||
reset();
|
||||
}
|
||||
}
|
||||
@@ -140,14 +140,12 @@ public class TextAttachmentController extends TextSendController
|
||||
|
||||
public void onImageReceived(@Nullable Intent resultData) {
|
||||
if (resultData == null) return;
|
||||
if (loadingUris) throw new AssertionError();
|
||||
if (loadingUris || !imageUris.isEmpty()) throw new AssertionError();
|
||||
if (resultData.getData() != null) {
|
||||
imageUris = new ArrayList<>(1);
|
||||
imageUris.add(resultData.getData());
|
||||
onNewUris();
|
||||
} else if (SDK_INT >= 18 && resultData.getClipData() != null) {
|
||||
ClipData clipData = resultData.getClipData();
|
||||
imageUris = new ArrayList<>(clipData.getItemCount());
|
||||
for (int i = 0; i < clipData.getItemCount(); i++) {
|
||||
imageUris.add(clipData.getItemAt(i).getUri());
|
||||
}
|
||||
@@ -157,40 +155,62 @@ public class TextAttachmentController extends TextSendController
|
||||
|
||||
private void onNewUris() {
|
||||
if (imageUris.isEmpty()) return;
|
||||
if (loadingUris || urisLoaded != 0) throw new AssertionError();
|
||||
if (loadingUris) throw new AssertionError();
|
||||
loadingUris = true;
|
||||
updateViewState();
|
||||
textInput.setHint(R.string.image_caption_hint);
|
||||
List<ImagePreviewItem> items = ImagePreviewItem.fromUris(imageUris);
|
||||
imagePreview.showPreview(items);
|
||||
// store attachments and show preview when successful
|
||||
boolean needsSize = items.size() == 1;
|
||||
for (ImagePreviewItem item : items) {
|
||||
attachmentManager.storeAttachment(item.getUri(), needsSize)
|
||||
.observe(imageListener, this::onAttachmentResultReceived);
|
||||
AttachmentResult result = attachmentManager.storeAttachments(imageUris);
|
||||
for (LiveData<AttachmentItemResult> liveData : result
|
||||
.getItemResults()) {
|
||||
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) {
|
||||
if (!loadingUris) return; // if this is false, the user cancelled
|
||||
private void onLiveDataReturned(LiveData<AttachmentItemResult> liveData) {
|
||||
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) {
|
||||
onError(result.getErrorMsg());
|
||||
} else {
|
||||
imagePreview.loadPreviewImage(result);
|
||||
urisLoaded++;
|
||||
checkAllUrisLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
private void onAllAttachmentsCreated() {
|
||||
if (!loadingUris) throw new AssertionError();
|
||||
loadingUris = false;
|
||||
updateViewState();
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
// restore hint
|
||||
textInput.setHint(textHint);
|
||||
// hide image layout
|
||||
imagePreview.setVisibility(GONE);
|
||||
// reset image URIs
|
||||
imageUris = emptyList();
|
||||
// no URIs has been loaded
|
||||
urisLoaded = 0;
|
||||
imageUris.clear();
|
||||
// definitely not loading anymore
|
||||
loadingUris = false;
|
||||
// show the image button again, so images can get attached
|
||||
updateViewState();
|
||||
@@ -208,7 +228,8 @@ public class TextAttachmentController extends TextSendController
|
||||
@Nullable
|
||||
public Parcelable onRestoreInstanceState(Parcelable inState) {
|
||||
SavedState state = (SavedState) inState;
|
||||
imageUris = requireNonNull(state.imageUris);
|
||||
if (!imageUris.isEmpty()) throw new AssertionError();
|
||||
if (state.imageUris != null) imageUris.addAll(state.imageUris);
|
||||
onNewUris();
|
||||
return state.getSuperState();
|
||||
}
|
||||
@@ -226,19 +247,10 @@ public class TextAttachmentController extends TextSendController
|
||||
@Override
|
||||
public void onCancel() {
|
||||
textInput.clearText();
|
||||
attachmentManager.removeAttachments();
|
||||
attachmentManager.cancel();
|
||||
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,
|
||||
Runnable onOnboardingSeen) {
|
||||
PromptStateChangeListener listener = (prompt, state) -> {
|
||||
@@ -297,19 +309,4 @@ public class TextAttachmentController extends TextSendController
|
||||
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.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.AttachmentHeader;
|
||||
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.assertTrue;
|
||||
|
||||
public class AttachmentControllerTest extends BrambleMockTestCase {
|
||||
public class AttachmentRetrieverTest extends BrambleMockTestCase {
|
||||
|
||||
private final AttachmentDimensions dimensions = new AttachmentDimensions(
|
||||
100, 50, 200, 75, 300
|
||||
@@ -32,8 +32,8 @@ public class AttachmentControllerTest extends BrambleMockTestCase {
|
||||
private final MessagingManager messagingManager =
|
||||
context.mock(MessagingManager.class);
|
||||
private final ImageHelper imageHelper = context.mock(ImageHelper.class);
|
||||
private final AttachmentController controller =
|
||||
new AttachmentController(
|
||||
private final AttachmentRetriever controller =
|
||||
new AttachmentRetriever(
|
||||
messagingManager,
|
||||
dimensions,
|
||||
imageHelper
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.briar.android.conversation;
|
||||
package org.briarproject.briar.android.attachment;
|
||||
|
||||
import com.bumptech.glide.util.MarkEnforcingInputStream;
|
||||
|
||||
@@ -174,6 +174,10 @@ class MessagingManagerImpl extends ConversationClientImpl
|
||||
}
|
||||
if (is.available() > 0) throw new FileTooBigException();
|
||||
is.close();
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
Message m = messageFactory.createMessage(groupId, timestamp, body);
|
||||
clientHelper.addLocalMessage(m, new BdfDictionary(), false);
|
||||
return new AttachmentHeader(m.getId(), contentType);
|
||||
|
||||
Reference in New Issue
Block a user