Separate compression job.
This commit is contained in:
parent
7f0a7b0c13
commit
942154a61f
17 changed files with 424 additions and 308 deletions
|
@ -2,14 +2,15 @@ package org.thoughtcrime.securesms.components;
|
|||
|
||||
import android.animation.LayoutTransition;
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
import com.pnikosis.materialishprogress.ProgressWheel;
|
||||
|
||||
|
@ -28,7 +29,14 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class TransferControlView extends FrameLayout {
|
||||
public final class TransferControlView extends FrameLayout {
|
||||
|
||||
private static final int UPLOAD_TASK_WEIGHT = 1;
|
||||
|
||||
/**
|
||||
* A weighting compared to {@link #UPLOAD_TASK_WEIGHT}
|
||||
*/
|
||||
private static final int COMPRESSION_TASK_WEIGHT = 3;
|
||||
|
||||
@Nullable private List<Slide> slides;
|
||||
@Nullable private View current;
|
||||
|
@ -37,7 +45,8 @@ public class TransferControlView extends FrameLayout {
|
|||
private final View downloadDetails;
|
||||
private final TextView downloadDetailsText;
|
||||
|
||||
private final Map<Attachment, Float> downloadProgress;
|
||||
private final Map<Attachment, Float> networkProgress;
|
||||
private final Map<Attachment, Float> compresssionProgress;
|
||||
|
||||
public TransferControlView(Context context) {
|
||||
this(context, null);
|
||||
|
@ -56,7 +65,9 @@ public class TransferControlView extends FrameLayout {
|
|||
setVisibility(GONE);
|
||||
setLayoutTransition(new LayoutTransition());
|
||||
|
||||
this.downloadProgress = new HashMap<>();
|
||||
this.networkProgress = new HashMap<>();
|
||||
this.compresssionProgress = new HashMap<>();
|
||||
|
||||
this.progressWheel = ViewUtil.findById(this, R.id.progress_wheel);
|
||||
this.downloadDetails = ViewUtil.findById(this, R.id.download_details);
|
||||
this.downloadDetailsText = ViewUtil.findById(this, R.id.download_details_text);
|
||||
|
@ -98,19 +109,20 @@ public class TransferControlView extends FrameLayout {
|
|||
this.slides = slides;
|
||||
|
||||
if (!isUpdateToExistingSet(slides)) {
|
||||
downloadProgress.clear();
|
||||
Stream.of(slides).forEach(s -> downloadProgress.put(s.asAttachment(), 0f));
|
||||
networkProgress.clear();
|
||||
compresssionProgress.clear();
|
||||
Stream.of(slides).forEach(s -> networkProgress.put(s.asAttachment(), 0f));
|
||||
}
|
||||
|
||||
for (Slide slide : slides) {
|
||||
if (slide.asAttachment().getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE) {
|
||||
downloadProgress.put(slide.asAttachment(), 1f);
|
||||
networkProgress.put(slide.asAttachment(), 1f);
|
||||
}
|
||||
}
|
||||
|
||||
switch (getTransferState(slides)) {
|
||||
case AttachmentDatabase.TRANSFER_PROGRESS_STARTED:
|
||||
showProgressSpinner(calculateProgress(downloadProgress));
|
||||
showProgressSpinner(calculateProgress(networkProgress, compresssionProgress));
|
||||
break;
|
||||
case AttachmentDatabase.TRANSFER_PROGRESS_PENDING:
|
||||
case AttachmentDatabase.TRANSFER_PROGRESS_FAILED:
|
||||
|
@ -124,7 +136,7 @@ public class TransferControlView extends FrameLayout {
|
|||
}
|
||||
|
||||
public void showProgressSpinner() {
|
||||
showProgressSpinner(calculateProgress(downloadProgress));
|
||||
showProgressSpinner(calculateProgress(networkProgress, compresssionProgress));
|
||||
}
|
||||
|
||||
public void showProgressSpinner(float progress) {
|
||||
|
@ -158,12 +170,12 @@ public class TransferControlView extends FrameLayout {
|
|||
}
|
||||
|
||||
private boolean isUpdateToExistingSet(@NonNull List<Slide> slides) {
|
||||
if (slides.size() != downloadProgress.size()) {
|
||||
if (slides.size() != networkProgress.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Slide slide : slides) {
|
||||
if (!downloadProgress.containsKey(slide.asAttachment())) {
|
||||
if (!networkProgress.containsKey(slide.asAttachment())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -207,19 +219,36 @@ public class TransferControlView extends FrameLayout {
|
|||
current = view;
|
||||
}
|
||||
|
||||
private float calculateProgress(@NonNull Map<Attachment, Float> downloadProgress) {
|
||||
float totalProgress = 0;
|
||||
for (float progress : downloadProgress.values()) {
|
||||
totalProgress += progress / downloadProgress.size();
|
||||
private static float calculateProgress(@NonNull Map<Attachment, Float> uploadDownloadProgress, Map<Attachment, Float> compresssionProgress) {
|
||||
float totalDownloadProgress = 0;
|
||||
float totalCompressionProgress = 0;
|
||||
|
||||
for (float progress : uploadDownloadProgress.values()) {
|
||||
totalDownloadProgress += progress;
|
||||
}
|
||||
return totalProgress;
|
||||
|
||||
for (float progress : compresssionProgress.values()) {
|
||||
totalCompressionProgress += progress;
|
||||
}
|
||||
|
||||
float weightedProgress = UPLOAD_TASK_WEIGHT * totalDownloadProgress + COMPRESSION_TASK_WEIGHT * totalCompressionProgress;
|
||||
float weightedTotal = UPLOAD_TASK_WEIGHT * uploadDownloadProgress.size() + COMPRESSION_TASK_WEIGHT * compresssionProgress.size();
|
||||
|
||||
return weightedProgress / weightedTotal;
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
public void onEventAsync(final PartProgressEvent event) {
|
||||
if (downloadProgress.containsKey(event.attachment)) {
|
||||
downloadProgress.put(event.attachment, ((float) event.progress) / event.total);
|
||||
progressWheel.setInstantProgress(calculateProgress(downloadProgress));
|
||||
if (networkProgress.containsKey(event.attachment)) {
|
||||
float proportionCompleted = ((float) event.progress) / event.total;
|
||||
|
||||
if (event.type == PartProgressEvent.Type.COMPRESSION) {
|
||||
compresssionProgress.put(event.attachment, proportionCompleted);
|
||||
} else {
|
||||
networkProgress.put(event.attachment, proportionCompleted);
|
||||
}
|
||||
|
||||
progressWheel.setInstantProgress(calculateProgress(networkProgress, compresssionProgress));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1340,9 +1340,9 @@ public class ConversationItem extends LinearLayout
|
|||
database.markAsOutbox(messageRecord.getId());
|
||||
database.markAsForcedSms(messageRecord.getId());
|
||||
|
||||
ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.add(new MmsSendJob(messageRecord.getId()));
|
||||
MmsSendJob.enqueue(context,
|
||||
ApplicationContext.getInstance(context).getJobManager(),
|
||||
messageRecord.getId());
|
||||
} else {
|
||||
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
|
||||
database.markAsInsecure(messageRecord.getId());
|
||||
|
|
|
@ -497,8 +497,8 @@ public class AttachmentDatabase extends Database {
|
|||
return insertedAttachments;
|
||||
}
|
||||
|
||||
public @NonNull DatabaseAttachment updateAttachmentData(@NonNull DatabaseAttachment databaseAttachment,
|
||||
@NonNull MediaStream mediaStream)
|
||||
public void updateAttachmentData(@NonNull DatabaseAttachment databaseAttachment,
|
||||
@NonNull MediaStream mediaStream)
|
||||
throws MmsException
|
||||
{
|
||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||
|
@ -518,29 +518,8 @@ public class AttachmentDatabase extends Database {
|
|||
contentValues.put(DATA_RANDOM, dataInfo.random);
|
||||
|
||||
database.update(TABLE_NAME, contentValues, PART_ID_WHERE, databaseAttachment.getAttachmentId().toStrings());
|
||||
|
||||
return new DatabaseAttachment(databaseAttachment.getAttachmentId(),
|
||||
databaseAttachment.getMmsId(),
|
||||
databaseAttachment.hasData(),
|
||||
databaseAttachment.hasThumbnail(),
|
||||
mediaStream.getMimeType(),
|
||||
databaseAttachment.getTransferState(),
|
||||
dataInfo.length,
|
||||
databaseAttachment.getFileName(),
|
||||
databaseAttachment.getLocation(),
|
||||
databaseAttachment.getKey(),
|
||||
databaseAttachment.getRelay(),
|
||||
databaseAttachment.getDigest(),
|
||||
databaseAttachment.getFastPreflightId(),
|
||||
databaseAttachment.isVoiceNote(),
|
||||
mediaStream.getWidth(),
|
||||
mediaStream.getHeight(),
|
||||
databaseAttachment.isQuote(),
|
||||
databaseAttachment.getCaption(),
|
||||
databaseAttachment.getSticker());
|
||||
}
|
||||
|
||||
|
||||
public void updateAttachmentFileName(@NonNull AttachmentId attachmentId,
|
||||
@Nullable String fileName)
|
||||
{
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
package org.thoughtcrime.securesms.events;
|
||||
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
|
||||
public class PartProgressEvent {
|
||||
public final class PartProgressEvent {
|
||||
|
||||
public final Attachment attachment;
|
||||
public final Type type;
|
||||
public final long total;
|
||||
public final long progress;
|
||||
|
||||
public PartProgressEvent(@NonNull Attachment attachment, long total, long progress) {
|
||||
public enum Type {
|
||||
COMPRESSION,
|
||||
NETWORK
|
||||
}
|
||||
|
||||
public PartProgressEvent(@NonNull Attachment attachment, @NonNull Type type, long total, long progress) {
|
||||
this.attachment = attachment;
|
||||
this.type = type;
|
||||
this.total = total;
|
||||
this.progress = progress;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.MediaDataSource;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.events.PartProgressEvent;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.mms.MediaStream;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.service.GenericForegroundService;
|
||||
import org.thoughtcrime.securesms.service.NotificationController;
|
||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.video.InMemoryTranscoder;
|
||||
import org.thoughtcrime.securesms.video.VideoSizeException;
|
||||
import org.thoughtcrime.securesms.video.VideoSourceException;
|
||||
import org.thoughtcrime.securesms.video.videoconverter.EncodingException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public final class AttachmentCompressionJob extends BaseJob {
|
||||
|
||||
public static final String KEY = "AttachmentCompressionJob";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = Log.tag(AttachmentCompressionJob.class);
|
||||
|
||||
private static final String KEY_ROW_ID = "row_id";
|
||||
private static final String KEY_UNIQUE_ID = "unique_id";
|
||||
private static final String KEY_MMS = "mms";
|
||||
private static final String KEY_MMS_SUBSCRIPTION_ID = "mms_subscription_id";
|
||||
|
||||
private final AttachmentId attachmentId;
|
||||
private final boolean mms;
|
||||
private final int mmsSubscriptionId;
|
||||
|
||||
public static AttachmentCompressionJob fromAttachment(@NonNull DatabaseAttachment databaseAttachment,
|
||||
boolean mms,
|
||||
int mmsSubscriptionId)
|
||||
{
|
||||
return new AttachmentCompressionJob(databaseAttachment.getAttachmentId(),
|
||||
MediaUtil.isVideo(databaseAttachment) && MediaConstraints.isVideoTranscodeAvailable(),
|
||||
mms,
|
||||
mmsSubscriptionId);
|
||||
}
|
||||
|
||||
private AttachmentCompressionJob(@NonNull AttachmentId attachmentId,
|
||||
boolean isVideoTranscode,
|
||||
boolean mms,
|
||||
int mmsSubscriptionId)
|
||||
{
|
||||
this(new Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.setQueue(isVideoTranscode ? "VIDEO_TRANSCODE" : null)
|
||||
.build(),
|
||||
attachmentId,
|
||||
mms,
|
||||
mmsSubscriptionId);
|
||||
}
|
||||
|
||||
private AttachmentCompressionJob(@NonNull Parameters parameters,
|
||||
@NonNull AttachmentId attachmentId,
|
||||
boolean mms,
|
||||
int mmsSubscriptionId)
|
||||
{
|
||||
super(parameters);
|
||||
this.attachmentId = attachmentId;
|
||||
this.mms = mms;
|
||||
this.mmsSubscriptionId = mmsSubscriptionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Data serialize() {
|
||||
return new Data.Builder().putLong(KEY_ROW_ID, attachmentId.getRowId())
|
||||
.putLong(KEY_UNIQUE_ID, attachmentId.getUniqueId())
|
||||
.putBoolean(KEY_MMS, mms)
|
||||
.putInt(KEY_MMS_SUBSCRIPTION_ID, mmsSubscriptionId)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull String getFactoryKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRun() throws Exception {
|
||||
AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context);
|
||||
DatabaseAttachment databaseAttachment = database.getAttachment(attachmentId);
|
||||
|
||||
if (databaseAttachment == null) {
|
||||
throw new IllegalStateException("Cannot find the specified attachment.");
|
||||
}
|
||||
|
||||
MediaConstraints mediaConstraints = mms ? MediaConstraints.getMmsMediaConstraints(mmsSubscriptionId)
|
||||
: MediaConstraints.getPushMediaConstraints();
|
||||
|
||||
scaleAndStripExif(database, mediaConstraints, databaseAttachment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCanceled() { }
|
||||
|
||||
@Override
|
||||
protected boolean onShouldRetry(@NonNull Exception exception) {
|
||||
return exception instanceof IOException;
|
||||
}
|
||||
|
||||
private void scaleAndStripExif(@NonNull AttachmentDatabase attachmentDatabase,
|
||||
@NonNull MediaConstraints constraints,
|
||||
@NonNull DatabaseAttachment attachment)
|
||||
throws UndeliverableMessageException
|
||||
{
|
||||
try {
|
||||
if (MediaUtil.isVideo(attachment) && MediaConstraints.isVideoTranscodeAvailable()) {
|
||||
transcodeVideoIfNeededToDatabase(context, attachmentDatabase, attachment, constraints, EventBus.getDefault());
|
||||
} else if (constraints.isSatisfied(context, attachment)) {
|
||||
if (MediaUtil.isJpeg(attachment)) {
|
||||
MediaStream stripped = getResizedMedia(context, attachment, constraints);
|
||||
attachmentDatabase.updateAttachmentData(attachment, stripped);
|
||||
}
|
||||
} else if (constraints.canResize(attachment)) {
|
||||
MediaStream resized = getResizedMedia(context, attachment, constraints);
|
||||
attachmentDatabase.updateAttachmentData(attachment, resized);
|
||||
} else {
|
||||
throw new UndeliverableMessageException("Size constraints could not be met!");
|
||||
}
|
||||
} catch (IOException | MmsException e) {
|
||||
throw new UndeliverableMessageException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(26)
|
||||
private static void transcodeVideoIfNeededToDatabase(@NonNull Context context,
|
||||
@NonNull AttachmentDatabase attachmentDatabase,
|
||||
@NonNull DatabaseAttachment attachment,
|
||||
@NonNull MediaConstraints constraints,
|
||||
@NonNull EventBus eventBus)
|
||||
throws UndeliverableMessageException
|
||||
{
|
||||
try (NotificationController notification = GenericForegroundService.startForegroundTask(context, context.getString(R.string.AttachmentUploadJob_compressing_video_start))) {
|
||||
|
||||
notification.setIndeterminateProgress();
|
||||
|
||||
try (MediaDataSource dataSource = attachmentDatabase.mediaDataSourceFor(attachment.getAttachmentId())) {
|
||||
|
||||
if (dataSource == null) {
|
||||
throw new UndeliverableMessageException("Cannot get media data source for attachment.");
|
||||
}
|
||||
|
||||
try (InMemoryTranscoder transcoder = new InMemoryTranscoder(context, dataSource, constraints.getCompressedVideoMaxSize(context))) {
|
||||
|
||||
if (transcoder.isTranscodeRequired()) {
|
||||
|
||||
MediaStream mediaStream = transcoder.transcode(percent -> {
|
||||
notification.setProgress(100, percent);
|
||||
eventBus.postSticky(new PartProgressEvent(attachment,
|
||||
PartProgressEvent.Type.COMPRESSION,
|
||||
100,
|
||||
percent));
|
||||
});
|
||||
|
||||
attachmentDatabase.updateAttachmentData(attachment, mediaStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (VideoSourceException | EncodingException e) {
|
||||
if (attachment.getSize() > constraints.getVideoMaxSize(context)) {
|
||||
throw new UndeliverableMessageException("Duration not found, attachment too large to skip transcode", e);
|
||||
} else {
|
||||
Log.w(TAG, "Problem with video source, but video small enough to skip transcode", e);
|
||||
}
|
||||
} catch (IOException | MmsException | VideoSizeException e) {
|
||||
throw new UndeliverableMessageException("Failed to transcode", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static MediaStream getResizedMedia(@NonNull Context context,
|
||||
@NonNull Attachment attachment,
|
||||
@NonNull MediaConstraints constraints)
|
||||
throws IOException
|
||||
{
|
||||
if (!constraints.canResize(attachment)) {
|
||||
throw new UnsupportedOperationException("Cannot resize this content type");
|
||||
}
|
||||
|
||||
try {
|
||||
BitmapUtil.ScaleResult scaleResult = BitmapUtil.createScaledBytes(context,
|
||||
new DecryptableStreamUriLoader.DecryptableUri(attachment.getDataUri()),
|
||||
constraints);
|
||||
|
||||
return new MediaStream(new ByteArrayInputStream(scaleResult.getBitmap()),
|
||||
MediaUtil.IMAGE_JPEG,
|
||||
scaleResult.getWidth(),
|
||||
scaleResult.getHeight());
|
||||
} catch (BitmapDecodingException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Factory implements Job.Factory<AttachmentCompressionJob> {
|
||||
@Override
|
||||
public @NonNull AttachmentCompressionJob create(@NonNull Parameters parameters, @NonNull Data data) {
|
||||
return new AttachmentCompressionJob(parameters,
|
||||
new AttachmentId(data.getLong(KEY_ROW_ID), data.getLong(KEY_UNIQUE_ID)),
|
||||
data.getBoolean(KEY_MMS),
|
||||
data.getInt(KEY_MMS_SUBSCRIPTION_ID));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -162,7 +162,7 @@ public class AttachmentDownloadJob extends BaseJob {
|
|||
|
||||
SignalServiceMessageReceiver messageReceiver = ApplicationDependencies.getSignalServiceMessageReceiver();
|
||||
SignalServiceAttachmentPointer pointer = createAttachmentPointer(attachment);
|
||||
InputStream stream = messageReceiver.retrieveAttachment(pointer, attachmentFile, MAX_ATTACHMENT_SIZE, (total, progress) -> EventBus.getDefault().postSticky(new PartProgressEvent(attachment, total, progress)));
|
||||
InputStream stream = messageReceiver.retrieveAttachment(pointer, attachmentFile, MAX_ATTACHMENT_SIZE, (total, progress) -> EventBus.getDefault().postSticky(new PartProgressEvent(attachment, PartProgressEvent.Type.NETWORK, total, progress)));
|
||||
|
||||
database.insertAttachmentsForPlaceholder(messageId, attachmentId, stream);
|
||||
} catch (InvalidPartException | NonSuccessfulResponseCodeException | InvalidMessageException | MmsException e) {
|
||||
|
|
|
@ -17,12 +17,9 @@ import org.thoughtcrime.securesms.jobmanager.Data;
|
|||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||
import org.thoughtcrime.securesms.service.GenericForegroundService;
|
||||
import org.thoughtcrime.securesms.service.NotificationController;
|
||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||
|
@ -32,11 +29,17 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class AttachmentUploadJob extends BaseJob {
|
||||
/**
|
||||
* Uploads an attachment without alteration.
|
||||
* <p>
|
||||
* Queue {@link AttachmentCompressionJob} before to compress.
|
||||
*/
|
||||
public final class AttachmentUploadJob extends BaseJob {
|
||||
|
||||
public static final String KEY = "AttachmentUploadJob";
|
||||
public static final String KEY = "AttachmentUploadJobV2";
|
||||
|
||||
private static final String TAG = AttachmentUploadJob.class.getSimpleName();
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = Log.tag(AttachmentUploadJob.class);
|
||||
|
||||
private static final String KEY_ROW_ID = "row_id";
|
||||
private static final String KEY_UNIQUE_ID = "unique_id";
|
||||
|
@ -46,26 +49,13 @@ public class AttachmentUploadJob extends BaseJob {
|
|||
*/
|
||||
private static final int FOREGROUND_LIMIT = 10 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* The {@link PartProgressEvent} on the {@link EventBus} is shared between transcoding and uploading.
|
||||
* <p>
|
||||
* This number is the ratio that represents the transcoding effort, after which it will hand
|
||||
* over to the to complete the progress.
|
||||
*/
|
||||
private static final double ENCODING_PROGRESS_RATIO = 0.75;
|
||||
private final AttachmentId attachmentId;
|
||||
|
||||
private final AttachmentId attachmentId;
|
||||
|
||||
public static AttachmentUploadJob fromAttachment(DatabaseAttachment databaseAttachment) {
|
||||
return new AttachmentUploadJob(databaseAttachment.getAttachmentId(), MediaUtil.isVideo(databaseAttachment) && MediaConstraints.isVideoTranscodeAvailable());
|
||||
}
|
||||
|
||||
private AttachmentUploadJob(AttachmentId attachmentId, boolean isVideoTranscode) {
|
||||
public AttachmentUploadJob(AttachmentId attachmentId) {
|
||||
this(new Job.Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.setQueue(isVideoTranscode ? "VIDEO_TRANSCODE" : null)
|
||||
.build(),
|
||||
attachmentId);
|
||||
}
|
||||
|
@ -97,13 +87,8 @@ public class AttachmentUploadJob extends BaseJob {
|
|||
throw new InvalidAttachmentException("Cannot find the specified attachment.");
|
||||
}
|
||||
|
||||
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
|
||||
Attachment scaledAttachment = scaleAndStripExif(database, mediaConstraints, databaseAttachment);
|
||||
boolean videoTranscodeOccurred = databaseAttachment != scaledAttachment && MediaUtil.isVideo(scaledAttachment);
|
||||
double progressStartPoint = videoTranscodeOccurred ? ENCODING_PROGRESS_RATIO : 0;
|
||||
|
||||
try (NotificationController notification = getNotificationForAttachment(scaledAttachment)) {
|
||||
SignalServiceAttachment localAttachment = getAttachmentFor(scaledAttachment, notification, progressStartPoint);
|
||||
try (NotificationController notification = getNotificationForAttachment(databaseAttachment)) {
|
||||
SignalServiceAttachment localAttachment = getAttachmentFor(databaseAttachment, notification);
|
||||
SignalServiceAttachmentPointer remoteAttachment = messageSender.uploadAttachment(localAttachment.asStream(), databaseAttachment.isSticker());
|
||||
Attachment attachment = PointerAttachment.forPointer(Optional.of(remoteAttachment), null, databaseAttachment.getFastPreflightId()).get();
|
||||
|
||||
|
@ -127,14 +112,7 @@ public class AttachmentUploadJob extends BaseJob {
|
|||
return exception instanceof IOException;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param progressStartPoint A value from 0..1 that represents any progress already shown.
|
||||
* The {@link PartProgressEvent} of this task will fit in the remaining
|
||||
* 1 - progressStartPoint.
|
||||
*/
|
||||
private @NonNull SignalServiceAttachment getAttachmentFor(Attachment attachment, @Nullable NotificationController notification, double progressStartPoint)
|
||||
throws InvalidAttachmentException
|
||||
{
|
||||
private @NonNull SignalServiceAttachment getAttachmentFor(Attachment attachment, @Nullable NotificationController notification) throws InvalidAttachmentException {
|
||||
try {
|
||||
if (attachment.getDataUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!");
|
||||
InputStream is = PartAuthority.getAttachmentStream(context, attachment.getDataUri());
|
||||
|
@ -148,8 +126,7 @@ public class AttachmentUploadJob extends BaseJob {
|
|||
.withHeight(attachment.getHeight())
|
||||
.withCaption(attachment.getCaption())
|
||||
.withListener((total, progress) -> {
|
||||
long cumulativeProgress = (long) ((1.0 - progressStartPoint) * progress + total * progressStartPoint);
|
||||
EventBus.getDefault().postSticky(new PartProgressEvent(attachment, total, cumulativeProgress));
|
||||
EventBus.getDefault().postSticky(new PartProgressEvent(attachment, PartProgressEvent.Type.NETWORK, total, progress));
|
||||
if (notification != null) {
|
||||
notification.setProgress(total, progress);
|
||||
}
|
||||
|
@ -160,25 +137,6 @@ public class AttachmentUploadJob extends BaseJob {
|
|||
}
|
||||
}
|
||||
|
||||
private Attachment scaleAndStripExif(@NonNull AttachmentDatabase attachmentDatabase,
|
||||
@NonNull MediaConstraints constraints,
|
||||
@NonNull DatabaseAttachment attachment)
|
||||
throws UndeliverableMessageException
|
||||
{
|
||||
MediaResizer mediaResizer = new MediaResizer(context, constraints);
|
||||
|
||||
MediaResizer.ProgressListener progressListener = (progress, total) -> {
|
||||
PartProgressEvent event = new PartProgressEvent(attachment,
|
||||
total,
|
||||
(long) (progress * ENCODING_PROGRESS_RATIO));
|
||||
EventBus.getDefault().postSticky(event);
|
||||
};
|
||||
|
||||
return mediaResizer.scaleAndStripExifToDatabase(attachmentDatabase,
|
||||
attachment,
|
||||
progressListener);
|
||||
}
|
||||
|
||||
private class InvalidAttachmentException extends Exception {
|
||||
InvalidAttachmentException(String message) {
|
||||
super(message);
|
||||
|
|
|
@ -29,6 +29,7 @@ public final class JobManagerFactories {
|
|||
put(AttachmentCopyJob.KEY, new AttachmentCopyJob.Factory());
|
||||
put(AttachmentDownloadJob.KEY, new AttachmentDownloadJob.Factory());
|
||||
put(AttachmentUploadJob.KEY, new AttachmentUploadJob.Factory());
|
||||
put(AttachmentCompressionJob.KEY, new AttachmentCompressionJob.Factory());
|
||||
put(AvatarDownloadJob.KEY, new AvatarDownloadJob.Factory());
|
||||
put(CleanPreKeysJob.KEY, new CleanPreKeysJob.Factory());
|
||||
put(CreateSignedPreKeyJob.KEY, new CreateSignedPreKeyJob.Factory());
|
||||
|
@ -82,6 +83,8 @@ public final class JobManagerFactories {
|
|||
|
||||
// Dead jobs
|
||||
put("PushContentReceiveJob", new FailingJob.Factory());
|
||||
put("AttachmentUploadJob", new FailingJob.Factory());
|
||||
put("MmsSendJob", new FailingJob.Factory());
|
||||
}};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,154 +0,0 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.MediaDataSource;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.mms.MediaStream;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.service.GenericForegroundService;
|
||||
import org.thoughtcrime.securesms.service.NotificationController;
|
||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.video.InMemoryTranscoder;
|
||||
import org.thoughtcrime.securesms.video.VideoSourceException;
|
||||
import org.thoughtcrime.securesms.video.VideoSizeException;
|
||||
import org.thoughtcrime.securesms.video.videoconverter.EncodingException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
final class MediaResizer {
|
||||
|
||||
private static final String TAG = Log.tag(MediaResizer.class);
|
||||
|
||||
@NonNull private final Context context;
|
||||
@NonNull private final MediaConstraints constraints;
|
||||
|
||||
MediaResizer(@NonNull Context context,
|
||||
@NonNull MediaConstraints constraints)
|
||||
{
|
||||
this.context = context;
|
||||
this.constraints = constraints;
|
||||
}
|
||||
|
||||
List<Attachment> scaleAndStripExifToDatabase(@NonNull AttachmentDatabase attachmentDatabase,
|
||||
@NonNull List<Attachment> attachments)
|
||||
throws UndeliverableMessageException
|
||||
{
|
||||
List<Attachment> results = new ArrayList<>(attachments.size());
|
||||
|
||||
for (Attachment attachment : attachments) {
|
||||
results.add(scaleAndStripExifToDatabase(attachmentDatabase, (DatabaseAttachment) attachment, null));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
DatabaseAttachment scaleAndStripExifToDatabase(@NonNull AttachmentDatabase attachmentDatabase,
|
||||
@NonNull DatabaseAttachment attachment,
|
||||
@Nullable ProgressListener transcodeProgressListener)
|
||||
throws UndeliverableMessageException
|
||||
{
|
||||
try {
|
||||
if (MediaUtil.isVideo(attachment) && MediaConstraints.isVideoTranscodeAvailable()) {
|
||||
return transcodeVideoIfNeededToDatabase(attachmentDatabase, attachment, transcodeProgressListener);
|
||||
} else if (constraints.isSatisfied(context, attachment)) {
|
||||
if (MediaUtil.isJpeg(attachment)) {
|
||||
MediaStream stripped = getResizedMedia(context, attachment);
|
||||
return attachmentDatabase.updateAttachmentData(attachment, stripped);
|
||||
} else {
|
||||
return attachment;
|
||||
}
|
||||
} else if (constraints.canResize(attachment)) {
|
||||
MediaStream resized = getResizedMedia(context, attachment);
|
||||
return attachmentDatabase.updateAttachmentData(attachment, resized);
|
||||
} else {
|
||||
throw new UndeliverableMessageException("Size constraints could not be met!");
|
||||
}
|
||||
} catch (IOException | MmsException e) {
|
||||
throw new UndeliverableMessageException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(26)
|
||||
private @NonNull DatabaseAttachment transcodeVideoIfNeededToDatabase(@NonNull AttachmentDatabase attachmentDatabase,
|
||||
@NonNull DatabaseAttachment attachment,
|
||||
@Nullable ProgressListener progressListener)
|
||||
throws UndeliverableMessageException
|
||||
{
|
||||
try (NotificationController notification = GenericForegroundService.startForegroundTask(context, context.getString(R.string.AttachmentUploadJob_compressing_video_start))) {
|
||||
|
||||
notification.setIndeterminateProgress();
|
||||
|
||||
try (MediaDataSource dataSource = attachmentDatabase.mediaDataSourceFor(attachment.getAttachmentId())) {
|
||||
|
||||
if (dataSource == null) {
|
||||
throw new UndeliverableMessageException("Cannot get media data source for attachment.");
|
||||
}
|
||||
|
||||
try (InMemoryTranscoder transcoder = new InMemoryTranscoder(context, dataSource, constraints.getCompressedVideoMaxSize(context))) {
|
||||
|
||||
if (transcoder.isTranscodeRequired()) {
|
||||
|
||||
MediaStream mediaStream = transcoder.transcode(percent -> {
|
||||
notification.setProgress(100, percent);
|
||||
|
||||
if (progressListener != null) {
|
||||
progressListener.onProgress(percent, 100);
|
||||
}
|
||||
});
|
||||
|
||||
return attachmentDatabase.updateAttachmentData(attachment, mediaStream);
|
||||
} else {
|
||||
return attachment;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (VideoSourceException | EncodingException e) {
|
||||
if (attachment.getSize() > constraints.getVideoMaxSize(context)) {
|
||||
throw new UndeliverableMessageException("Duration not found, attachment too large to skip transcode", e);
|
||||
} else {
|
||||
Log.w(TAG, "Duration not found, video small enough to skip transcode", e);
|
||||
return attachment;
|
||||
}
|
||||
} catch (IOException | MmsException | VideoSizeException e) {
|
||||
throw new UndeliverableMessageException("Failed to transcode", e);
|
||||
}
|
||||
}
|
||||
|
||||
private MediaStream getResizedMedia(@NonNull Context context, @NonNull Attachment attachment)
|
||||
throws IOException
|
||||
{
|
||||
if (!constraints.canResize(attachment)) {
|
||||
throw new UnsupportedOperationException("Cannot resize this content type");
|
||||
}
|
||||
|
||||
try {
|
||||
// XXX - This is loading everything into memory! We want the send path to be stream-like.
|
||||
BitmapUtil.ScaleResult scaleResult = BitmapUtil.createScaledBytes(context, new DecryptableStreamUriLoader.DecryptableUri(attachment.getDataUri()), constraints);
|
||||
return new MediaStream(new ByteArrayInputStream(scaleResult.getBitmap()), MediaUtil.IMAGE_JPEG, scaleResult.getWidth(), scaleResult.getHeight());
|
||||
} catch (BitmapDecodingException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public interface ProgressListener {
|
||||
|
||||
void onProgress(long progress, long total);
|
||||
}
|
||||
}
|
|
@ -1,16 +1,14 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.android.mms.dom.smil.parser.SmilXmlSerializer;
|
||||
import com.annimon.stream.Stream;
|
||||
import com.google.android.mms.ContentType;
|
||||
import com.google.android.mms.InvalidHeaderValueException;
|
||||
import com.google.android.mms.pdu_alt.CharacterSets;
|
||||
|
@ -25,11 +23,17 @@ import com.google.android.mms.smil.SmilHelper;
|
|||
import com.klinker.android.send_message.Utils;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.jobmanager.Data;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.CompatMmsConnection;
|
||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
|
@ -50,17 +54,17 @@ import java.io.IOException;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class MmsSendJob extends SendJob {
|
||||
public final class MmsSendJob extends SendJob {
|
||||
|
||||
public static final String KEY = "MmsSendJob";
|
||||
public static final String KEY = "MmsSendJobV2";
|
||||
|
||||
private static final String TAG = MmsSendJob.class.getSimpleName();
|
||||
|
||||
private static final String KEY_MESSAGE_ID = "message_id";
|
||||
|
||||
private long messageId;
|
||||
private final long messageId;
|
||||
|
||||
public MmsSendJob(long messageId) {
|
||||
private MmsSendJob(long messageId) {
|
||||
this(new Job.Parameters.Builder()
|
||||
.setQueue("mms-operation")
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
|
@ -69,6 +73,29 @@ public class MmsSendJob extends SendJob {
|
|||
messageId);
|
||||
}
|
||||
|
||||
/** Enqueues compression jobs for attachments and finally the MMS send job. */
|
||||
@WorkerThread
|
||||
public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId) {
|
||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||
OutgoingMediaMessage message;
|
||||
|
||||
try {
|
||||
message = database.getOutgoingMessage(messageId);
|
||||
} catch (MmsException | NoSuchMessageException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
List<Job> compressionJobs = Stream.of(message.getAttachments())
|
||||
.map(a -> (Job) AttachmentCompressionJob.fromAttachment((DatabaseAttachment) a, true, message.getSubscriptionId()))
|
||||
.toList();
|
||||
|
||||
MmsSendJob sendJob = new MmsSendJob(messageId);
|
||||
|
||||
jobManager.startChain(compressionJobs)
|
||||
.then(sendJob)
|
||||
.enqueue();
|
||||
}
|
||||
|
||||
private MmsSendJob(@NonNull Job.Parameters parameters, long messageId) {
|
||||
super(parameters);
|
||||
this.messageId = messageId;
|
||||
|
@ -196,7 +223,7 @@ public class MmsSendJob extends SendJob {
|
|||
String lineNumber = getMyNumber(context);
|
||||
Address destination = message.getRecipient().getAddress();
|
||||
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId());
|
||||
List<Attachment> scaledAttachments = scaleAndStripExifFromAttachments(mediaConstraints, message.getAttachments());
|
||||
List<Attachment> scaledAttachments = message.getAttachments();
|
||||
|
||||
if (!TextUtils.isEmpty(lineNumber)) {
|
||||
req.setFrom(new EncodedStringValue(lineNumber));
|
||||
|
|
|
@ -94,12 +94,15 @@ public class PushGroupSendJob extends PushSendJob {
|
|||
attachments.addAll(Stream.of(message.getLinkPreviews()).filter(p -> p.getThumbnail().isPresent()).map(p -> p.getThumbnail().get()).toList());
|
||||
attachments.addAll(Stream.of(message.getSharedContacts()).filter(c -> c.getAvatar() != null).map(c -> c.getAvatar().getAttachment()).withoutNulls().toList());
|
||||
|
||||
List<AttachmentUploadJob> attachmentJobs = Stream.of(attachments).map(a -> AttachmentUploadJob.fromAttachment((DatabaseAttachment) a)).toList();
|
||||
List<AttachmentCompressionJob> compressionJobs = Stream.of(attachments).map(a -> AttachmentCompressionJob.fromAttachment((DatabaseAttachment) a, false, -1)).toList();
|
||||
|
||||
List<AttachmentUploadJob> attachmentJobs = Stream.of(attachments).map(a -> new AttachmentUploadJob(((DatabaseAttachment) a).getAttachmentId())).toList();
|
||||
|
||||
if (attachmentJobs.isEmpty()) {
|
||||
jobManager.add(new PushGroupSendJob(messageId, destination, filterAddress));
|
||||
} else {
|
||||
jobManager.startChain(attachmentJobs)
|
||||
jobManager.startChain(compressionJobs)
|
||||
.then(attachmentJobs)
|
||||
.then(new PushGroupSendJob(messageId, destination, filterAddress))
|
||||
.enqueue();
|
||||
}
|
||||
|
|
|
@ -81,12 +81,15 @@ public class PushMediaSendJob extends PushSendJob {
|
|||
attachments.addAll(Stream.of(message.getLinkPreviews()).filter(p -> p.getThumbnail().isPresent()).map(p -> p.getThumbnail().get()).toList());
|
||||
attachments.addAll(Stream.of(message.getSharedContacts()).filter(c -> c.getAvatar() != null).map(c -> c.getAvatar().getAttachment()).withoutNulls().toList());
|
||||
|
||||
List<AttachmentUploadJob> attachmentJobs = Stream.of(attachments).map(a -> AttachmentUploadJob.fromAttachment((DatabaseAttachment) a)).toList();
|
||||
List<AttachmentCompressionJob> compressionJobs = Stream.of(attachments).map(a -> AttachmentCompressionJob.fromAttachment((DatabaseAttachment) a, false, -1)).toList();
|
||||
|
||||
List<AttachmentUploadJob> attachmentJobs = Stream.of(attachments).map(a -> new AttachmentUploadJob(((DatabaseAttachment) a).getAttachmentId())).toList();
|
||||
|
||||
if (attachmentJobs.isEmpty()) {
|
||||
jobManager.add(new PushMediaSendJob(messageId, destination));
|
||||
} else {
|
||||
jobManager.startChain(attachmentJobs)
|
||||
jobManager.startChain(compressionJobs)
|
||||
.then(attachmentJobs)
|
||||
.then(new PushMediaSendJob(messageId, destination))
|
||||
.enqueue();
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@ public abstract class PushSendJob extends SendJob {
|
|||
.withWidth(attachment.getWidth())
|
||||
.withHeight(attachment.getHeight())
|
||||
.withCaption(attachment.getCaption())
|
||||
.withListener((total, progress) -> EventBus.getDefault().postSticky(new PartProgressEvent(attachment, total, progress)))
|
||||
.withListener((total, progress) -> EventBus.getDefault().postSticky(new PartProgressEvent(attachment, PartProgressEvent.Type.NETWORK, total, progress)))
|
||||
.build();
|
||||
} catch (IOException ioe) {
|
||||
Log.w(TAG, "Couldn't open attachment", ioe);
|
||||
|
|
|
@ -46,14 +46,4 @@ public abstract class SendJob extends BaseJob {
|
|||
database.markAttachmentUploaded(messageId, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
protected List<Attachment> scaleAndStripExifFromAttachments(@NonNull MediaConstraints constraints,
|
||||
@NonNull List<Attachment> attachments)
|
||||
throws UndeliverableMessageException
|
||||
{
|
||||
MediaResizer mediaResizer = new MediaResizer(context, constraints);
|
||||
AttachmentDatabase attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context);
|
||||
|
||||
return mediaResizer.scaleAndStripExifToDatabase(attachmentDatabase, attachments);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,39 +16,39 @@
|
|||
*/
|
||||
package org.thoughtcrime.securesms.sms;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobs.AttachmentCopyJob;
|
||||
import org.thoughtcrime.securesms.jobs.AttachmentUploadJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
import org.thoughtcrime.securesms.attachments.AttachmentId;
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.database.Address;
|
||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||
import org.thoughtcrime.securesms.database.RecipientDatabase;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
||||
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||
import org.thoughtcrime.securesms.jobs.AttachmentCopyJob;
|
||||
import org.thoughtcrime.securesms.jobs.AttachmentCompressionJob;
|
||||
import org.thoughtcrime.securesms.jobs.AttachmentUploadJob;
|
||||
import org.thoughtcrime.securesms.jobs.MmsSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
||||
import org.thoughtcrime.securesms.jobs.SmsSendJob;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.mms.MmsException;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
|
||||
|
@ -171,14 +171,17 @@ public class MessageSender {
|
|||
mmsDatabase.endTransaction();
|
||||
}
|
||||
|
||||
List<AttachmentUploadJob> uploadJobs = new ArrayList<>(databaseAttachments.size());
|
||||
List<AttachmentCopyJob> copyJobs = new ArrayList<>(databaseAttachments.size());
|
||||
List<Job> messageJobs = new ArrayList<>(databaseAttachments.get(0).size());
|
||||
List<Job> compressionJobs = new ArrayList<>(databaseAttachments.size());
|
||||
List<Job> uploadJobs = new ArrayList<>(databaseAttachments.size());
|
||||
List<Job> copyJobs = new ArrayList<>(databaseAttachments.size());
|
||||
List<Job> messageJobs = new ArrayList<>(databaseAttachments.get(0).size());
|
||||
|
||||
for (List<DatabaseAttachment> attachmentList : databaseAttachments) {
|
||||
DatabaseAttachment source = attachmentList.get(0);
|
||||
|
||||
uploadJobs.add(AttachmentUploadJob.fromAttachment(source));
|
||||
compressionJobs.add(AttachmentCompressionJob.fromAttachment(source, false, -1));
|
||||
|
||||
uploadJobs.add(new AttachmentUploadJob(source.getAttachmentId()));
|
||||
|
||||
if (attachmentList.size() > 1) {
|
||||
AttachmentId sourceId = source.getAttachmentId();
|
||||
|
@ -209,7 +212,10 @@ public class MessageSender {
|
|||
copyJobs.size(),
|
||||
messageJobs.size()));
|
||||
|
||||
JobManager.Chain chain = ApplicationContext.getInstance(context).getJobManager().startChain(uploadJobs);
|
||||
JobManager.Chain chain = ApplicationContext.getInstance(context)
|
||||
.getJobManager()
|
||||
.startChain(compressionJobs)
|
||||
.then(uploadJobs);
|
||||
|
||||
if (copyJobs.size() > 0) {
|
||||
chain = chain.then(copyJobs);
|
||||
|
@ -289,7 +295,7 @@ public class MessageSender {
|
|||
|
||||
private static void sendMms(Context context, long messageId) {
|
||||
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
||||
jobManager.add(new MmsSendJob(messageId));
|
||||
MmsSendJob.enqueue(context, jobManager, messageId);
|
||||
}
|
||||
|
||||
private static boolean isPushTextSend(Context context, Recipient recipient, boolean keyExchange) {
|
||||
|
|
|
@ -116,7 +116,7 @@ public final class InMemoryTranscoder implements Closeable {
|
|||
|
||||
memoryFile = MemoryFileDescriptor.newMemoryFileDescriptor(context,
|
||||
"TRANSCODE",
|
||||
memoryFileEstimate);
|
||||
memoryFileEstimate);
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
final FileDescriptor memoryFileFileDescriptor = memoryFile.getFileDescriptor();
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package org.thoughtcrime.securesms.jobs;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public final class JobManagerFactoriesTest {
|
||||
|
||||
@Test
|
||||
public void PushContentReceiveJob_is_retired() {
|
||||
Map<String, Job.Factory> factories = JobManagerFactories.getJobFactories(mock(Application.class));
|
||||
|
||||
assertTrue(factories.get("PushContentReceiveJob") instanceof FailingJob.Factory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void AttachmentUploadJob_is_retired() {
|
||||
Map<String, Job.Factory> factories = JobManagerFactories.getJobFactories(mock(Application.class));
|
||||
|
||||
assertTrue(factories.get("AttachmentUploadJob") instanceof FailingJob.Factory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void MmsSendJob_is_retired() {
|
||||
Map<String, Job.Factory> factories = JobManagerFactories.getJobFactories(mock(Application.class));
|
||||
|
||||
assertTrue(factories.get("MmsSendJob") instanceof FailingJob.Factory);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue