mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Compare commits
2 Commits
alpha-1.2.
...
debugging-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
464dcf8742 | ||
|
|
09af494d5c |
@@ -5,10 +5,6 @@
|
||||
|
||||
# QR codes
|
||||
-keep class com.google.zxing.Result
|
||||
-keepclassmembers enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
|
||||
# RSS libraries
|
||||
-keep,includedescriptorclasses class com.rometools.rome.feed.synd.impl.** { *; }
|
||||
|
||||
@@ -4,7 +4,6 @@ import org.briarproject.bramble.BrambleAndroidModule;
|
||||
import org.briarproject.bramble.BrambleCoreModule;
|
||||
import org.briarproject.bramble.account.BriarAccountModule;
|
||||
import org.briarproject.briar.BriarCoreModule;
|
||||
import org.briarproject.briar.android.attachment.AttachmentModule;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivityTest;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
@@ -14,7 +13,6 @@ import dagger.Component;
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
AppModule.class,
|
||||
AttachmentModule.class,
|
||||
BriarCoreModule.class,
|
||||
BrambleAndroidModule.class,
|
||||
BriarAccountModule.class,
|
||||
|
||||
@@ -47,10 +47,8 @@ public class AttachmentRetrieverIntegrationTest {
|
||||
);
|
||||
private final MessageId msgId = new MessageId(getRandomId());
|
||||
|
||||
private final ImageHelper imageHelper = new ImageHelperImpl();
|
||||
private final AttachmentRetriever retriever =
|
||||
new AttachmentRetrieverImpl(null, dimensions, imageHelper,
|
||||
new ImageSizeCalculator(imageHelper));
|
||||
new AttachmentRetriever(null, dimensions);
|
||||
|
||||
@Test
|
||||
public void testSmallJpegImage() throws Exception {
|
||||
|
||||
@@ -30,7 +30,6 @@ import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
|
||||
import org.briarproject.briar.BriarCoreEagerSingletons;
|
||||
import org.briarproject.briar.BriarCoreModule;
|
||||
import org.briarproject.briar.android.attachment.AttachmentModule;
|
||||
import org.briarproject.briar.android.conversation.glide.BriarModelLoader;
|
||||
import org.briarproject.briar.android.login.SignInReminderReceiver;
|
||||
import org.briarproject.briar.android.reporting.BriarReportSender;
|
||||
@@ -69,8 +68,7 @@ import dagger.Component;
|
||||
BriarCoreModule.class,
|
||||
BrambleAndroidModule.class,
|
||||
BriarAccountModule.class,
|
||||
AppModule.class,
|
||||
AttachmentModule.class
|
||||
AppModule.class
|
||||
})
|
||||
public interface AndroidComponent
|
||||
extends BrambleCoreEagerSingletons, BrambleAndroidEagerSingletons,
|
||||
|
||||
@@ -239,7 +239,7 @@ public class AppModule {
|
||||
|
||||
@Override
|
||||
public boolean shouldEnableImageAttachments() {
|
||||
return false;
|
||||
return IS_DEBUG_BUILD;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.briarproject.briar.android.account;
|
||||
import android.os.Bundle;
|
||||
import android.support.design.widget.TextInputEditText;
|
||||
import android.support.design.widget.TextInputLayout;
|
||||
import android.text.Editable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -11,6 +10,7 @@ import android.widget.Button;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
|
||||
@@ -20,7 +20,6 @@ import static android.view.inputmethod.EditorInfo.IME_ACTION_NEXT;
|
||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_NONE;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
import static org.briarproject.bramble.util.StringUtils.toUtf8;
|
||||
import static org.briarproject.briar.android.util.UiUtils.setError;
|
||||
import static org.briarproject.briar.android.util.UiUtils.showSoftKeyboard;
|
||||
|
||||
@@ -78,7 +77,7 @@ public class AuthorNameFragment extends SetupFragment {
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence authorName, int i, int i1, int i2) {
|
||||
int authorNameLength = toUtf8(authorName.toString().trim()).length;
|
||||
int authorNameLength = StringUtils.toUtf8(authorName.toString()).length;
|
||||
boolean error = authorNameLength > MAX_AUTHOR_NAME_LENGTH;
|
||||
setError(authorNameWrapper, getString(R.string.name_too_long), error);
|
||||
boolean enabled = authorNameLength > 0 && !error;
|
||||
@@ -90,11 +89,8 @@ public class AuthorNameFragment extends SetupFragment {
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Editable text = authorNameInput.getText();
|
||||
if (text != null) {
|
||||
setupController.setAuthorName(text.toString().trim());
|
||||
setupController.showPasswordFragment();
|
||||
}
|
||||
setupController.setAuthorName(authorNameInput.getText().toString());
|
||||
setupController.showPasswordFragment();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package org.briarproject.briar.android.attachment;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory.Options;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
@@ -14,17 +12,11 @@ import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||
import org.jsoup.UnsupportedMimeTypeException;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static android.graphics.Bitmap.CompressFormat.JPEG;
|
||||
import static android.graphics.BitmapFactory.decodeStream;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||
@@ -32,7 +24,6 @@ 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;
|
||||
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_IMAGE_SIZE;
|
||||
|
||||
@NotNullByDefault
|
||||
class AttachmentCreationTask {
|
||||
@@ -40,11 +31,8 @@ class AttachmentCreationTask {
|
||||
private static Logger LOG =
|
||||
getLogger(AttachmentCreationTask.class.getName());
|
||||
|
||||
private static final int MAX_ATTACHMENT_DIMENSION = 1000;
|
||||
|
||||
private final MessagingManager messagingManager;
|
||||
private final ContentResolver contentResolver;
|
||||
private final ImageSizeCalculator imageSizeCalculator;
|
||||
private final GroupId groupId;
|
||||
private final Collection<Uri> uris;
|
||||
private final boolean needsSize;
|
||||
@@ -55,26 +43,24 @@ class AttachmentCreationTask {
|
||||
|
||||
AttachmentCreationTask(MessagingManager messagingManager,
|
||||
ContentResolver contentResolver,
|
||||
AttachmentCreator attachmentCreator,
|
||||
ImageSizeCalculator imageSizeCalculator,
|
||||
GroupId groupId, Collection<Uri> uris, boolean needsSize) {
|
||||
AttachmentCreator attachmentCreator, GroupId groupId,
|
||||
Collection<Uri> uris, boolean needsSize) {
|
||||
this.messagingManager = messagingManager;
|
||||
this.contentResolver = contentResolver;
|
||||
this.imageSizeCalculator = imageSizeCalculator;
|
||||
this.groupId = groupId;
|
||||
this.uris = uris;
|
||||
this.needsSize = needsSize;
|
||||
this.attachmentCreator = attachmentCreator;
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
public void cancel() {
|
||||
canceled = true;
|
||||
attachmentCreator = null;
|
||||
}
|
||||
|
||||
@IoExecutor
|
||||
void storeAttachments() {
|
||||
for (Uri uri : uris) processUri(uri);
|
||||
public void storeAttachments() {
|
||||
for (Uri uri: uris) processUri(uri);
|
||||
AttachmentCreator attachmentCreator = this.attachmentCreator;
|
||||
if (!canceled && attachmentCreator != null)
|
||||
attachmentCreator.onAttachmentCreationFinished();
|
||||
@@ -112,8 +98,6 @@ class AttachmentCreationTask {
|
||||
}
|
||||
InputStream is = contentResolver.openInputStream(uri);
|
||||
if (is == null) throw new IOException();
|
||||
is = compressImage(is, contentType);
|
||||
contentType = "image/jpeg";
|
||||
long timestamp = System.currentTimeMillis();
|
||||
AttachmentHeader h = messagingManager
|
||||
.addLocalAttachment(groupId, timestamp, contentType, is);
|
||||
@@ -129,48 +113,4 @@ class AttachmentCreationTask {
|
||||
return false;
|
||||
}
|
||||
|
||||
private InputStream compressImage(InputStream is, String contentType)
|
||||
throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
Bitmap bitmap = createBitmap(is, contentType);
|
||||
for (int quality = 100; quality >= 0; quality -= 10) {
|
||||
if (!bitmap.compress(JPEG, quality, out))
|
||||
throw new IOException();
|
||||
if (out.size() <= MAX_IMAGE_SIZE) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Compressed image to "
|
||||
+ out.size() + " bytes, quality " + quality);
|
||||
}
|
||||
return new ByteArrayInputStream(out.toByteArray());
|
||||
}
|
||||
out.reset();
|
||||
}
|
||||
throw new IOException();
|
||||
} finally {
|
||||
tryToClose(is, LOG, WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap createBitmap(InputStream is, String contentType)
|
||||
throws IOException {
|
||||
is = new BufferedInputStream(is);
|
||||
Size size = imageSizeCalculator.getSize(is, contentType);
|
||||
if (size.error) throw new IOException();
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Original image size: " + size.width + "x" + size.height);
|
||||
int dimension = Math.max(size.width, size.height);
|
||||
int inSampleSize = 1;
|
||||
while (dimension > MAX_ATTACHMENT_DIMENSION) {
|
||||
inSampleSize *= 2;
|
||||
dimension /= 2;
|
||||
}
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Scaling attachment by factor of " + inSampleSize);
|
||||
Options options = new Options();
|
||||
options.inSampleSize = inSampleSize;
|
||||
Bitmap bitmap = decodeStream(is, null, options);
|
||||
if (bitmap == null) throw new IOException();
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,82 @@
|
||||
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.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
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.LogUtils.logException;
|
||||
import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce;
|
||||
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_IMAGE_SIZE;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface AttachmentCreator {
|
||||
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 retriever;
|
||||
|
||||
private final CopyOnWriteArrayList<Uri> uris = new CopyOnWriteArrayList<>();
|
||||
private final CopyOnWriteArrayList<AttachmentItemResult> itemResults =
|
||||
new CopyOnWriteArrayList<>();
|
||||
|
||||
private final MutableLiveData<AttachmentResult> result =
|
||||
new MutableLiveData<>();
|
||||
@Nullable
|
||||
private AttachmentCreationTask task;
|
||||
|
||||
public AttachmentCreator(Application app, @IoExecutor Executor ioExecutor,
|
||||
MessagingManager messagingManager, AttachmentRetriever retriever) {
|
||||
this.app = app;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.messagingManager = messagingManager;
|
||||
this.retriever = retriever;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
LiveData<AttachmentResult> storeAttachments(LiveData<GroupId> groupId,
|
||||
Collection<Uri> newUris);
|
||||
public LiveData<AttachmentResult> storeAttachments(
|
||||
LiveData<GroupId> groupId, Collection<Uri> newUris) {
|
||||
if (task != null || !uris.isEmpty())
|
||||
throw new IllegalStateException();
|
||||
uris.addAll(newUris);
|
||||
observeForeverOnce(groupId, id -> {
|
||||
if (id == null) throw new IllegalStateException();
|
||||
boolean needsSize = uris.size() == 1;
|
||||
task = new AttachmentCreationTask(messagingManager,
|
||||
app.getContentResolver(), this, id, uris, needsSize);
|
||||
ioExecutor.execute(() -> task.storeAttachments());
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be only called after configuration changes.
|
||||
@@ -26,10 +84,68 @@ public interface AttachmentCreator {
|
||||
* They are already being created and returned by the existing LiveData.
|
||||
*/
|
||||
@UiThread
|
||||
LiveData<AttachmentResult> getLiveAttachments();
|
||||
public LiveData<AttachmentResult> getLiveAttachments() {
|
||||
if (task == null || uris.isEmpty())
|
||||
throw new IllegalStateException();
|
||||
// A task is already running. It will update the result LiveData.
|
||||
// So nothing more to do here.
|
||||
return result;
|
||||
}
|
||||
|
||||
@IoExecutor
|
||||
void onAttachmentHeaderReceived(Uri uri, AttachmentHeader h,
|
||||
boolean needsSize) {
|
||||
// get and cache AttachmentItem for ImagePreview
|
||||
try {
|
||||
Attachment a = retriever.getMessageAttachment(h);
|
||||
AttachmentItem item = retriever.getAttachmentItem(a, needsSize);
|
||||
if (item.hasError()) throw new IOException();
|
||||
AttachmentItemResult itemResult =
|
||||
new AttachmentItemResult(uri, item);
|
||||
itemResults.add(itemResult);
|
||||
result.postValue(getResult(false));
|
||||
} catch (IOException | DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
onAttachmentError(uri, e);
|
||||
}
|
||||
}
|
||||
|
||||
@IoExecutor
|
||||
void onAttachmentError(Uri uri, Throwable t) {
|
||||
// get error message
|
||||
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
|
||||
}
|
||||
AttachmentItemResult itemResult =
|
||||
new AttachmentItemResult(uri, errorMsg);
|
||||
itemResults.add(itemResult);
|
||||
result.postValue(getResult(false));
|
||||
// expect to receive a cancel from the UI
|
||||
}
|
||||
|
||||
@IoExecutor
|
||||
void onAttachmentCreationFinished() {
|
||||
result.postValue(getResult(true));
|
||||
}
|
||||
|
||||
@UiThread
|
||||
List<AttachmentHeader> getAttachmentHeadersForSending();
|
||||
public List<AttachmentHeader> getAttachmentHeadersForSending() {
|
||||
List<AttachmentHeader> headers = new ArrayList<>(itemResults.size());
|
||||
for (AttachmentItemResult itemResult : itemResults) {
|
||||
// check if we are trying to send attachment items with errors
|
||||
if (itemResult.getItem() == null) throw new IllegalStateException();
|
||||
headers.add(itemResult.getItem().getHeader());
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the attachments as sent and adds the items to the cache for display
|
||||
@@ -37,24 +153,66 @@ public interface AttachmentCreator {
|
||||
* @param id The MessageId of the sent message.
|
||||
*/
|
||||
@UiThread
|
||||
void onAttachmentsSent(MessageId id);
|
||||
public void onAttachmentsSent(MessageId id) {
|
||||
List<AttachmentItem> items = new ArrayList<>(itemResults.size());
|
||||
for (AttachmentItemResult itemResult : itemResults) {
|
||||
// check if we are trying to send attachment items with errors
|
||||
if (itemResult.getItem() == null) throw new IllegalStateException();
|
||||
items.add(itemResult.getItem());
|
||||
}
|
||||
retriever.cachePut(id, items);
|
||||
resetState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Needs to be called when created attachments will not be sent anymore.
|
||||
*/
|
||||
@UiThread
|
||||
void cancel();
|
||||
public void cancel() {
|
||||
if (task == null) throw new AssertionError();
|
||||
task.cancel();
|
||||
deleteUnsentAttachments();
|
||||
resetState();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void deleteUnsentAttachments();
|
||||
private void resetState() {
|
||||
task = null;
|
||||
uris.clear();
|
||||
itemResults.clear();
|
||||
result.setValue(null);
|
||||
}
|
||||
|
||||
@IoExecutor
|
||||
void onAttachmentHeaderReceived(Uri uri, AttachmentHeader h,
|
||||
boolean needsSize);
|
||||
@UiThread
|
||||
public void deleteUnsentAttachments() {
|
||||
// Make a copy for the IoExecutor as we clear the itemResults soon
|
||||
List<AttachmentHeader> headers = new ArrayList<>(itemResults.size());
|
||||
for (AttachmentItemResult itemResult : itemResults) {
|
||||
// check if we are trying to send attachment items with errors
|
||||
if (itemResult.getItem() != null)
|
||||
headers.add(itemResult.getItem().getHeader());
|
||||
}
|
||||
ioExecutor.execute(() -> {
|
||||
for (AttachmentHeader header : headers) {
|
||||
try {
|
||||
messagingManager.removeAttachment(header);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@IoExecutor
|
||||
void onAttachmentError(Uri uri, Throwable t);
|
||||
private AttachmentResult getResult(boolean finished) {
|
||||
// Make a copy of the list,
|
||||
// because our copy will continue to change in the background.
|
||||
// (As it's a CopyOnWriteArrayList,
|
||||
// the code that receives the result can safely do simple things
|
||||
// like iterating over the list,
|
||||
// but anything that involves calling more than one list method
|
||||
// is still unsafe.)
|
||||
Collection<AttachmentItemResult> items = new ArrayList<>(itemResults);
|
||||
return new AttachmentResult(items, finished);
|
||||
}
|
||||
|
||||
@IoExecutor
|
||||
void onAttachmentCreationFinished();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,233 +0,0 @@
|
||||
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.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
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.android.util.UiUtils.observeForeverOnce;
|
||||
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_IMAGE_SIZE;
|
||||
|
||||
@NotNullByDefault
|
||||
class AttachmentCreatorImpl implements AttachmentCreator {
|
||||
|
||||
private static Logger LOG =
|
||||
getLogger(AttachmentCreatorImpl.class.getName());
|
||||
|
||||
private final Application app;
|
||||
@IoExecutor
|
||||
private final Executor ioExecutor;
|
||||
private final MessagingManager messagingManager;
|
||||
private final AttachmentRetriever retriever;
|
||||
private final ImageSizeCalculator imageSizeCalculator;
|
||||
|
||||
private final CopyOnWriteArrayList<Uri> uris = new CopyOnWriteArrayList<>();
|
||||
private final CopyOnWriteArrayList<AttachmentItemResult> itemResults =
|
||||
new CopyOnWriteArrayList<>();
|
||||
|
||||
@Nullable
|
||||
private AttachmentCreationTask task;
|
||||
|
||||
@Nullable
|
||||
private volatile MutableLiveData<AttachmentResult> result;
|
||||
|
||||
@Inject
|
||||
AttachmentCreatorImpl(Application app, @IoExecutor Executor ioExecutor,
|
||||
MessagingManager messagingManager, AttachmentRetriever retriever,
|
||||
ImageSizeCalculator imageSizeCalculator) {
|
||||
this.app = app;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.messagingManager = messagingManager;
|
||||
this.retriever = retriever;
|
||||
this.imageSizeCalculator = imageSizeCalculator;
|
||||
}
|
||||
|
||||
@Override
|
||||
@UiThread
|
||||
public LiveData<AttachmentResult> storeAttachments(
|
||||
LiveData<GroupId> groupId, Collection<Uri> newUris) {
|
||||
if (task != null || result != null || !uris.isEmpty())
|
||||
throw new IllegalStateException();
|
||||
MutableLiveData<AttachmentResult> result = new MutableLiveData<>();
|
||||
this.result = result;
|
||||
uris.addAll(newUris);
|
||||
observeForeverOnce(groupId, id -> {
|
||||
if (id == null) throw new IllegalStateException();
|
||||
boolean needsSize = uris.size() == 1;
|
||||
task = new AttachmentCreationTask(messagingManager,
|
||||
app.getContentResolver(), this, imageSizeCalculator, id,
|
||||
uris, needsSize);
|
||||
ioExecutor.execute(() -> task.storeAttachments());
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@UiThread
|
||||
public LiveData<AttachmentResult> getLiveAttachments() {
|
||||
MutableLiveData<AttachmentResult> result = this.result;
|
||||
if (task == null || result == null || uris.isEmpty())
|
||||
throw new IllegalStateException();
|
||||
// A task is already running. It will update the result LiveData.
|
||||
// So nothing more to do here.
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@IoExecutor
|
||||
public void onAttachmentHeaderReceived(Uri uri, AttachmentHeader h,
|
||||
boolean needsSize) {
|
||||
// get and cache AttachmentItem for ImagePreview
|
||||
try {
|
||||
Attachment a = retriever.getMessageAttachment(h);
|
||||
AttachmentItem item = retriever.getAttachmentItem(a, needsSize);
|
||||
if (item.hasError()) throw new IOException();
|
||||
AttachmentItemResult itemResult =
|
||||
new AttachmentItemResult(uri, item);
|
||||
itemResults.add(itemResult);
|
||||
MutableLiveData<AttachmentResult> result = this.result;
|
||||
if (result != null) result.postValue(getResult(false));
|
||||
} catch (IOException | DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
onAttachmentError(uri, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@IoExecutor
|
||||
public void onAttachmentError(Uri uri, Throwable t) {
|
||||
// get error message
|
||||
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
|
||||
}
|
||||
AttachmentItemResult itemResult =
|
||||
new AttachmentItemResult(uri, errorMsg);
|
||||
itemResults.add(itemResult);
|
||||
MutableLiveData<AttachmentResult> result = this.result;
|
||||
if (result != null) result.postValue(getResult(false));
|
||||
// expect to receive a cancel from the UI
|
||||
}
|
||||
|
||||
@Override
|
||||
@IoExecutor
|
||||
public void onAttachmentCreationFinished() {
|
||||
MutableLiveData<AttachmentResult> result = this.result;
|
||||
if (result != null) result.postValue(getResult(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
@UiThread
|
||||
public List<AttachmentHeader> getAttachmentHeadersForSending() {
|
||||
List<AttachmentHeader> headers = new ArrayList<>(itemResults.size());
|
||||
for (AttachmentItemResult itemResult : itemResults) {
|
||||
// check if we are trying to send attachment items with errors
|
||||
if (itemResult.getItem() == null) throw new IllegalStateException();
|
||||
headers.add(itemResult.getItem().getHeader());
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
@UiThread
|
||||
public void onAttachmentsSent(MessageId id) {
|
||||
List<AttachmentItem> items = new ArrayList<>(itemResults.size());
|
||||
for (AttachmentItemResult itemResult : itemResults) {
|
||||
// check if we are trying to send attachment items with errors
|
||||
if (itemResult.getItem() == null) throw new IllegalStateException();
|
||||
items.add(itemResult.getItem());
|
||||
}
|
||||
retriever.cachePut(id, items);
|
||||
resetState();
|
||||
}
|
||||
|
||||
@Override
|
||||
@UiThread
|
||||
public void cancel() {
|
||||
if (task == null) throw new AssertionError();
|
||||
task.cancel();
|
||||
deleteUnsentAttachments();
|
||||
resetState();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void resetState() {
|
||||
task = null;
|
||||
uris.clear();
|
||||
itemResults.clear();
|
||||
MutableLiveData<AttachmentResult> result = this.result;
|
||||
if (result != null) {
|
||||
result.setValue(null);
|
||||
this.result = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@UiThread
|
||||
public void deleteUnsentAttachments() {
|
||||
// Make a copy for the IoExecutor as we clear the itemResults soon
|
||||
List<AttachmentHeader> headers = new ArrayList<>(itemResults.size());
|
||||
for (AttachmentItemResult itemResult : itemResults) {
|
||||
// check if we are trying to send attachment items with errors
|
||||
if (itemResult.getItem() != null)
|
||||
headers.add(itemResult.getItem().getHeader());
|
||||
}
|
||||
ioExecutor.execute(() -> {
|
||||
for (AttachmentHeader header : headers) {
|
||||
try {
|
||||
messagingManager.removeAttachment(header);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private AttachmentResult getResult(boolean finished) {
|
||||
// Make a copy of the list,
|
||||
// because our copy will continue to change in the background.
|
||||
// (As it's a CopyOnWriteArrayList,
|
||||
// the code that receives the result can safely do simple things
|
||||
// like iterating over the list,
|
||||
// but anything that involves calling more than one list method
|
||||
// is still unsafe.)
|
||||
Collection<AttachmentItemResult> items = new ArrayList<>(itemResults);
|
||||
return new AttachmentResult(items, finished);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class AttachmentDimensions {
|
||||
public class AttachmentDimensions {
|
||||
|
||||
final int defaultSize;
|
||||
final int minWidth, maxWidth;
|
||||
@@ -26,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(
|
||||
|
||||
@@ -4,14 +4,12 @@ import android.arch.lifecycle.LiveData;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
public interface AttachmentManager {
|
||||
|
||||
LiveData<AttachmentResult> storeAttachments(Collection<Uri> uri,
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
package org.briarproject.briar.android.attachment;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
import static org.briarproject.briar.android.attachment.AttachmentDimensions.getAttachmentDimensions;
|
||||
|
||||
@Module
|
||||
public class AttachmentModule {
|
||||
|
||||
@Provides
|
||||
ImageHelper provideImageHelper(ImageHelperImpl imageHelper) {
|
||||
return imageHelper;
|
||||
}
|
||||
|
||||
@Provides
|
||||
ImageSizeCalculator provideImageSizeCalculator(ImageHelper imageHelper) {
|
||||
return new ImageSizeCalculator(imageHelper);
|
||||
}
|
||||
|
||||
@Provides
|
||||
AttachmentDimensions provideAttachmentDimensions(Application app) {
|
||||
return getAttachmentDimensions(app.getResources());
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
AttachmentRetriever provideAttachmentRetriever(
|
||||
AttachmentRetrieverImpl attachmentRetriever) {
|
||||
return attachmentRetriever;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
AttachmentCreator provideAttachmentCreator(
|
||||
AttachmentCreatorImpl attachmentCreator) {
|
||||
return attachmentCreator;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,241 @@
|
||||
package org.briarproject.briar.android.attachment;
|
||||
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.BitmapFactory.Options;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
import android.support.media.ExifInterface;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import com.bumptech.glide.util.MarkEnforcingInputStream;
|
||||
|
||||
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.attachment.ImageHelper.DecodeResult;
|
||||
import org.briarproject.briar.api.messaging.Attachment;
|
||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static android.support.media.ExifInterface.ORIENTATION_ROTATE_270;
|
||||
import static android.support.media.ExifInterface.ORIENTATION_ROTATE_90;
|
||||
import static android.support.media.ExifInterface.ORIENTATION_TRANSPOSE;
|
||||
import static android.support.media.ExifInterface.ORIENTATION_TRANSVERSE;
|
||||
import static android.support.media.ExifInterface.TAG_IMAGE_LENGTH;
|
||||
import static android.support.media.ExifInterface.TAG_IMAGE_WIDTH;
|
||||
import static android.support.media.ExifInterface.TAG_ORIENTATION;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface AttachmentRetriever {
|
||||
public class AttachmentRetriever {
|
||||
|
||||
void cachePut(MessageId messageId, List<AttachmentItem> attachments);
|
||||
private static final Logger LOG =
|
||||
getLogger(AttachmentRetriever.class.getName());
|
||||
private static final int READ_LIMIT = 1024 * 8192;
|
||||
|
||||
private final MessagingManager messagingManager;
|
||||
private final ImageHelper imageHelper;
|
||||
private final int defaultSize;
|
||||
private final int minWidth, maxWidth;
|
||||
private final int minHeight, maxHeight;
|
||||
|
||||
private final Map<MessageId, List<AttachmentItem>> attachmentCache =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
@VisibleForTesting
|
||||
AttachmentRetriever(MessagingManager messagingManager,
|
||||
AttachmentDimensions dimensions, ImageHelper imageHelper) {
|
||||
this.messagingManager = messagingManager;
|
||||
this.imageHelper = imageHelper;
|
||||
defaultSize = dimensions.defaultSize;
|
||||
minWidth = dimensions.minWidth;
|
||||
maxWidth = dimensions.maxWidth;
|
||||
minHeight = dimensions.minHeight;
|
||||
maxHeight = dimensions.maxHeight;
|
||||
}
|
||||
|
||||
public AttachmentRetriever(MessagingManager messagingManager,
|
||||
AttachmentDimensions dimensions) {
|
||||
this(messagingManager, dimensions, new ImageHelper() {
|
||||
@Override
|
||||
public DecodeResult decodeStream(InputStream is) {
|
||||
Options options = new Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeStream(is, null, options);
|
||||
String mimeType = options.outMimeType;
|
||||
if (mimeType == null) mimeType = "";
|
||||
return new DecodeResult(options.outWidth, options.outHeight,
|
||||
mimeType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getExtensionFromMimeType(String mimeType) {
|
||||
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
|
||||
return mimeTypeMap.getExtensionFromMimeType(mimeType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void cachePut(MessageId messageId,
|
||||
List<AttachmentItem> attachments) {
|
||||
attachmentCache.put(messageId, attachments);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
List<AttachmentItem> cacheGet(MessageId messageId);
|
||||
public List<AttachmentItem> cacheGet(MessageId messageId) {
|
||||
return attachmentCache.get(messageId);
|
||||
}
|
||||
|
||||
Attachment getMessageAttachment(AttachmentHeader h) throws DbException;
|
||||
public Attachment getMessageAttachment(AttachmentHeader h)
|
||||
throws DbException {
|
||||
return messagingManager.getAttachment(h);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link AttachmentItem} from the {@link Attachment}'s
|
||||
* {@link InputStream} which will be closed when this method returns.
|
||||
*/
|
||||
AttachmentItem getAttachmentItem(Attachment a, boolean needsSize);
|
||||
public AttachmentItem getAttachmentItem(Attachment a, boolean needsSize) {
|
||||
AttachmentHeader h = a.getHeader();
|
||||
if (!needsSize) {
|
||||
String extension =
|
||||
imageHelper.getExtensionFromMimeType(h.getContentType());
|
||||
boolean hasError = false;
|
||||
if (extension == null) {
|
||||
extension = "";
|
||||
hasError = true;
|
||||
}
|
||||
return new AttachmentItem(h, 0, 0, extension, 0, 0, hasError);
|
||||
}
|
||||
|
||||
Size size = new Size();
|
||||
InputStream is = new MarkEnforcingInputStream(
|
||||
new BufferedInputStream(a.getStream()));
|
||||
is.mark(READ_LIMIT);
|
||||
try {
|
||||
// use exif to get size
|
||||
if (h.getContentType().equals("image/jpeg")) {
|
||||
size = getSizeFromExif(is);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
try {
|
||||
// use BitmapFactory to get size
|
||||
if (size.error) {
|
||||
is.reset();
|
||||
// need to mark again to re-add read limit
|
||||
is.mark(READ_LIMIT);
|
||||
size = getSizeFromBitmap(is);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
} finally {
|
||||
tryToClose(is, LOG, WARNING);
|
||||
}
|
||||
|
||||
// calculate thumbnail size
|
||||
Size thumbnailSize = new Size(defaultSize, defaultSize, size.mimeType);
|
||||
if (!size.error) {
|
||||
thumbnailSize =
|
||||
getThumbnailSize(size.width, size.height, size.mimeType);
|
||||
}
|
||||
// get file extension
|
||||
String extension = imageHelper.getExtensionFromMimeType(size.mimeType);
|
||||
boolean hasError = extension == null || size.error;
|
||||
if (!h.getContentType().equals(size.mimeType)) {
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Header has different mime type (" +
|
||||
h.getContentType() + ") than image (" + size.mimeType +
|
||||
").");
|
||||
}
|
||||
hasError = true;
|
||||
}
|
||||
if (extension == null) extension = "";
|
||||
return new AttachmentItem(h, size.width, size.height, extension,
|
||||
thumbnailSize.width, thumbnailSize.height, hasError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of a JPEG {@link InputStream} if EXIF info is available.
|
||||
*/
|
||||
private Size getSizeFromExif(InputStream is) throws IOException {
|
||||
ExifInterface exif = new ExifInterface(is);
|
||||
// these can return 0 independent of default value
|
||||
int width = exif.getAttributeInt(TAG_IMAGE_WIDTH, 0);
|
||||
int height = exif.getAttributeInt(TAG_IMAGE_LENGTH, 0);
|
||||
if (width == 0 || height == 0) return new Size();
|
||||
int orientation = exif.getAttributeInt(TAG_ORIENTATION, 0);
|
||||
if (orientation == ORIENTATION_ROTATE_90 ||
|
||||
orientation == ORIENTATION_ROTATE_270 ||
|
||||
orientation == ORIENTATION_TRANSVERSE ||
|
||||
orientation == ORIENTATION_TRANSPOSE) {
|
||||
//noinspection SuspiciousNameCombination
|
||||
return new Size(height, width, "image/jpeg");
|
||||
}
|
||||
return new Size(width, height, "image/jpeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of any image {@link InputStream}.
|
||||
*/
|
||||
private Size getSizeFromBitmap(InputStream is) {
|
||||
DecodeResult result = imageHelper.decodeStream(is);
|
||||
if (result.width < 1 || result.height < 1) return new Size();
|
||||
return new Size(result.width, result.height, result.mimeType);
|
||||
}
|
||||
|
||||
private Size getThumbnailSize(int width, int height, String mimeType) {
|
||||
float widthPercentage = maxWidth / (float) width;
|
||||
float heightPercentage = maxHeight / (float) height;
|
||||
float scaleFactor = Math.min(widthPercentage, heightPercentage);
|
||||
if (scaleFactor > 1) scaleFactor = 1f;
|
||||
int thumbnailWidth = (int) (width * scaleFactor);
|
||||
int thumbnailHeight = (int) (height * scaleFactor);
|
||||
if (thumbnailWidth < minWidth || thumbnailHeight < minHeight) {
|
||||
widthPercentage = minWidth / (float) width;
|
||||
heightPercentage = minHeight / (float) height;
|
||||
scaleFactor = Math.max(widthPercentage, heightPercentage);
|
||||
thumbnailWidth = (int) (width * scaleFactor);
|
||||
thumbnailHeight = (int) (height * scaleFactor);
|
||||
if (thumbnailWidth > maxWidth) thumbnailWidth = maxWidth;
|
||||
if (thumbnailHeight > maxHeight) thumbnailHeight = maxHeight;
|
||||
}
|
||||
return new Size(thumbnailWidth, thumbnailHeight, mimeType);
|
||||
}
|
||||
|
||||
private static class Size {
|
||||
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final String mimeType;
|
||||
private final boolean error;
|
||||
|
||||
private Size(int width, int height, String mimeType) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.mimeType = mimeType;
|
||||
this.error = false;
|
||||
}
|
||||
|
||||
private Size() {
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
this.mimeType = "";
|
||||
this.error = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
package org.briarproject.briar.android.attachment;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.api.messaging.Attachment;
|
||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
|
||||
@NotNullByDefault
|
||||
class AttachmentRetrieverImpl implements AttachmentRetriever {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(AttachmentRetrieverImpl.class.getName());
|
||||
|
||||
private final MessagingManager messagingManager;
|
||||
private final ImageHelper imageHelper;
|
||||
private final ImageSizeCalculator imageSizeCalculator;
|
||||
private final int defaultSize;
|
||||
private final int minWidth, maxWidth;
|
||||
private final int minHeight, maxHeight;
|
||||
|
||||
private final Map<MessageId, List<AttachmentItem>> attachmentCache =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
@Inject
|
||||
AttachmentRetrieverImpl(MessagingManager messagingManager,
|
||||
AttachmentDimensions dimensions, ImageHelper imageHelper,
|
||||
ImageSizeCalculator imageSizeCalculator) {
|
||||
this.messagingManager = messagingManager;
|
||||
this.imageHelper = imageHelper;
|
||||
this.imageSizeCalculator = imageSizeCalculator;
|
||||
defaultSize = dimensions.defaultSize;
|
||||
minWidth = dimensions.minWidth;
|
||||
maxWidth = dimensions.maxWidth;
|
||||
minHeight = dimensions.minHeight;
|
||||
maxHeight = dimensions.maxHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cachePut(MessageId messageId,
|
||||
List<AttachmentItem> attachments) {
|
||||
attachmentCache.put(messageId, attachments);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public List<AttachmentItem> cacheGet(MessageId messageId) {
|
||||
return attachmentCache.get(messageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attachment getMessageAttachment(AttachmentHeader h)
|
||||
throws DbException {
|
||||
return messagingManager.getAttachment(h);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttachmentItem getAttachmentItem(Attachment a, boolean needsSize) {
|
||||
AttachmentHeader h = a.getHeader();
|
||||
if (!needsSize) {
|
||||
String extension =
|
||||
imageHelper.getExtensionFromMimeType(h.getContentType());
|
||||
boolean hasError = false;
|
||||
if (extension == null) {
|
||||
extension = "";
|
||||
hasError = true;
|
||||
}
|
||||
return new AttachmentItem(h, 0, 0, extension, 0, 0, hasError);
|
||||
}
|
||||
|
||||
InputStream is = new BufferedInputStream(a.getStream());
|
||||
Size size = imageSizeCalculator.getSize(is, h.getContentType());
|
||||
|
||||
// calculate thumbnail size
|
||||
Size thumbnailSize = new Size(defaultSize, defaultSize, size.mimeType);
|
||||
if (!size.error) {
|
||||
thumbnailSize =
|
||||
getThumbnailSize(size.width, size.height, size.mimeType);
|
||||
}
|
||||
// get file extension
|
||||
String extension = imageHelper.getExtensionFromMimeType(size.mimeType);
|
||||
boolean hasError = extension == null || size.error;
|
||||
if (!h.getContentType().equals(size.mimeType)) {
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Header has different mime type (" +
|
||||
h.getContentType() + ") than image (" + size.mimeType +
|
||||
").");
|
||||
}
|
||||
hasError = true;
|
||||
}
|
||||
if (extension == null) extension = "";
|
||||
return new AttachmentItem(h, size.width, size.height, extension,
|
||||
thumbnailSize.width, thumbnailSize.height, hasError);
|
||||
}
|
||||
|
||||
private Size getThumbnailSize(int width, int height, String mimeType) {
|
||||
float widthPercentage = maxWidth / (float) width;
|
||||
float heightPercentage = maxHeight / (float) height;
|
||||
float scaleFactor = Math.min(widthPercentage, heightPercentage);
|
||||
if (scaleFactor > 1) scaleFactor = 1f;
|
||||
int thumbnailWidth = (int) (width * scaleFactor);
|
||||
int thumbnailHeight = (int) (height * scaleFactor);
|
||||
if (thumbnailWidth < minWidth || thumbnailHeight < minHeight) {
|
||||
widthPercentage = minWidth / (float) width;
|
||||
heightPercentage = minHeight / (float) height;
|
||||
scaleFactor = Math.max(widthPercentage, heightPercentage);
|
||||
thumbnailWidth = (int) (width * scaleFactor);
|
||||
thumbnailHeight = (int) (height * scaleFactor);
|
||||
if (thumbnailWidth > maxWidth) thumbnailWidth = maxWidth;
|
||||
if (thumbnailHeight > maxHeight) thumbnailHeight = maxHeight;
|
||||
}
|
||||
return new Size(thumbnailWidth, thumbnailHeight, mimeType);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import java.io.InputStream;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface ImageHelper {
|
||||
interface ImageHelper {
|
||||
|
||||
DecodeResult decodeStream(InputStream is);
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package org.briarproject.briar.android.attachment;
|
||||
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class ImageHelperImpl implements ImageHelper {
|
||||
|
||||
@Inject
|
||||
ImageHelperImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecodeResult decodeStream(InputStream is) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeStream(is, null, options);
|
||||
String mimeType = options.outMimeType;
|
||||
if (mimeType == null) mimeType = "";
|
||||
return new DecodeResult(options.outWidth, options.outHeight,
|
||||
mimeType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getExtensionFromMimeType(String mimeType) {
|
||||
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
|
||||
return mimeTypeMap.getExtensionFromMimeType(mimeType);
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package org.briarproject.briar.android.attachment;
|
||||
|
||||
import android.support.media.ExifInterface;
|
||||
|
||||
import com.bumptech.glide.util.MarkEnforcingInputStream;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.android.attachment.ImageHelper.DecodeResult;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static android.support.media.ExifInterface.ORIENTATION_ROTATE_270;
|
||||
import static android.support.media.ExifInterface.ORIENTATION_ROTATE_90;
|
||||
import static android.support.media.ExifInterface.ORIENTATION_TRANSPOSE;
|
||||
import static android.support.media.ExifInterface.ORIENTATION_TRANSVERSE;
|
||||
import static android.support.media.ExifInterface.TAG_IMAGE_LENGTH;
|
||||
import static android.support.media.ExifInterface.TAG_IMAGE_WIDTH;
|
||||
import static android.support.media.ExifInterface.TAG_ORIENTATION;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@NotNullByDefault
|
||||
class ImageSizeCalculator {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(ImageSizeCalculator.class.getName());
|
||||
|
||||
private static final int READ_LIMIT = 1024 * 8192;
|
||||
|
||||
private final ImageHelper imageHelper;
|
||||
|
||||
ImageSizeCalculator(ImageHelper imageHelper) {
|
||||
this.imageHelper = imageHelper;
|
||||
}
|
||||
|
||||
Size getSize(InputStream is, String contentType) {
|
||||
Size size = new Size();
|
||||
is = new MarkEnforcingInputStream(is);
|
||||
is.mark(READ_LIMIT);
|
||||
if (contentType.equals("image/jpeg")) {
|
||||
try {
|
||||
// use exif to get size
|
||||
size = getSizeFromExif(is);
|
||||
is.reset();
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
}
|
||||
if (size.error) {
|
||||
// need to mark again to re-add read limit
|
||||
is.mark(READ_LIMIT);
|
||||
try {
|
||||
// use BitmapFactory to get size
|
||||
size = getSizeFromBitmap(is);
|
||||
is.reset();
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of a JPEG {@link InputStream} if EXIF info is available.
|
||||
*/
|
||||
private Size getSizeFromExif(InputStream is) throws IOException {
|
||||
ExifInterface exif = new ExifInterface(is);
|
||||
// these can return 0 independent of default value
|
||||
int width = exif.getAttributeInt(TAG_IMAGE_WIDTH, 0);
|
||||
int height = exif.getAttributeInt(TAG_IMAGE_LENGTH, 0);
|
||||
if (width == 0 || height == 0) return new Size();
|
||||
int orientation = exif.getAttributeInt(TAG_ORIENTATION, 0);
|
||||
if (orientation == ORIENTATION_ROTATE_90 ||
|
||||
orientation == ORIENTATION_ROTATE_270 ||
|
||||
orientation == ORIENTATION_TRANSVERSE ||
|
||||
orientation == ORIENTATION_TRANSPOSE) {
|
||||
//noinspection SuspiciousNameCombination
|
||||
return new Size(height, width, "image/jpeg");
|
||||
}
|
||||
return new Size(width, height, "image/jpeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of any image {@link InputStream}.
|
||||
*/
|
||||
private Size getSizeFromBitmap(InputStream is) {
|
||||
DecodeResult result = imageHelper.decodeStream(is);
|
||||
if (result.width < 1 || result.height < 1) return new Size();
|
||||
return new Size(result.width, result.height, result.mimeType);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package org.briarproject.briar.android.attachment;
|
||||
|
||||
class Size {
|
||||
|
||||
final int width;
|
||||
final int height;
|
||||
final String mimeType;
|
||||
final boolean error;
|
||||
|
||||
Size(int width, int height, String mimeType) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.mimeType = mimeType;
|
||||
this.error = false;
|
||||
}
|
||||
|
||||
Size() {
|
||||
this.width = 0;
|
||||
this.height = 0;
|
||||
this.mimeType = "";
|
||||
this.error = true;
|
||||
}
|
||||
}
|
||||
@@ -82,20 +82,19 @@ public class NicknameFragment extends BaseFragment {
|
||||
|
||||
@Nullable
|
||||
private String getNicknameOrNull() {
|
||||
Editable text = contactNameInput.getText();
|
||||
if (text == null || text.toString().trim().length() == 0) {
|
||||
Editable name = contactNameInput.getText();
|
||||
if (name == null || name.toString().trim().length() == 0) {
|
||||
contactNameLayout.setError(getString(R.string.nickname_missing));
|
||||
contactNameInput.requestFocus();
|
||||
return null;
|
||||
}
|
||||
String name = text.toString().trim();
|
||||
if (utf8IsTooLong(name, MAX_AUTHOR_NAME_LENGTH)) {
|
||||
if (utf8IsTooLong(name.toString(), MAX_AUTHOR_NAME_LENGTH)) {
|
||||
contactNameLayout.setError(getString(R.string.name_too_long));
|
||||
contactNameInput.requestFocus();
|
||||
return null;
|
||||
}
|
||||
contactNameLayout.setError(null);
|
||||
return name;
|
||||
return name.toString().trim();
|
||||
}
|
||||
|
||||
private void onAddButtonClicked() {
|
||||
|
||||
@@ -82,7 +82,7 @@ public class AliasDialogFragment extends AppCompatDialogFragment {
|
||||
}
|
||||
|
||||
private void onSetButtonClicked() {
|
||||
String alias = aliasEditText.getText().toString().trim();
|
||||
String alias = aliasEditText.getText().toString();
|
||||
if (toUtf8(alias).length > MAX_AUTHOR_NAME_LENGTH) {
|
||||
aliasEditLayout.setError(getString(R.string.name_too_long));
|
||||
} else {
|
||||
|
||||
@@ -18,6 +18,7 @@ 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;
|
||||
@@ -50,6 +51,7 @@ 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.attachment.AttachmentDimensions.getAttachmentDimensions;
|
||||
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||
import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce;
|
||||
|
||||
@@ -99,13 +101,10 @@ public class ConversationViewModel extends AndroidViewModel
|
||||
@Inject
|
||||
ConversationViewModel(Application application,
|
||||
@DatabaseExecutor Executor dbExecutor,
|
||||
TransactionManager db,
|
||||
MessagingManager messagingManager,
|
||||
ContactManager contactManager,
|
||||
@IoExecutor Executor ioExecutor, TransactionManager db,
|
||||
MessagingManager messagingManager, ContactManager contactManager,
|
||||
SettingsManager settingsManager,
|
||||
PrivateMessageFactory privateMessageFactory,
|
||||
AttachmentRetriever attachmentRetriever,
|
||||
AttachmentCreator attachmentCreator) {
|
||||
PrivateMessageFactory privateMessageFactory) {
|
||||
super(application);
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.db = db;
|
||||
@@ -113,8 +112,10 @@ public class ConversationViewModel extends AndroidViewModel
|
||||
this.contactManager = contactManager;
|
||||
this.settingsManager = settingsManager;
|
||||
this.privateMessageFactory = privateMessageFactory;
|
||||
this.attachmentRetriever = attachmentRetriever;
|
||||
this.attachmentCreator = attachmentCreator;
|
||||
this.attachmentRetriever = new AttachmentRetriever(messagingManager,
|
||||
getAttachmentDimensions(application.getResources()));
|
||||
this.attachmentCreator = new AttachmentCreator(getApplication(),
|
||||
ioExecutor, messagingManager, attachmentRetriever);
|
||||
messagingGroupId = Transformations
|
||||
.map(contact, c -> messagingManager.getContactGroup(c).getId());
|
||||
contactDeleted.setValue(false);
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:hint="@string/set_contact_alias_hint"
|
||||
android:inputType="text|textCapWords"
|
||||
android:inputType="textPersonName"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="@dimen/text_size_medium"/>
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@ package org.briarproject.briar.android.attachment;
|
||||
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
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;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.lib.legacy.ClassImposteriser;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -24,18 +24,14 @@ public class AttachmentRetrieverTest extends BrambleMockTestCase {
|
||||
100, 50, 200, 75, 300
|
||||
);
|
||||
private final MessageId msgId = new MessageId(getRandomId());
|
||||
private final MessagingManager messagingManager =
|
||||
context.mock(MessagingManager.class);
|
||||
private final ImageHelper imageHelper = context.mock(ImageHelper.class);
|
||||
private final ImageSizeCalculator imageSizeCalculator;
|
||||
private final AttachmentRetriever retriever;
|
||||
|
||||
public AttachmentRetrieverTest() {
|
||||
context.setImposteriser(ClassImposteriser.INSTANCE);
|
||||
MessagingManager messagingManager =
|
||||
context.mock(MessagingManager.class);
|
||||
imageSizeCalculator = context.mock(ImageSizeCalculator.class);
|
||||
retriever = new AttachmentRetrieverImpl(messagingManager, dimensions,
|
||||
imageHelper, imageSizeCalculator);
|
||||
}
|
||||
private final AttachmentRetriever retriever = new AttachmentRetriever(
|
||||
messagingManager,
|
||||
dimensions,
|
||||
imageHelper
|
||||
);
|
||||
|
||||
@Test
|
||||
public void testNoSize() {
|
||||
@@ -73,9 +69,8 @@ public class AttachmentRetrieverTest extends BrambleMockTestCase {
|
||||
Attachment attachment = getAttachment(mimeType);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(imageSizeCalculator).getSize(with(any(InputStream.class)),
|
||||
with(mimeType));
|
||||
will(returnValue(new Size(160, 240, mimeType)));
|
||||
oneOf(imageHelper).decodeStream(with(any(InputStream.class)));
|
||||
will(returnValue(new DecodeResult(160, 240, mimeType)));
|
||||
oneOf(imageHelper).getExtensionFromMimeType(mimeType);
|
||||
will(returnValue("jpg"));
|
||||
}});
|
||||
@@ -97,9 +92,8 @@ public class AttachmentRetrieverTest extends BrambleMockTestCase {
|
||||
Attachment attachment = getAttachment(mimeType);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(imageSizeCalculator).getSize(with(any(InputStream.class)),
|
||||
with(mimeType));
|
||||
will(returnValue(new Size(1728, 2592, mimeType)));
|
||||
oneOf(imageHelper).decodeStream(with(any(InputStream.class)));
|
||||
will(returnValue(new DecodeResult(1728, 2592, mimeType)));
|
||||
oneOf(imageHelper).getExtensionFromMimeType(mimeType);
|
||||
will(returnValue("jpg"));
|
||||
}});
|
||||
@@ -114,13 +108,11 @@ public class AttachmentRetrieverTest extends BrambleMockTestCase {
|
||||
|
||||
@Test
|
||||
public void testMalformedError() {
|
||||
String mimeType = "image/jpeg";
|
||||
Attachment attachment = getAttachment(mimeType);
|
||||
Attachment attachment = getAttachment("image/jpeg");
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(imageSizeCalculator).getSize(with(any(InputStream.class)),
|
||||
with(mimeType));
|
||||
will(returnValue(new Size()));
|
||||
oneOf(imageHelper).decodeStream(with(any(InputStream.class)));
|
||||
will(returnValue(new DecodeResult(0, 0, "")));
|
||||
oneOf(imageHelper).getExtensionFromMimeType("");
|
||||
will(returnValue(null));
|
||||
}});
|
||||
|
||||
@@ -30,7 +30,7 @@ public interface MessagingManager extends ConversationClient {
|
||||
/**
|
||||
* The current minor version of the messaging client.
|
||||
*/
|
||||
int MINOR_VERSION = 2;
|
||||
int MINOR_VERSION = 1;
|
||||
|
||||
/**
|
||||
* Stores a local private message.
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.briarproject.bramble.api.sync.Message;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.bramble.api.sync.MessageStatus;
|
||||
import org.briarproject.bramble.api.sync.validation.IncomingMessageHook;
|
||||
import org.briarproject.bramble.api.system.Scheduler;
|
||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
|
||||
import org.briarproject.briar.api.client.MessageTracker;
|
||||
@@ -47,13 +48,19 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
|
||||
import static org.briarproject.briar.messaging.MessageTypes.ATTACHMENT;
|
||||
import static org.briarproject.briar.messaging.MessageTypes.PRIVATE_MESSAGE;
|
||||
@@ -72,6 +79,10 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
|
||||
ConversationClient, OpenDatabaseHook, ContactHook,
|
||||
ClientVersioningHook {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(MessagingManagerImpl.class.getName());
|
||||
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final DatabaseComponent db;
|
||||
private final ClientHelper clientHelper;
|
||||
private final MetadataParser metadataParser;
|
||||
@@ -80,10 +91,12 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
|
||||
private final ContactGroupFactory contactGroupFactory;
|
||||
|
||||
@Inject
|
||||
MessagingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
|
||||
MessagingManagerImpl(@Scheduler ScheduledExecutorService scheduler,
|
||||
DatabaseComponent db, ClientHelper clientHelper,
|
||||
ClientVersioningManager clientVersioningManager,
|
||||
MetadataParser metadataParser, MessageTracker messageTracker,
|
||||
ContactGroupFactory contactGroupFactory) {
|
||||
this.scheduler = scheduler;
|
||||
this.db = db;
|
||||
this.clientHelper = clientHelper;
|
||||
this.metadataParser = metadataParser;
|
||||
@@ -239,9 +252,20 @@ class MessagingManagerImpl implements MessagingManager, IncomingMessageHook,
|
||||
meta.put(MSG_KEY_ATTACHMENT_HEADERS, headers);
|
||||
}
|
||||
// Mark attachments as shared and permanent now we're ready to send
|
||||
// FIXME: Revert
|
||||
int i = 15;
|
||||
for (AttachmentHeader a : m.getAttachmentHeaders()) {
|
||||
db.setMessageShared(txn, a.getMessageId());
|
||||
db.setMessagePermanent(txn, a.getMessageId());
|
||||
scheduler.schedule(() -> {
|
||||
try {
|
||||
db.transaction(false, txn1 -> {
|
||||
db.setMessageShared(txn1, a.getMessageId());
|
||||
db.setMessagePermanent(txn1, a.getMessageId());
|
||||
});
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
}, i, SECONDS);
|
||||
i *= 2;
|
||||
}
|
||||
clientHelper.addLocalMessage(txn, m.getMessage(), meta, true,
|
||||
false);
|
||||
|
||||
@@ -65,7 +65,7 @@ public class MessagingModule {
|
||||
conversationManager.registerConversationClient(messagingManager);
|
||||
// Advertise the current or previous minor version depending on the
|
||||
// feature flag
|
||||
int minorVersion = featureFlags.shouldEnableImageAttachments() ? 2 : 0;
|
||||
int minorVersion = featureFlags.shouldEnableImageAttachments() ? 1 : 0;
|
||||
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
|
||||
minorVersion, messagingManager);
|
||||
return messagingManager;
|
||||
|
||||
Reference in New Issue
Block a user