mirror of
https://github.com/oxen-io/session-android.git
synced 2025-06-09 14:18:34 +00:00
Separate compression job.
This commit is contained in:
parent
7f0a7b0c13
commit
942154a61f
@ -2,14 +2,15 @@ package org.thoughtcrime.securesms.components;
|
|||||||
|
|
||||||
import android.animation.LayoutTransition;
|
import android.animation.LayoutTransition;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
import com.pnikosis.materialishprogress.ProgressWheel;
|
import com.pnikosis.materialishprogress.ProgressWheel;
|
||||||
|
|
||||||
@ -28,7 +29,14 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
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 List<Slide> slides;
|
||||||
@Nullable private View current;
|
@Nullable private View current;
|
||||||
@ -37,7 +45,8 @@ public class TransferControlView extends FrameLayout {
|
|||||||
private final View downloadDetails;
|
private final View downloadDetails;
|
||||||
private final TextView downloadDetailsText;
|
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) {
|
public TransferControlView(Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
@ -56,7 +65,9 @@ public class TransferControlView extends FrameLayout {
|
|||||||
setVisibility(GONE);
|
setVisibility(GONE);
|
||||||
setLayoutTransition(new LayoutTransition());
|
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.progressWheel = ViewUtil.findById(this, R.id.progress_wheel);
|
||||||
this.downloadDetails = ViewUtil.findById(this, R.id.download_details);
|
this.downloadDetails = ViewUtil.findById(this, R.id.download_details);
|
||||||
this.downloadDetailsText = ViewUtil.findById(this, R.id.download_details_text);
|
this.downloadDetailsText = ViewUtil.findById(this, R.id.download_details_text);
|
||||||
@ -98,19 +109,20 @@ public class TransferControlView extends FrameLayout {
|
|||||||
this.slides = slides;
|
this.slides = slides;
|
||||||
|
|
||||||
if (!isUpdateToExistingSet(slides)) {
|
if (!isUpdateToExistingSet(slides)) {
|
||||||
downloadProgress.clear();
|
networkProgress.clear();
|
||||||
Stream.of(slides).forEach(s -> downloadProgress.put(s.asAttachment(), 0f));
|
compresssionProgress.clear();
|
||||||
|
Stream.of(slides).forEach(s -> networkProgress.put(s.asAttachment(), 0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Slide slide : slides) {
|
for (Slide slide : slides) {
|
||||||
if (slide.asAttachment().getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE) {
|
if (slide.asAttachment().getTransferState() == AttachmentDatabase.TRANSFER_PROGRESS_DONE) {
|
||||||
downloadProgress.put(slide.asAttachment(), 1f);
|
networkProgress.put(slide.asAttachment(), 1f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (getTransferState(slides)) {
|
switch (getTransferState(slides)) {
|
||||||
case AttachmentDatabase.TRANSFER_PROGRESS_STARTED:
|
case AttachmentDatabase.TRANSFER_PROGRESS_STARTED:
|
||||||
showProgressSpinner(calculateProgress(downloadProgress));
|
showProgressSpinner(calculateProgress(networkProgress, compresssionProgress));
|
||||||
break;
|
break;
|
||||||
case AttachmentDatabase.TRANSFER_PROGRESS_PENDING:
|
case AttachmentDatabase.TRANSFER_PROGRESS_PENDING:
|
||||||
case AttachmentDatabase.TRANSFER_PROGRESS_FAILED:
|
case AttachmentDatabase.TRANSFER_PROGRESS_FAILED:
|
||||||
@ -124,7 +136,7 @@ public class TransferControlView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void showProgressSpinner() {
|
public void showProgressSpinner() {
|
||||||
showProgressSpinner(calculateProgress(downloadProgress));
|
showProgressSpinner(calculateProgress(networkProgress, compresssionProgress));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showProgressSpinner(float progress) {
|
public void showProgressSpinner(float progress) {
|
||||||
@ -158,12 +170,12 @@ public class TransferControlView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isUpdateToExistingSet(@NonNull List<Slide> slides) {
|
private boolean isUpdateToExistingSet(@NonNull List<Slide> slides) {
|
||||||
if (slides.size() != downloadProgress.size()) {
|
if (slides.size() != networkProgress.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Slide slide : slides) {
|
for (Slide slide : slides) {
|
||||||
if (!downloadProgress.containsKey(slide.asAttachment())) {
|
if (!networkProgress.containsKey(slide.asAttachment())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -207,19 +219,36 @@ public class TransferControlView extends FrameLayout {
|
|||||||
current = view;
|
current = view;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float calculateProgress(@NonNull Map<Attachment, Float> downloadProgress) {
|
private static float calculateProgress(@NonNull Map<Attachment, Float> uploadDownloadProgress, Map<Attachment, Float> compresssionProgress) {
|
||||||
float totalProgress = 0;
|
float totalDownloadProgress = 0;
|
||||||
for (float progress : downloadProgress.values()) {
|
float totalCompressionProgress = 0;
|
||||||
totalProgress += progress / downloadProgress.size();
|
|
||||||
|
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)
|
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||||
public void onEventAsync(final PartProgressEvent event) {
|
public void onEventAsync(final PartProgressEvent event) {
|
||||||
if (downloadProgress.containsKey(event.attachment)) {
|
if (networkProgress.containsKey(event.attachment)) {
|
||||||
downloadProgress.put(event.attachment, ((float) event.progress) / event.total);
|
float proportionCompleted = ((float) event.progress) / event.total;
|
||||||
progressWheel.setInstantProgress(calculateProgress(downloadProgress));
|
|
||||||
|
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.markAsOutbox(messageRecord.getId());
|
||||||
database.markAsForcedSms(messageRecord.getId());
|
database.markAsForcedSms(messageRecord.getId());
|
||||||
|
|
||||||
ApplicationContext.getInstance(context)
|
MmsSendJob.enqueue(context,
|
||||||
.getJobManager()
|
ApplicationContext.getInstance(context).getJobManager(),
|
||||||
.add(new MmsSendJob(messageRecord.getId()));
|
messageRecord.getId());
|
||||||
} else {
|
} else {
|
||||||
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
|
SmsDatabase database = DatabaseFactory.getSmsDatabase(context);
|
||||||
database.markAsInsecure(messageRecord.getId());
|
database.markAsInsecure(messageRecord.getId());
|
||||||
|
@ -497,8 +497,8 @@ public class AttachmentDatabase extends Database {
|
|||||||
return insertedAttachments;
|
return insertedAttachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull DatabaseAttachment updateAttachmentData(@NonNull DatabaseAttachment databaseAttachment,
|
public void updateAttachmentData(@NonNull DatabaseAttachment databaseAttachment,
|
||||||
@NonNull MediaStream mediaStream)
|
@NonNull MediaStream mediaStream)
|
||||||
throws MmsException
|
throws MmsException
|
||||||
{
|
{
|
||||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||||
@ -518,29 +518,8 @@ public class AttachmentDatabase extends Database {
|
|||||||
contentValues.put(DATA_RANDOM, dataInfo.random);
|
contentValues.put(DATA_RANDOM, dataInfo.random);
|
||||||
|
|
||||||
database.update(TABLE_NAME, contentValues, PART_ID_WHERE, databaseAttachment.getAttachmentId().toStrings());
|
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,
|
public void updateAttachmentFileName(@NonNull AttachmentId attachmentId,
|
||||||
@Nullable String fileName)
|
@Nullable String fileName)
|
||||||
{
|
{
|
||||||
|
@ -1,18 +1,24 @@
|
|||||||
package org.thoughtcrime.securesms.events;
|
package org.thoughtcrime.securesms.events;
|
||||||
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||||
|
|
||||||
public class PartProgressEvent {
|
public final class PartProgressEvent {
|
||||||
|
|
||||||
public final Attachment attachment;
|
public final Attachment attachment;
|
||||||
|
public final Type type;
|
||||||
public final long total;
|
public final long total;
|
||||||
public final long progress;
|
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.attachment = attachment;
|
||||||
|
this.type = type;
|
||||||
this.total = total;
|
this.total = total;
|
||||||
this.progress = progress;
|
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();
|
SignalServiceMessageReceiver messageReceiver = ApplicationDependencies.getSignalServiceMessageReceiver();
|
||||||
SignalServiceAttachmentPointer pointer = createAttachmentPointer(attachment);
|
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);
|
database.insertAttachmentsForPlaceholder(messageId, attachmentId, stream);
|
||||||
} catch (InvalidPartException | NonSuccessfulResponseCodeException | InvalidMessageException | MmsException e) {
|
} 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.Job;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||||
import org.thoughtcrime.securesms.logging.Log;
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
|
||||||
import org.thoughtcrime.securesms.mms.PartAuthority;
|
import org.thoughtcrime.securesms.mms.PartAuthority;
|
||||||
import org.thoughtcrime.securesms.service.GenericForegroundService;
|
import org.thoughtcrime.securesms.service.GenericForegroundService;
|
||||||
import org.thoughtcrime.securesms.service.NotificationController;
|
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.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||||
@ -32,11 +29,17 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.concurrent.TimeUnit;
|
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_ROW_ID = "row_id";
|
||||||
private static final String KEY_UNIQUE_ID = "unique_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;
|
private static final int FOREGROUND_LIMIT = 10 * 1024 * 1024;
|
||||||
|
|
||||||
/**
|
private final AttachmentId attachmentId;
|
||||||
* 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;
|
public AttachmentUploadJob(AttachmentId attachmentId) {
|
||||||
|
|
||||||
public static AttachmentUploadJob fromAttachment(DatabaseAttachment databaseAttachment) {
|
|
||||||
return new AttachmentUploadJob(databaseAttachment.getAttachmentId(), MediaUtil.isVideo(databaseAttachment) && MediaConstraints.isVideoTranscodeAvailable());
|
|
||||||
}
|
|
||||||
|
|
||||||
private AttachmentUploadJob(AttachmentId attachmentId, boolean isVideoTranscode) {
|
|
||||||
this(new Job.Parameters.Builder()
|
this(new Job.Parameters.Builder()
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
.addConstraint(NetworkConstraint.KEY)
|
||||||
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||||
.setMaxAttempts(Parameters.UNLIMITED)
|
.setMaxAttempts(Parameters.UNLIMITED)
|
||||||
.setQueue(isVideoTranscode ? "VIDEO_TRANSCODE" : null)
|
|
||||||
.build(),
|
.build(),
|
||||||
attachmentId);
|
attachmentId);
|
||||||
}
|
}
|
||||||
@ -97,13 +87,8 @@ public class AttachmentUploadJob extends BaseJob {
|
|||||||
throw new InvalidAttachmentException("Cannot find the specified attachment.");
|
throw new InvalidAttachmentException("Cannot find the specified attachment.");
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaConstraints mediaConstraints = MediaConstraints.getPushMediaConstraints();
|
try (NotificationController notification = getNotificationForAttachment(databaseAttachment)) {
|
||||||
Attachment scaledAttachment = scaleAndStripExif(database, mediaConstraints, databaseAttachment);
|
SignalServiceAttachment localAttachment = getAttachmentFor(databaseAttachment, notification);
|
||||||
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);
|
|
||||||
SignalServiceAttachmentPointer remoteAttachment = messageSender.uploadAttachment(localAttachment.asStream(), databaseAttachment.isSticker());
|
SignalServiceAttachmentPointer remoteAttachment = messageSender.uploadAttachment(localAttachment.asStream(), databaseAttachment.isSticker());
|
||||||
Attachment attachment = PointerAttachment.forPointer(Optional.of(remoteAttachment), null, databaseAttachment.getFastPreflightId()).get();
|
Attachment attachment = PointerAttachment.forPointer(Optional.of(remoteAttachment), null, databaseAttachment.getFastPreflightId()).get();
|
||||||
|
|
||||||
@ -127,14 +112,7 @@ public class AttachmentUploadJob extends BaseJob {
|
|||||||
return exception instanceof IOException;
|
return exception instanceof IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private @NonNull SignalServiceAttachment getAttachmentFor(Attachment attachment, @Nullable NotificationController notification) throws InvalidAttachmentException {
|
||||||
* @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
|
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
if (attachment.getDataUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!");
|
if (attachment.getDataUri() == null || attachment.getSize() == 0) throw new IOException("Assertion failed, outgoing attachment has no data!");
|
||||||
InputStream is = PartAuthority.getAttachmentStream(context, attachment.getDataUri());
|
InputStream is = PartAuthority.getAttachmentStream(context, attachment.getDataUri());
|
||||||
@ -148,8 +126,7 @@ public class AttachmentUploadJob extends BaseJob {
|
|||||||
.withHeight(attachment.getHeight())
|
.withHeight(attachment.getHeight())
|
||||||
.withCaption(attachment.getCaption())
|
.withCaption(attachment.getCaption())
|
||||||
.withListener((total, progress) -> {
|
.withListener((total, progress) -> {
|
||||||
long cumulativeProgress = (long) ((1.0 - progressStartPoint) * progress + total * progressStartPoint);
|
EventBus.getDefault().postSticky(new PartProgressEvent(attachment, PartProgressEvent.Type.NETWORK, total, progress));
|
||||||
EventBus.getDefault().postSticky(new PartProgressEvent(attachment, total, cumulativeProgress));
|
|
||||||
if (notification != null) {
|
if (notification != null) {
|
||||||
notification.setProgress(total, progress);
|
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 {
|
private class InvalidAttachmentException extends Exception {
|
||||||
InvalidAttachmentException(String message) {
|
InvalidAttachmentException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
|
@ -29,6 +29,7 @@ public final class JobManagerFactories {
|
|||||||
put(AttachmentCopyJob.KEY, new AttachmentCopyJob.Factory());
|
put(AttachmentCopyJob.KEY, new AttachmentCopyJob.Factory());
|
||||||
put(AttachmentDownloadJob.KEY, new AttachmentDownloadJob.Factory());
|
put(AttachmentDownloadJob.KEY, new AttachmentDownloadJob.Factory());
|
||||||
put(AttachmentUploadJob.KEY, new AttachmentUploadJob.Factory());
|
put(AttachmentUploadJob.KEY, new AttachmentUploadJob.Factory());
|
||||||
|
put(AttachmentCompressionJob.KEY, new AttachmentCompressionJob.Factory());
|
||||||
put(AvatarDownloadJob.KEY, new AvatarDownloadJob.Factory());
|
put(AvatarDownloadJob.KEY, new AvatarDownloadJob.Factory());
|
||||||
put(CleanPreKeysJob.KEY, new CleanPreKeysJob.Factory());
|
put(CleanPreKeysJob.KEY, new CleanPreKeysJob.Factory());
|
||||||
put(CreateSignedPreKeyJob.KEY, new CreateSignedPreKeyJob.Factory());
|
put(CreateSignedPreKeyJob.KEY, new CreateSignedPreKeyJob.Factory());
|
||||||
@ -82,6 +83,8 @@ public final class JobManagerFactories {
|
|||||||
|
|
||||||
// Dead jobs
|
// Dead jobs
|
||||||
put("PushContentReceiveJob", new FailingJob.Factory());
|
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;
|
package org.thoughtcrime.securesms.jobs;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import android.text.TextUtils;
|
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 android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
import com.android.mms.dom.smil.parser.SmilXmlSerializer;
|
import com.android.mms.dom.smil.parser.SmilXmlSerializer;
|
||||||
|
import com.annimon.stream.Stream;
|
||||||
import com.google.android.mms.ContentType;
|
import com.google.android.mms.ContentType;
|
||||||
import com.google.android.mms.InvalidHeaderValueException;
|
import com.google.android.mms.InvalidHeaderValueException;
|
||||||
import com.google.android.mms.pdu_alt.CharacterSets;
|
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 com.klinker.android.send_message.Utils;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||||
|
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||||
import org.thoughtcrime.securesms.database.Address;
|
import org.thoughtcrime.securesms.database.Address;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
import org.thoughtcrime.securesms.database.NoSuchMessageException;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
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.CompatMmsConnection;
|
||||||
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
import org.thoughtcrime.securesms.mms.MediaConstraints;
|
||||||
import org.thoughtcrime.securesms.mms.MmsException;
|
import org.thoughtcrime.securesms.mms.MmsException;
|
||||||
@ -50,17 +54,17 @@ import java.io.IOException;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
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 TAG = MmsSendJob.class.getSimpleName();
|
||||||
|
|
||||||
private static final String KEY_MESSAGE_ID = "message_id";
|
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()
|
this(new Job.Parameters.Builder()
|
||||||
.setQueue("mms-operation")
|
.setQueue("mms-operation")
|
||||||
.addConstraint(NetworkConstraint.KEY)
|
.addConstraint(NetworkConstraint.KEY)
|
||||||
@ -69,6 +73,29 @@ public class MmsSendJob extends SendJob {
|
|||||||
messageId);
|
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) {
|
private MmsSendJob(@NonNull Job.Parameters parameters, long messageId) {
|
||||||
super(parameters);
|
super(parameters);
|
||||||
this.messageId = messageId;
|
this.messageId = messageId;
|
||||||
@ -196,7 +223,7 @@ public class MmsSendJob extends SendJob {
|
|||||||
String lineNumber = getMyNumber(context);
|
String lineNumber = getMyNumber(context);
|
||||||
Address destination = message.getRecipient().getAddress();
|
Address destination = message.getRecipient().getAddress();
|
||||||
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId());
|
MediaConstraints mediaConstraints = MediaConstraints.getMmsMediaConstraints(message.getSubscriptionId());
|
||||||
List<Attachment> scaledAttachments = scaleAndStripExifFromAttachments(mediaConstraints, message.getAttachments());
|
List<Attachment> scaledAttachments = message.getAttachments();
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(lineNumber)) {
|
if (!TextUtils.isEmpty(lineNumber)) {
|
||||||
req.setFrom(new EncodedStringValue(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.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());
|
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()) {
|
if (attachmentJobs.isEmpty()) {
|
||||||
jobManager.add(new PushGroupSendJob(messageId, destination, filterAddress));
|
jobManager.add(new PushGroupSendJob(messageId, destination, filterAddress));
|
||||||
} else {
|
} else {
|
||||||
jobManager.startChain(attachmentJobs)
|
jobManager.startChain(compressionJobs)
|
||||||
|
.then(attachmentJobs)
|
||||||
.then(new PushGroupSendJob(messageId, destination, filterAddress))
|
.then(new PushGroupSendJob(messageId, destination, filterAddress))
|
||||||
.enqueue();
|
.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.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());
|
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()) {
|
if (attachmentJobs.isEmpty()) {
|
||||||
jobManager.add(new PushMediaSendJob(messageId, destination));
|
jobManager.add(new PushMediaSendJob(messageId, destination));
|
||||||
} else {
|
} else {
|
||||||
jobManager.startChain(attachmentJobs)
|
jobManager.startChain(compressionJobs)
|
||||||
|
.then(attachmentJobs)
|
||||||
.then(new PushMediaSendJob(messageId, destination))
|
.then(new PushMediaSendJob(messageId, destination))
|
||||||
.enqueue();
|
.enqueue();
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@ public abstract class PushSendJob extends SendJob {
|
|||||||
.withWidth(attachment.getWidth())
|
.withWidth(attachment.getWidth())
|
||||||
.withHeight(attachment.getHeight())
|
.withHeight(attachment.getHeight())
|
||||||
.withCaption(attachment.getCaption())
|
.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();
|
.build();
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
Log.w(TAG, "Couldn't open attachment", ioe);
|
Log.w(TAG, "Couldn't open attachment", ioe);
|
||||||
|
@ -46,14 +46,4 @@ public abstract class SendJob extends BaseJob {
|
|||||||
database.markAttachmentUploaded(messageId, attachment);
|
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;
|
package org.thoughtcrime.securesms.sms;
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
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.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
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.Address;
|
||||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId;
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
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.RecipientDatabase;
|
||||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.database.model.MessageRecord;
|
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.MmsSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
import org.thoughtcrime.securesms.jobs.PushGroupSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
import org.thoughtcrime.securesms.jobs.PushMediaSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
import org.thoughtcrime.securesms.jobs.PushTextSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.SmsSendJob;
|
import org.thoughtcrime.securesms.jobs.SmsSendJob;
|
||||||
|
import org.thoughtcrime.securesms.logging.Log;
|
||||||
import org.thoughtcrime.securesms.mms.MmsException;
|
import org.thoughtcrime.securesms.mms.MmsException;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
|
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
|
||||||
@ -171,14 +171,17 @@ public class MessageSender {
|
|||||||
mmsDatabase.endTransaction();
|
mmsDatabase.endTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AttachmentUploadJob> uploadJobs = new ArrayList<>(databaseAttachments.size());
|
List<Job> compressionJobs = new ArrayList<>(databaseAttachments.size());
|
||||||
List<AttachmentCopyJob> copyJobs = new ArrayList<>(databaseAttachments.size());
|
List<Job> uploadJobs = new ArrayList<>(databaseAttachments.size());
|
||||||
List<Job> messageJobs = new ArrayList<>(databaseAttachments.get(0).size());
|
List<Job> copyJobs = new ArrayList<>(databaseAttachments.size());
|
||||||
|
List<Job> messageJobs = new ArrayList<>(databaseAttachments.get(0).size());
|
||||||
|
|
||||||
for (List<DatabaseAttachment> attachmentList : databaseAttachments) {
|
for (List<DatabaseAttachment> attachmentList : databaseAttachments) {
|
||||||
DatabaseAttachment source = attachmentList.get(0);
|
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) {
|
if (attachmentList.size() > 1) {
|
||||||
AttachmentId sourceId = source.getAttachmentId();
|
AttachmentId sourceId = source.getAttachmentId();
|
||||||
@ -209,7 +212,10 @@ public class MessageSender {
|
|||||||
copyJobs.size(),
|
copyJobs.size(),
|
||||||
messageJobs.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) {
|
if (copyJobs.size() > 0) {
|
||||||
chain = chain.then(copyJobs);
|
chain = chain.then(copyJobs);
|
||||||
@ -289,7 +295,7 @@ public class MessageSender {
|
|||||||
|
|
||||||
private static void sendMms(Context context, long messageId) {
|
private static void sendMms(Context context, long messageId) {
|
||||||
JobManager jobManager = ApplicationContext.getInstance(context).getJobManager();
|
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) {
|
private static boolean isPushTextSend(Context context, Recipient recipient, boolean keyExchange) {
|
||||||
|
@ -116,7 +116,7 @@ public final class InMemoryTranscoder implements Closeable {
|
|||||||
|
|
||||||
memoryFile = MemoryFileDescriptor.newMemoryFileDescriptor(context,
|
memoryFile = MemoryFileDescriptor.newMemoryFileDescriptor(context,
|
||||||
"TRANSCODE",
|
"TRANSCODE",
|
||||||
memoryFileEstimate);
|
memoryFileEstimate);
|
||||||
final long startTime = System.currentTimeMillis();
|
final long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
final FileDescriptor memoryFileFileDescriptor = memoryFile.getFileDescriptor();
|
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…
x
Reference in New Issue
Block a user