mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 05:09:53 +01:00
Compress image attachments.
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
package org.briarproject.briar.android.attachment;
|
package org.briarproject.briar.android.attachment;
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory.Options;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
@@ -12,11 +14,17 @@ import org.briarproject.briar.api.messaging.AttachmentHeader;
|
|||||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
import org.jsoup.UnsupportedMimeTypeException;
|
import org.jsoup.UnsupportedMimeTypeException;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.logging.Logger;
|
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.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||||
@@ -24,6 +32,7 @@ import static org.briarproject.bramble.util.LogUtils.logDuration;
|
|||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
import static org.briarproject.briar.api.messaging.MessagingConstants.IMAGE_MIME_TYPES;
|
import static org.briarproject.briar.api.messaging.MessagingConstants.IMAGE_MIME_TYPES;
|
||||||
|
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_IMAGE_SIZE;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class AttachmentCreationTask {
|
class AttachmentCreationTask {
|
||||||
@@ -31,8 +40,11 @@ class AttachmentCreationTask {
|
|||||||
private static Logger LOG =
|
private static Logger LOG =
|
||||||
getLogger(AttachmentCreationTask.class.getName());
|
getLogger(AttachmentCreationTask.class.getName());
|
||||||
|
|
||||||
|
private static final int MAX_ATTACHMENT_DIMENSION = 1000;
|
||||||
|
|
||||||
private final MessagingManager messagingManager;
|
private final MessagingManager messagingManager;
|
||||||
private final ContentResolver contentResolver;
|
private final ContentResolver contentResolver;
|
||||||
|
private final ImageSizeCalculator imageSizeCalculator;
|
||||||
private final GroupId groupId;
|
private final GroupId groupId;
|
||||||
private final Collection<Uri> uris;
|
private final Collection<Uri> uris;
|
||||||
private final boolean needsSize;
|
private final boolean needsSize;
|
||||||
@@ -43,24 +55,26 @@ class AttachmentCreationTask {
|
|||||||
|
|
||||||
AttachmentCreationTask(MessagingManager messagingManager,
|
AttachmentCreationTask(MessagingManager messagingManager,
|
||||||
ContentResolver contentResolver,
|
ContentResolver contentResolver,
|
||||||
AttachmentCreator attachmentCreator, GroupId groupId,
|
AttachmentCreator attachmentCreator,
|
||||||
Collection<Uri> uris, boolean needsSize) {
|
ImageSizeCalculator imageSizeCalculator,
|
||||||
|
GroupId groupId, Collection<Uri> uris, boolean needsSize) {
|
||||||
this.messagingManager = messagingManager;
|
this.messagingManager = messagingManager;
|
||||||
this.contentResolver = contentResolver;
|
this.contentResolver = contentResolver;
|
||||||
|
this.imageSizeCalculator = imageSizeCalculator;
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
this.uris = uris;
|
this.uris = uris;
|
||||||
this.needsSize = needsSize;
|
this.needsSize = needsSize;
|
||||||
this.attachmentCreator = attachmentCreator;
|
this.attachmentCreator = attachmentCreator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancel() {
|
void cancel() {
|
||||||
canceled = true;
|
canceled = true;
|
||||||
attachmentCreator = null;
|
attachmentCreator = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@IoExecutor
|
@IoExecutor
|
||||||
public void storeAttachments() {
|
void storeAttachments() {
|
||||||
for (Uri uri: uris) processUri(uri);
|
for (Uri uri : uris) processUri(uri);
|
||||||
AttachmentCreator attachmentCreator = this.attachmentCreator;
|
AttachmentCreator attachmentCreator = this.attachmentCreator;
|
||||||
if (!canceled && attachmentCreator != null)
|
if (!canceled && attachmentCreator != null)
|
||||||
attachmentCreator.onAttachmentCreationFinished();
|
attachmentCreator.onAttachmentCreationFinished();
|
||||||
@@ -98,6 +112,8 @@ class AttachmentCreationTask {
|
|||||||
}
|
}
|
||||||
InputStream is = contentResolver.openInputStream(uri);
|
InputStream is = contentResolver.openInputStream(uri);
|
||||||
if (is == null) throw new IOException();
|
if (is == null) throw new IOException();
|
||||||
|
is = compressImage(is, contentType);
|
||||||
|
contentType = "image/jpeg";
|
||||||
long timestamp = System.currentTimeMillis();
|
long timestamp = System.currentTimeMillis();
|
||||||
AttachmentHeader h = messagingManager
|
AttachmentHeader h = messagingManager
|
||||||
.addLocalAttachment(groupId, timestamp, contentType, is);
|
.addLocalAttachment(groupId, timestamp, contentType, is);
|
||||||
@@ -113,4 +129,48 @@ class AttachmentCreationTask {
|
|||||||
return false;
|
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 scale = 1;
|
||||||
|
while (dimension > MAX_ATTACHMENT_DIMENSION) {
|
||||||
|
scale *= 2;
|
||||||
|
dimension /= 2;
|
||||||
|
}
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Scaling attachment by factor of " + scale);
|
||||||
|
Options options = new Options();
|
||||||
|
options.inSampleSize = scale;
|
||||||
|
Bitmap bitmap = decodeStream(is, null, options);
|
||||||
|
if (bitmap == null) throw new IOException();
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ class AttachmentCreatorImpl implements AttachmentCreator {
|
|||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
private final MessagingManager messagingManager;
|
private final MessagingManager messagingManager;
|
||||||
private final AttachmentRetriever retriever;
|
private final AttachmentRetriever retriever;
|
||||||
|
private final ImageSizeCalculator imageSizeCalculator;
|
||||||
|
|
||||||
private final CopyOnWriteArrayList<Uri> uris = new CopyOnWriteArrayList<>();
|
private final CopyOnWriteArrayList<Uri> uris = new CopyOnWriteArrayList<>();
|
||||||
private final CopyOnWriteArrayList<AttachmentItemResult> itemResults =
|
private final CopyOnWriteArrayList<AttachmentItemResult> itemResults =
|
||||||
@@ -60,11 +61,13 @@ class AttachmentCreatorImpl implements AttachmentCreator {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
AttachmentCreatorImpl(Application app, @IoExecutor Executor ioExecutor,
|
AttachmentCreatorImpl(Application app, @IoExecutor Executor ioExecutor,
|
||||||
MessagingManager messagingManager, AttachmentRetriever retriever) {
|
MessagingManager messagingManager, AttachmentRetriever retriever,
|
||||||
|
ImageSizeCalculator imageSizeCalculator) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.messagingManager = messagingManager;
|
this.messagingManager = messagingManager;
|
||||||
this.retriever = retriever;
|
this.retriever = retriever;
|
||||||
|
this.imageSizeCalculator = imageSizeCalculator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -78,7 +81,8 @@ class AttachmentCreatorImpl implements AttachmentCreator {
|
|||||||
if (id == null) throw new IllegalStateException();
|
if (id == null) throw new IllegalStateException();
|
||||||
boolean needsSize = uris.size() == 1;
|
boolean needsSize = uris.size() == 1;
|
||||||
task = new AttachmentCreationTask(messagingManager,
|
task = new AttachmentCreationTask(messagingManager,
|
||||||
app.getContentResolver(), this, id, uris, needsSize);
|
app.getContentResolver(), this, imageSizeCalculator, id,
|
||||||
|
uris, needsSize);
|
||||||
ioExecutor.execute(() -> task.storeAttachments());
|
ioExecutor.execute(() -> task.storeAttachments());
|
||||||
});
|
});
|
||||||
MutableLiveData<AttachmentResult> result = new MutableLiveData<>();
|
MutableLiveData<AttachmentResult> result = new MutableLiveData<>();
|
||||||
|
|||||||
Reference in New Issue
Block a user