From 2df9d4f27c97ac21ede6cf44c18f2c7cea4be342 Mon Sep 17 00:00:00 2001 From: Ryan ZHAO Date: Fri, 5 Mar 2021 11:24:43 +1100 Subject: [PATCH] clean --- .../attachments/DatabaseAttachmentProvider.kt | 1 - .../database/helpers/SQLCipherOpenHelper.java | 6 + .../SignalCommunicationModule.java | 12 +- .../securesms/jobs/JobManagerFactories.java | 5 - .../securesms/jobs/PushGroupSendJob.java | 298 ----------------- .../securesms/jobs/PushMediaSendJob.java | 308 ------------------ .../securesms/jobs/PushSendJob.java | 230 ------------- .../securesms/jobs/PushTextSendJob.java | 241 -------------- .../jobs/SendDeliveryReceiptJob.java | 2 +- .../thoughtcrime/securesms/jobs/SendJob.java | 63 ---- .../securesms/jobs/SendReadReceiptJob.java | 123 ------- .../securesms/jobs/TypingSendJob.java | 116 ------- 12 files changed, 8 insertions(+), 1397 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/SendJob.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/jobs/TypingSendJob.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt index 6a49452262..b7abb2d99a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt @@ -21,7 +21,6 @@ import org.thoughtcrime.securesms.database.Database import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper import org.thoughtcrime.securesms.events.PartProgressEvent -import org.thoughtcrime.securesms.jobs.PushSendJob import org.thoughtcrime.securesms.mms.MediaConstraints import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.transport.UndeliverableMessageException diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index a8dad3ce26..622410c7b7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -260,6 +260,12 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { if (oldVersion < lokiV22) { db.execSQL(SessionJobDatabase.getCreateSessionJobTableCommand()); + deleteJobRecords(db, + "PushGroupSendJob", + "PushMediaSendJob", + "PushTextSendJob", + "SendReadReceiptJob", + "TypingSendJob"); } db.setTransactionSuccessful(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java index ec48ac6594..dbebc3f22a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java @@ -11,14 +11,9 @@ import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob; import org.thoughtcrime.securesms.jobs.AttachmentUploadJob; import org.thoughtcrime.securesms.jobs.AvatarDownloadJob; import org.thoughtcrime.securesms.jobs.PushDecryptJob; -import org.thoughtcrime.securesms.jobs.PushGroupSendJob; -import org.thoughtcrime.securesms.jobs.PushMediaSendJob; -import org.thoughtcrime.securesms.jobs.PushTextSendJob; import org.thoughtcrime.securesms.jobs.RequestGroupInfoJob; import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob; import org.thoughtcrime.securesms.jobs.SendDeliveryReceiptJob; -import org.thoughtcrime.securesms.jobs.SendReadReceiptJob; -import org.thoughtcrime.securesms.jobs.TypingSendJob; import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; import org.thoughtcrime.securesms.loki.api.SessionProtocolImpl; import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment; @@ -27,17 +22,12 @@ import org.session.libsession.utilities.TextSecurePreferences; import dagger.Module; import dagger.Provides; -@Module(complete = false, injects = {PushGroupSendJob.class, - PushTextSendJob.class, - PushMediaSendJob.class, - AttachmentDownloadJob.class, +@Module(complete = false, injects = {AttachmentDownloadJob.class, RequestGroupInfoJob.class, AvatarDownloadJob.class, RetrieveProfileAvatarJob.class, - SendReadReceiptJob.class, AppProtectionPreferenceFragment.class, SendDeliveryReceiptJob.class, - TypingSendJob.class, AttachmentUploadJob.class, PushDecryptJob.class, LinkPreviewRepository.class}) diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index 26640d514d..2459507bb6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -37,15 +37,10 @@ public final class JobManagerFactories { put(LocalBackupJob.KEY, new LocalBackupJob.Factory()); put(PushContentReceiveJob.KEY, new PushContentReceiveJob.Factory()); put(PushDecryptJob.KEY, new PushDecryptJob.Factory()); - put(PushGroupSendJob.KEY, new PushGroupSendJob.Factory()); - put(PushMediaSendJob.KEY, new PushMediaSendJob.Factory()); - put(PushTextSendJob.KEY, new PushTextSendJob.Factory()); put(RequestGroupInfoJob.KEY, new RequestGroupInfoJob.Factory()); put(RetrieveProfileAvatarJob.KEY, new RetrieveProfileAvatarJob.Factory(application)); put(SendDeliveryReceiptJob.KEY, new SendDeliveryReceiptJob.Factory()); - put(SendReadReceiptJob.KEY, new SendReadReceiptJob.Factory()); put(TrimThreadJob.KEY, new TrimThreadJob.Factory()); - put(TypingSendJob.KEY, new TypingSendJob.Factory()); put(UpdateApkJob.KEY, new UpdateApkJob.Factory()); put(PrepareAttachmentAudioExtrasJob.KEY, new PrepareAttachmentAudioExtrasJob.Factory()); }}; diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java deleted file mode 100644 index ee266d43a9..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ /dev/null @@ -1,298 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.WorkerThread; - -import com.annimon.stream.Collectors; -import com.annimon.stream.Stream; - -import org.session.libsession.messaging.jobs.Data; -import org.session.libsession.messaging.sending_receiving.attachments.Attachment; -import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment; -import org.session.libsession.messaging.threads.recipients.Recipient; -import org.session.libsession.messaging.threads.Address; -import org.session.libsession.utilities.GroupUtil; - -import org.session.libsignal.service.api.crypto.UnidentifiedAccess; -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.NoSuchMessageException; -import org.session.libsession.database.documents.IdentityKeyMismatch; -import org.session.libsession.database.documents.NetworkFailure; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.JobManager; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.session.libsignal.utilities.logging.Log; -import org.thoughtcrime.securesms.loki.protocol.ClosedGroupsProtocolV2; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; -import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; -import org.thoughtcrime.securesms.transport.RetryLaterException; -import org.session.libsignal.libsignal.util.guava.Optional; -import org.session.libsignal.service.api.SignalServiceMessageSender; -import org.session.libsignal.service.api.messages.SendMessageResult; -import org.session.libsignal.service.api.messages.SignalServiceAttachment; -import org.session.libsignal.service.api.messages.SignalServiceDataMessage; -import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Preview; -import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Quote; -import org.session.libsignal.service.api.messages.SignalServiceGroup; -import org.session.libsignal.service.api.messages.shared.SharedContact; -import org.session.libsignal.service.api.push.SignalServiceAddress; -import org.session.libsignal.service.internal.push.SignalServiceProtos.GroupContext; - -import java.io.IOException; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class PushGroupSendJob extends PushSendJob implements InjectableType { - - public static final String KEY = "PushGroupSendJob"; - - private static final String TAG = PushGroupSendJob.class.getSimpleName(); - - @Inject SignalServiceMessageSender messageSender; - - private static final String KEY_MESSAGE_ID = "message_id"; - private static final String KEY_FILTER_ADDRESS = "filter_address"; - - private long messageId; - private String filterAddress; - - public PushGroupSendJob(long messageId, @NonNull Address destination, @Nullable Address filterAddress) { - this(new Job.Parameters.Builder() - .setQueue(destination.toGroupString()) - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build(), - messageId, filterAddress); - - } - - private PushGroupSendJob(@NonNull Job.Parameters parameters, long messageId, @Nullable Address filterAddress) { - super(parameters); - - this.messageId = messageId; - this.filterAddress = filterAddress == null ? null :filterAddress.toString(); - } - - @WorkerThread - public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Address destination, @Nullable Address filterAddress) { - try { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - OutgoingMediaMessage message = database.getOutgoingMessage(messageId); - List attachments = new LinkedList<>(); - - attachments.addAll(message.getAttachments()); - 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 attachmentJobs = Stream.of(attachments).map(a -> new AttachmentUploadJob(((DatabaseAttachment) a).getAttachmentId(), destination)).toList(); - - if (attachmentJobs.isEmpty()) { - jobManager.add(new PushGroupSendJob(messageId, destination, filterAddress)); - } else { - jobManager.startChain(attachmentJobs) - .then(new PushGroupSendJob(messageId, destination, filterAddress)) - .enqueue(); - } - - } catch (NoSuchMessageException | MmsException e) { - Log.w(TAG, "Failed to enqueue message.", e); - DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); - notifyMediaMessageDeliveryFailed(context, messageId); - } - } - - @Override - public @NonNull - Data serialize() { - return new Data.Builder().putLong(KEY_MESSAGE_ID, messageId) - .putString(KEY_FILTER_ADDRESS, filterAddress) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onAdded() { - DatabaseFactory.getMmsDatabase(context).markAsSending(messageId); - } - - @Override - public void onPushSend() - throws MmsException, NoSuchMessageException - { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - OutgoingMediaMessage message = database.getOutgoingMessage(messageId); - List existingNetworkFailures = message.getNetworkFailures(); - List existingIdentityMismatches = message.getIdentityKeyMismatches(); - - if (database.isSent(messageId)) { - log(TAG, "Message " + messageId + " was already sent. Ignoring."); - return; - } - - try { - log(TAG, "Sending message: " + messageId); - - List
targets; - - if (filterAddress != null) targets = Collections.singletonList(Address.fromSerialized(filterAddress)); - else if (!existingNetworkFailures.isEmpty()) targets = Stream.of(existingNetworkFailures).map(NetworkFailure::getAddress).toList(); - else targets = ClosedGroupsProtocolV2.getMessageDestinations(context, message.getRecipient().getAddress().toGroupString()); - - List results = deliver(message, targets); - List networkFailures = Stream.of(results).filter(SendMessageResult::isNetworkFailure).map(result -> new NetworkFailure(Address.fromSerialized(result.getAddress().getNumber()))).toList(); - List identityMismatches = Stream.of(results).filter(result -> result.getIdentityFailure() != null).map(result -> new IdentityKeyMismatch(Address.fromSerialized(result.getAddress().getNumber()), result.getIdentityFailure().getIdentityKey())).toList(); - Set
successAddresses = Stream.of(results).filter(result -> result.getSuccess() != null).map(result -> Address.fromSerialized(result.getAddress().getNumber())).collect(Collectors.toSet()); - List resolvedNetworkFailures = Stream.of(existingNetworkFailures).filter(failure -> successAddresses.contains(failure.getAddress())).toList(); - List resolvedIdentityFailures = Stream.of(existingIdentityMismatches).filter(failure -> successAddresses.contains(failure.getAddress())).toList(); - List successes = Stream.of(results).filter(result -> result.getSuccess() != null).toList(); - - for (NetworkFailure resolvedFailure : resolvedNetworkFailures) { - database.removeFailure(messageId, resolvedFailure); - existingNetworkFailures.remove(resolvedFailure); - } - - for (IdentityKeyMismatch resolvedIdentity : resolvedIdentityFailures) { - database.removeMismatchedIdentity(messageId, resolvedIdentity.getAddress(), resolvedIdentity.getIdentityKey()); - existingIdentityMismatches.remove(resolvedIdentity); - } - - if (!networkFailures.isEmpty()) { - database.addFailures(messageId, networkFailures); - } - - for (IdentityKeyMismatch mismatch : identityMismatches) { - database.addMismatchedIdentity(messageId, mismatch.getAddress(), mismatch.getIdentityKey()); - } - - for (SendMessageResult success : successes) { - DatabaseFactory.getGroupReceiptDatabase(context).setUnidentified(Address.fromSerialized(success.getAddress().getNumber()), - messageId, - success.getSuccess().isUnidentified()); - } - - if (existingNetworkFailures.isEmpty() && networkFailures.isEmpty() && identityMismatches.isEmpty() && existingIdentityMismatches.isEmpty()) { - database.markAsSent(messageId, true); - - markAttachmentsUploaded(messageId, message.getAttachments()); - - if (message.getExpiresIn() > 0 && !message.isExpirationUpdate()) { - database.markExpireStarted(messageId); - ApplicationContext.getInstance(context) - .getExpiringMessageManager() - .scheduleDeletion(messageId, true, message.getExpiresIn()); - } - } else if (!networkFailures.isEmpty()) { - throw new RetryLaterException(); - } else if (!identityMismatches.isEmpty()) { - database.markAsSentFailed(messageId); - notifyMediaMessageDeliveryFailed(context, messageId); - } - } catch (Exception e) { - warn(TAG, e); - database.markAsSentFailed(messageId); - notifyMediaMessageDeliveryFailed(context, messageId); - } - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - if (exception instanceof IOException) return true; - // Loki - Disable since we have our own retrying - return false; - } - - @Override - public void onCanceled() { - DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); - } - - private List deliver(OutgoingMediaMessage message, @NonNull List
destinations) - throws IOException { - - Address address = message.getRecipient().getAddress(); - - List addresses = Stream.of(destinations).map(this::getPushAddress).toList(); - List> unidentifiedAccess = Stream.of(addresses) - .map(a -> Address.fromSerialized(a.getNumber())) - .map(a -> Recipient.from(context, a, false)) - .map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)) - .toList(); - - if (message.isGroup() && address.isClosedGroup()) { - SignalServiceGroup.GroupType groupType = address.isOpenGroup() ? SignalServiceGroup.GroupType.PUBLIC_CHAT : SignalServiceGroup.GroupType.SIGNAL; - String groupId = address.toGroupString(); - List attachments = Stream.of(message.getAttachments()).toList(); - List attachmentPointers = getAttachmentPointersFor(attachments); - // Loki - Only send GroupUpdate or GroupQuit messages to closed groups - OutgoingGroupMediaMessage groupMessage = (OutgoingGroupMediaMessage) message; - GroupContext groupContext = groupMessage.getGroupContext(); - SignalServiceAttachment avatar = attachmentPointers.isEmpty() ? null : attachmentPointers.get(0); - SignalServiceGroup.Type type = groupMessage.isGroupQuit() ? SignalServiceGroup.Type.QUIT : SignalServiceGroup.Type.UPDATE; - SignalServiceGroup group = new SignalServiceGroup(type, GroupUtil.getDecodedGroupIDAsData(groupId), groupType, groupContext.getName(), groupContext.getMembersList(), avatar, groupContext.getAdminsList()); - SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder() - .withTimestamp(message.getSentTimeMillis()) - .withExpiration(message.getRecipient().getExpireMessages()) - .asGroupMessage(group) - .build(); - - return messageSender.sendMessage(messageId, addresses, unidentifiedAccess, groupDataMessage); - } else { - SignalServiceDataMessage groupMessage = getDataMessage(address, message).build(); - - return messageSender.sendMessage(messageId, addresses, unidentifiedAccess, groupMessage); - } - } - - public SignalServiceDataMessage.Builder getDataMessage(Address address, OutgoingMediaMessage message) { - - SignalServiceGroup.GroupType groupType = address.isOpenGroup() ? SignalServiceGroup.GroupType.PUBLIC_CHAT : SignalServiceGroup.GroupType.SIGNAL; - - String groupId = address.toGroupString(); - Optional profileKey = getProfileKey(message.getRecipient()); - Optional quote = getQuoteFor(message); - List sharedContacts = getSharedContactsFor(message); - List previews = getPreviewsFor(message); - List attachments = Stream.of(message.getAttachments()).toList(); - List attachmentPointers = getAttachmentPointersFor(attachments); - - SignalServiceGroup group = new SignalServiceGroup(GroupUtil.getDecodedGroupIDAsData(groupId), groupType); - return SignalServiceDataMessage.newBuilder() - .withTimestamp(message.getSentTimeMillis()) - .asGroupMessage(group) - .withAttachments(attachmentPointers) - .withBody(message.getBody()) - .withExpiration((int)(message.getExpiresIn() / 1000)) - .asExpirationUpdate(message.isExpirationUpdate()) - .withProfileKey(profileKey.orNull()) - .withQuote(quote.orNull()) - .withSharedContacts(sharedContacts) - .withPreviews(previews); - } - - public static class Factory implements Job.Factory { - @Override - public @NonNull PushGroupSendJob create(@NonNull Parameters parameters, @NonNull Data data) { - String address = data.getString(KEY_FILTER_ADDRESS); - Address filter = address != null ? Address.fromSerialized(data.getString(KEY_FILTER_ADDRESS)) : null; - - return new PushGroupSendJob(parameters, data.getLong(KEY_MESSAGE_ID), filter); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java deleted file mode 100644 index 08cc05da9f..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushMediaSendJob.java +++ /dev/null @@ -1,308 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.content.Context; -import androidx.annotation.NonNull; -import androidx.annotation.WorkerThread; - -import com.annimon.stream.Stream; - -import org.session.libsession.messaging.jobs.Data; -import org.session.libsession.messaging.threads.Address; -import org.session.libsession.messaging.sending_receiving.attachments.Attachment; -import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment; -import org.session.libsession.messaging.threads.recipients.Recipient.UnidentifiedAccessMode; -import org.session.libsession.utilities.TextSecurePreferences; - -import org.session.libsignal.service.api.crypto.UnidentifiedAccess; -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.NoSuchMessageException; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.JobManager; -import org.session.libsignal.utilities.logging.Log; -import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; -import org.session.libsession.messaging.threads.recipients.Recipient; -import org.thoughtcrime.securesms.service.ExpiringMessageManager; -import org.thoughtcrime.securesms.transport.RetryLaterException; -import org.thoughtcrime.securesms.transport.UndeliverableMessageException; -import org.session.libsignal.libsignal.util.guava.Optional; -import org.session.libsignal.service.api.SignalServiceMessageSender; -import org.session.libsignal.service.api.messages.SendMessageResult; -import org.session.libsignal.service.api.messages.SignalServiceAttachment; -import org.session.libsignal.service.api.messages.SignalServiceDataMessage; -import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Preview; -import org.session.libsignal.service.api.messages.shared.SharedContact; -import org.session.libsignal.service.api.push.SignalServiceAddress; -import org.session.libsignal.service.loki.api.SnodeAPI; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -import javax.inject.Inject; - -public class PushMediaSendJob extends PushSendJob implements InjectableType { - - public static final String KEY = "PushMediaSendJob"; - - private static final String TAG = PushMediaSendJob.class.getSimpleName(); - - private static final String KEY_TEMPLATE_MESSAGE_ID = "template_message_id"; - private static final String KEY_MESSAGE_ID = "message_id"; - private static final String KEY_DESTINATION = "destination"; - - @Inject SignalServiceMessageSender messageSender; - - private long messageId; - private long templateMessageId; - private Address destination; - - public PushMediaSendJob(long templateMessageId, long messageId, Address destination) { - this(constructParameters(destination), templateMessageId, messageId, destination); - } - - private PushMediaSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination) { - super(parameters); - this.templateMessageId = templateMessageId; - this.messageId = messageId; - this.destination = destination; - } - - public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long messageId, @NonNull Address destination) { - enqueue(context, jobManager, messageId, messageId, destination); - } - - public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, long templateMessageId, long messageId, @NonNull Address destination) { - enqueue(context, jobManager, Collections.singletonList(new PushMediaSendJob(templateMessageId, messageId, destination))); - } - - @WorkerThread - public static void enqueue(@NonNull Context context, @NonNull JobManager jobManager, List jobs) { - if (jobs.size() == 0) { return; } - PushMediaSendJob first = jobs.get(0); - long messageId = first.templateMessageId; - try { - List attachmentJobs = getAttachmentUploadJobs(context, messageId, first.destination); - - if (attachmentJobs.isEmpty()) { - for (PushMediaSendJob job : jobs) { jobManager.add(job); } - } else { - jobManager.startChain(attachmentJobs) - .then((List)(List)jobs) - .enqueue(); - } - } catch (NoSuchMessageException | MmsException e) { - Log.w(TAG, "Failed to enqueue message.", e); - DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); - notifyMediaMessageDeliveryFailed(context, messageId); - } - } - - public static List getAttachmentUploadJobs(@NonNull Context context, long messageId, @NonNull Address destination) - throws NoSuchMessageException, MmsException - { - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - OutgoingMediaMessage message = database.getOutgoingMessage(messageId); - List attachments = new LinkedList<>(); - - attachments.addAll(message.getAttachments()); - 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()); - - return Stream.of(attachments).map(a -> new AttachmentUploadJob(((DatabaseAttachment) a).getAttachmentId(), destination)).toList(); - } - - @Override - public @NonNull - Data serialize() { - return new Data.Builder() - .putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId) - .putLong(KEY_MESSAGE_ID, messageId) - .putString(KEY_DESTINATION, destination.serialize()).build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onAdded() { - DatabaseFactory.getMmsDatabase(context).markAsSending(messageId); - } - - @Override - public void onPushSend() - throws RetryLaterException, MmsException, NoSuchMessageException, - UndeliverableMessageException - { - ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager(); - MmsDatabase database = DatabaseFactory.getMmsDatabase(context); - OutgoingMediaMessage message = database.getOutgoingMessage(templateMessageId); - - if (messageId >= 0 && database.isSent(messageId)) { - warn(TAG, "Message " + messageId + " was already sent. Ignoring."); - return; - } - - try { - log(TAG, "Sending message: " + messageId); - - Recipient recipient = Recipient.from(context, destination, false); - byte[] profileKey = recipient.getProfileKey(); - UnidentifiedAccessMode accessMode = recipient.getUnidentifiedAccessMode(); - - boolean unidentified = deliver(message); - - if (messageId >= 0) { - database.markAsSent(messageId, true); - markAttachmentsUploaded(messageId, message.getAttachments()); - database.markUnidentified(messageId, unidentified); - } - - if (recipient.isLocalNumber()) { - SyncMessageId id = new SyncMessageId(recipient.getAddress(), message.getSentTimeMillis()); - DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis()); - DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis()); - } - - if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) { - log(TAG, "Marking recipient as UD-unrestricted following a UD send."); - DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED); - } else if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN) { - log(TAG, "Marking recipient as UD-enabled following a UD send."); - DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.ENABLED); - } else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) { - log(TAG, "Marking recipient as UD-disabled following a non-UD send."); - DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED); - } - - if (messageId > 0 && message.getExpiresIn() > 0 && !message.isExpirationUpdate()) { - database.markExpireStarted(messageId); - expirationManager.scheduleDeletion(messageId, true, message.getExpiresIn()); - } - - log(TAG, "Sent message: " + messageId); - - } catch (SnodeAPI.Error e) { - Log.d("Loki", "Couldn't send message due to error: " + e.getDescription()); - if (messageId >= 0) { - LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context); - lokiMessageDatabase.setErrorMessage(messageId, e.getDescription()); - database.markAsSentFailed(messageId); - } - } - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - // Loki - Disable since we have our own retrying - return false; - } - - @Override - public void onCanceled() { - if (messageId >= 0) { - DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); - notifyMediaMessageDeliveryFailed(context, messageId); - } - } - - private boolean deliver(OutgoingMediaMessage message) - throws RetryLaterException, UndeliverableMessageException, SnodeAPI.Error - { - try { - Recipient recipient = Recipient.from(context, destination, false); - String userPublicKey = TextSecurePreferences.getLocalNumber(context); - SignalServiceAddress address = getPushAddress(recipient.getAddress()); - SignalServiceAddress localAddress = new SignalServiceAddress(userPublicKey); - List attachments = Stream.of(message.getAttachments()).toList(); - List serviceAttachments = getAttachmentPointersFor(attachments); - Optional profileKey = getProfileKey(message.getRecipient()); - Optional quote = getQuoteFor(message); - List sharedContacts = getSharedContactsFor(message); - List previews = getPreviewsFor(message); - - Optional unidentifiedAccessPair = UnidentifiedAccessUtil.getAccessFor(context, recipient); - - SignalServiceDataMessage mediaMessage = SignalServiceDataMessage.newBuilder() - .withBody(message.getBody()) - .withAttachments(serviceAttachments) - .withTimestamp(message.getSentTimeMillis()) - .withExpiration((int)(message.getExpiresIn() / 1000)) - .withProfileKey(profileKey.orNull()) - .withQuote(quote.orNull()) - .withSharedContacts(sharedContacts) - .withPreviews(previews) - .asExpirationUpdate(message.isExpirationUpdate()) - .build(); - - SignalServiceDataMessage mediaSelfSendMessage = SignalServiceDataMessage.newBuilder() - .withBody(message.getBody()) - .withAttachments(serviceAttachments) - .withTimestamp(message.getSentTimeMillis()) - .withSyncTarget(destination.serialize()) - .withExpiration((int)(message.getExpiresIn() / 1000)) - .withProfileKey(profileKey.orNull()) - .withQuote(quote.orNull()) - .withSharedContacts(sharedContacts) - .withPreviews(previews) - .asExpirationUpdate(message.isExpirationUpdate()) - .build(); - - if (userPublicKey == address.getNumber()) { - // Loki - Device link messages don't go through here - SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccessPair, mediaMessage, true); - if (result.getLokiAPIError() != null) { - throw result.getLokiAPIError(); - } else { - return result.getSuccess().isUnidentified(); - } - } else { - SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccessPair, mediaMessage, false); - if (result.getLokiAPIError() != null) { - throw result.getLokiAPIError(); - } else { - boolean isUnidentified = result.getSuccess().isUnidentified(); - - try { - // send to ourselves to sync multi-device - Optional syncAccess = UnidentifiedAccessUtil.getAccessForSync(context); - SendMessageResult selfSendResult = messageSender.sendMessage(messageId, localAddress, syncAccess, mediaSelfSendMessage, true); - if (selfSendResult.getLokiAPIError() != null) { - throw selfSendResult.getLokiAPIError(); - } - } catch (Exception e) { - Log.e("Loki", "Error sending message to ourselves", e); - } - - return isUnidentified; - } - } - } catch (FileNotFoundException e) { - warn(TAG, e); - throw new UndeliverableMessageException(e); - } catch (IOException e) { - warn(TAG, e); - throw new RetryLaterException(e); - } - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull PushMediaSendJob create(@NonNull Parameters parameters, @NonNull Data data) { - long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID); - long messageID = data.getLong(KEY_MESSAGE_ID); - Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION)); - return new PushMediaSendJob(parameters, templateMessageID, messageID, destination); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java deleted file mode 100644 index d99ab36c55..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ /dev/null @@ -1,230 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import android.content.Context; -import android.graphics.Bitmap; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import android.text.TextUtils; - -import com.annimon.stream.Stream; - -import org.session.libsession.messaging.sending_receiving.attachments.Attachment; -import org.session.libsession.messaging.sending_receiving.sharecontacts.Contact; -import org.session.libsession.utilities.MediaTypes; -import org.session.libsignal.utilities.Base64; -import org.session.libsession.utilities.Util; - -import org.greenrobot.eventbus.EventBus; -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.contactshare.ContactModelMapper; -import org.session.libsession.utilities.preferences.ProfileKeyUtil; -import org.session.libsession.messaging.threads.Address; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.events.PartProgressEvent; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.session.libsignal.utilities.logging.Log; -import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader; -import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; -import org.thoughtcrime.securesms.mms.PartAuthority; -import org.session.libsession.messaging.threads.recipients.Recipient; -import org.thoughtcrime.securesms.util.BitmapDecodingException; -import org.thoughtcrime.securesms.util.BitmapUtil; -import org.session.libsignal.utilities.Hex; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.session.libsignal.libsignal.util.guava.Optional; -import org.session.libsignal.service.api.messages.SignalServiceAttachment; -import org.session.libsignal.service.api.messages.SignalServiceAttachmentPointer; -import org.session.libsignal.service.api.messages.SignalServiceDataMessage; -import org.session.libsignal.service.api.messages.SignalServiceDataMessage.Preview; -import org.session.libsignal.service.api.messages.shared.SharedContact; -import org.session.libsignal.service.api.push.SignalServiceAddress; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -public abstract class PushSendJob extends SendJob { - - private static final String TAG = PushSendJob.class.getSimpleName(); - - protected PushSendJob(Job.Parameters parameters) { - super(parameters); - } - - protected static Job.Parameters constructParameters(Address destination) { - return new Parameters.Builder() - .setQueue(destination.serialize()) - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(1) - .build(); - } - - @Override - protected final void onSend() throws Exception { - onPushSend(); - } - - @Override - public void onRetry() { - super.onRetry(); - Log.i(TAG, "onRetry()"); - } - - protected Optional getProfileKey(@NonNull Recipient recipient) { - if (!recipient.resolve().isSystemContact() && !recipient.resolve().isProfileSharing()) { - return Optional.absent(); - } - - return Optional.of(ProfileKeyUtil.getProfileKey(context)); - } - - protected SignalServiceAddress getPushAddress(Address address) { - String relay = null; - return new SignalServiceAddress(address.toString(), Optional.fromNullable(relay)); - } - - protected SignalServiceAttachment getAttachmentFor(Attachment attachment) { - 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()); - return SignalServiceAttachment.newStreamBuilder() - .withStream(is) - .withContentType(attachment.getContentType()) - .withLength(attachment.getSize()) - .withFileName(attachment.getFileName()) - .withVoiceNote(attachment.isVoiceNote()) - .withWidth(attachment.getWidth()) - .withHeight(attachment.getHeight()) - .withCaption(attachment.getCaption()) - .withListener((total, progress) -> EventBus.getDefault().postSticky(new PartProgressEvent(attachment, total, progress))) - .build(); - } catch (IOException ioe) { - Log.w(TAG, "Couldn't open attachment", ioe); - } - return null; - } - - protected @NonNull List getAttachmentPointersFor(List attachments) { - return Stream.of(attachments).map(this::getAttachmentPointerFor).filter(a -> a != null).toList(); - } - - protected @Nullable SignalServiceAttachment getAttachmentPointerFor(Attachment attachment) { - if (TextUtils.isEmpty(attachment.getLocation())) { - Log.w(TAG, "empty content id"); - return null; - } - - if (TextUtils.isEmpty(attachment.getKey())) { - Log.w(TAG, "empty encrypted key"); - return null; - } - - try { - long id = Long.parseLong(attachment.getLocation()); - byte[] key = Base64.decode(attachment.getKey()); - - return new SignalServiceAttachmentPointer(id, - attachment.getContentType(), - key, - Optional.of(Util.toIntExact(attachment.getSize())), - Optional.absent(), - attachment.getWidth(), - attachment.getHeight(), - Optional.fromNullable(attachment.getDigest()), - Optional.fromNullable(attachment.getFileName()), - attachment.isVoiceNote(), - Optional.fromNullable(attachment.getCaption()), attachment.getUrl()); - } catch (IOException | ArithmeticException e) { - Log.w(TAG, e); - return null; - } - } - - protected static void notifyMediaMessageDeliveryFailed(Context context, long messageId) { - long threadId = DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId); - Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); - - if (threadId != -1 && recipient != null) { - ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); - } - } - - protected Optional getQuoteFor(OutgoingMediaMessage message) { - if (message.getOutgoingQuote() == null) return Optional.absent(); - - long quoteId = message.getOutgoingQuote().getId(); - String quoteBody = message.getOutgoingQuote().getText(); - Address quoteAuthor = message.getOutgoingQuote().getAuthor(); - List quoteAttachments = new LinkedList<>(); - - for (Attachment attachment : message.getOutgoingQuote().getAttachments()) { - BitmapUtil.ScaleResult thumbnailData = null; - SignalServiceAttachment thumbnail = null; - String thumbnailType = MediaTypes.IMAGE_JPEG; - - try { - if (MediaUtil.isImageType(attachment.getContentType()) && attachment.getDataUri() != null) { - Bitmap.CompressFormat format = BitmapUtil.getCompressFormatForContentType(attachment.getContentType()); - - thumbnailData = BitmapUtil.createScaledBytes(context, new DecryptableStreamUriLoader.DecryptableUri(attachment.getDataUri()), 100, 100, 500 * 1024, format); - thumbnailType = attachment.getContentType(); - } else if (MediaUtil.isVideoType(attachment.getContentType()) && attachment.getThumbnailUri() != null) { - thumbnailData = BitmapUtil.createScaledBytes(context, new DecryptableStreamUriLoader.DecryptableUri(attachment.getThumbnailUri()), 100, 100, 500 * 1024); - } - - if (thumbnailData != null) { - thumbnail = SignalServiceAttachment.newStreamBuilder() - .withContentType(thumbnailType) - .withWidth(thumbnailData.getWidth()) - .withHeight(thumbnailData.getHeight()) - .withLength(thumbnailData.getBitmap().length) - .withStream(new ByteArrayInputStream(thumbnailData.getBitmap())) - .build(); - } - - quoteAttachments.add(new SignalServiceDataMessage.Quote.QuotedAttachment(attachment.getContentType(), - attachment.getFileName(), - thumbnail)); - } catch (BitmapDecodingException e) { - Log.w(TAG, e); - } - } - - return Optional.of(new SignalServiceDataMessage.Quote(quoteId, new SignalServiceAddress(quoteAuthor.serialize()), quoteBody, quoteAttachments)); - } - - List getSharedContactsFor(OutgoingMediaMessage mediaMessage) { - List sharedContacts = new LinkedList<>(); - - for (Contact contact : mediaMessage.getSharedContacts()) { - SharedContact.Builder builder = ContactModelMapper.localToRemoteBuilder(contact); - SharedContact.Avatar avatar = null; - - if (contact.getAvatar() != null && contact.getAvatar().getAttachment() != null) { - avatar = SharedContact.Avatar.newBuilder().withAttachment(getAttachmentFor(contact.getAvatarAttachment())) - .withProfileFlag(contact.getAvatar().isProfile()) - .build(); - } - - builder.setAvatar(avatar); - sharedContacts.add(builder.build()); - } - - return sharedContacts; - } - - List getPreviewsFor(OutgoingMediaMessage mediaMessage) { - return Stream.of(mediaMessage.getLinkPreviews()).map(lp -> { - SignalServiceAttachment attachment = lp.getThumbnail().isPresent() ? getAttachmentPointerFor(lp.getThumbnail().get()) : null; - return new Preview(lp.getUrl(), lp.getTitle(), Optional.fromNullable(attachment)); - }).toList(); - } - - protected abstract void onPushSend() throws Exception; -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java deleted file mode 100644 index ef6e43b31d..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java +++ /dev/null @@ -1,241 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; - -import org.session.libsession.messaging.jobs.Data; -import org.session.libsignal.service.api.crypto.UnidentifiedAccess; -import org.session.libsignal.utilities.logging.Log; - -import org.session.libsession.messaging.threads.Address; -import org.session.libsession.messaging.threads.recipients.Recipient; -import org.session.libsession.messaging.threads.recipients.Recipient.UnidentifiedAccessMode; -import org.session.libsession.utilities.TextSecurePreferences; - -import org.thoughtcrime.securesms.ApplicationContext; -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; -import org.thoughtcrime.securesms.database.NoSuchMessageException; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.database.model.SmsMessageRecord; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase; -import org.thoughtcrime.securesms.service.ExpiringMessageManager; -import org.thoughtcrime.securesms.transport.RetryLaterException; -import org.session.libsignal.libsignal.util.guava.Optional; -import org.session.libsignal.service.api.SignalServiceMessageSender; -import org.session.libsignal.service.api.messages.SendMessageResult; -import org.session.libsignal.service.api.messages.SignalServiceDataMessage; -import org.session.libsignal.service.api.push.SignalServiceAddress; -import org.session.libsignal.service.loki.api.SnodeAPI; - -import java.io.IOException; - -import javax.inject.Inject; - -public class PushTextSendJob extends PushSendJob implements InjectableType { - - public static final String KEY = "PushTextSendJob"; - - private static final String TAG = PushTextSendJob.class.getSimpleName(); - - private static final String KEY_TEMPLATE_MESSAGE_ID = "template_message_id"; - private static final String KEY_MESSAGE_ID = "message_id"; - private static final String KEY_DESTINATION = "destination"; - - @Inject SignalServiceMessageSender messageSender; - - private long messageId; - private long templateMessageId; - private Address destination; - - public PushTextSendJob(long messageId, Address destination) { - this(messageId, messageId, destination); - } - - public PushTextSendJob(long templateMessageId, long messageId, Address destination) { - this(constructParameters(destination), templateMessageId, messageId, destination); - } - - private PushTextSendJob(@NonNull Job.Parameters parameters, long templateMessageId, long messageId, Address destination) { - super(parameters); - this.templateMessageId = templateMessageId; - this.messageId = messageId; - this.destination = destination; - } - - @Override - public @NonNull - Data serialize() { - return new Data.Builder() - .putLong(KEY_TEMPLATE_MESSAGE_ID, templateMessageId) - .putLong(KEY_MESSAGE_ID, messageId) - .putString(KEY_DESTINATION, destination.serialize()).build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onAdded() { - if (messageId >= 0) { - DatabaseFactory.getSmsDatabase(context).markAsSending(messageId); - } - } - - @Override - public void onPushSend() throws NoSuchMessageException, RetryLaterException { - ExpiringMessageManager expirationManager = ApplicationContext.getInstance(context).getExpiringMessageManager(); - SmsDatabase database = DatabaseFactory.getSmsDatabase(context); - SmsMessageRecord record = database.getMessage(templateMessageId); - - Recipient recordRecipient = record.getRecipient().resolve(); - boolean hasSameDestination = destination.equals(recordRecipient.getAddress()); - - if (hasSameDestination && !record.isPending() && !record.isFailed()) { - Log.d("Loki", "Message with ID: " + templateMessageId + " was already sent; ignoring."); - return; - } - - try { - log(TAG, "Sending message: " + templateMessageId + (hasSameDestination ? "" : "to a linked device.")); - - Recipient recipient = Recipient.from(context, destination, false); - byte[] profileKey = recipient.getProfileKey(); - UnidentifiedAccessMode accessMode = recipient.getUnidentifiedAccessMode(); - - boolean unidentified = deliver(record); - - if (messageId >= 0) { - database.markAsSent(messageId, true); - database.markUnidentified(messageId, unidentified); - } - - if (recipient.isLocalNumber()) { - SyncMessageId id = new SyncMessageId(recipient.getAddress(), record.getDateSent()); - DatabaseFactory.getMmsSmsDatabase(context).incrementDeliveryReceiptCount(id, System.currentTimeMillis()); - DatabaseFactory.getMmsSmsDatabase(context).incrementReadReceiptCount(id, System.currentTimeMillis()); - } - - if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN && profileKey == null) { - log(TAG, "Marking recipient as UD-unrestricted following a UD send."); - DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.UNRESTRICTED); - } else if (unidentified && accessMode == UnidentifiedAccessMode.UNKNOWN) { - log(TAG, "Marking recipient as UD-enabled following a UD send."); - DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.ENABLED); - } else if (!unidentified && accessMode != UnidentifiedAccessMode.DISABLED) { - log(TAG, "Marking recipient as UD-disabled following a non-UD send."); - DatabaseFactory.getRecipientDatabase(context).setUnidentifiedAccessMode(recipient, UnidentifiedAccessMode.DISABLED); - } - - if (record.getExpiresIn() > 0 && messageId >= 0) { - database.markExpireStarted(messageId); - expirationManager.scheduleDeletion(record.getId(), record.isMms(), record.getExpiresIn()); - } - - log(TAG, "Sent message: " + templateMessageId + (hasSameDestination ? "" : "to a linked device.")); - - } catch (SnodeAPI.Error e) { - Log.d("Loki", "Couldn't send message due to error: " + e.getDescription()); - if (messageId >= 0) { - LokiMessageDatabase lokiMessageDatabase = DatabaseFactory.getLokiMessageDatabase(context); - lokiMessageDatabase.setErrorMessage(record.getId(), e.getDescription()); - database.markAsSentFailed(record.getId()); - } - } - } - - @Override - public boolean onShouldRetry(@NonNull Exception exception) { - // Loki - Disable since we have our own retrying - return false; - } - - @Override - public void onCanceled() { - if (messageId >= 0) { - DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId); - - long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId); - Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); - - if (threadId != -1 && recipient != null) { - ApplicationContext.getInstance(context).messageNotifier.notifyMessageDeliveryFailed(context, recipient, threadId); - } - } - } - - private boolean deliver(SmsMessageRecord message) throws RetryLaterException, SnodeAPI.Error - { - try { - String userPublicKey = TextSecurePreferences.getLocalNumber(context); - Recipient recipient = Recipient.from(context, destination, false); - SignalServiceAddress address = getPushAddress(recipient.getAddress()); - SignalServiceAddress localAddress = new SignalServiceAddress(userPublicKey); - Optional profileKey = getProfileKey(recipient); - Optional unidentifiedAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient); - - log(TAG, "Have access key to use: " + unidentifiedAccess.isPresent()); - - SignalServiceDataMessage textSecureMessage = SignalServiceDataMessage.newBuilder() - .withTimestamp(message.getDateSent()) - .withBody(message.getBody()) - .withExpiration((int)(message.getExpiresIn() / 1000)) - .withProfileKey(profileKey.orNull()) - .build(); - - SignalServiceDataMessage textSecureSelfSendMessage = SignalServiceDataMessage.newBuilder() - .withTimestamp(message.getDateSent()) - .withBody(message.getBody()) - .withSyncTarget(destination.serialize()) - .withExpiration((int)(message.getExpiresIn() / 1000)) - .withProfileKey(profileKey.orNull()) - .build(); - - if (userPublicKey.equals(address.getNumber())) { - // Loki - Device link messages don't go through here - SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccess, textSecureMessage, true); - if (result.getLokiAPIError() != null) { - throw result.getLokiAPIError(); - } else { - return result.getSuccess().isUnidentified(); - } - } else { - SendMessageResult result = messageSender.sendMessage(messageId, address, unidentifiedAccess, textSecureMessage, false); - if (result.getLokiAPIError() != null) { - throw result.getLokiAPIError(); - } else { - boolean isUnidentified = result.getSuccess().isUnidentified(); - - try { - // send to ourselves to sync multi-device - Optional syncAccess = UnidentifiedAccessUtil.getAccessForSync(context); - SendMessageResult selfSendResult = messageSender.sendMessage(messageId, localAddress, syncAccess, textSecureSelfSendMessage, true); - if (selfSendResult.getLokiAPIError() != null) { - throw selfSendResult.getLokiAPIError(); - } - } catch (Exception e) { - Log.e("Loki", "Error sending message to ourselves", e); - } - return isUnidentified; - } - } - } catch (IOException e) { - warn(TAG, "Failure", e); - throw new RetryLaterException(e); - } - } - - public static class Factory implements Job.Factory { - @Override - public @NonNull PushTextSendJob create(@NonNull Parameters parameters, @NonNull Data data) { - long templateMessageID = data.getLong(KEY_TEMPLATE_MESSAGE_ID); - long messageID = data.getLong(KEY_MESSAGE_ID); - Address destination = Address.fromSerialized(data.getString(KEY_DESTINATION)); - return new PushTextSendJob(parameters, templateMessageID, messageID, destination); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java index 638d155655..f938691d24 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendDeliveryReceiptJob.java @@ -30,7 +30,7 @@ public class SendDeliveryReceiptJob extends BaseJob implements InjectableType { private static final String KEY_MESSAGE_ID = "message_id"; private static final String KEY_TIMESTAMP = "timestamp"; - private static final String TAG = SendReadReceiptJob.class.getSimpleName(); + private static final String TAG = "SendReadReceiptJob"; @Inject transient SignalServiceMessageSender messageSender; diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendJob.java deleted file mode 100644 index f71471b74e..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendJob.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.session.libsession.messaging.sending_receiving.attachments.Attachment; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.JobLogger; -import org.session.libsignal.utilities.logging.Log; -import org.thoughtcrime.securesms.mms.MediaConstraints; -import org.thoughtcrime.securesms.mms.MediaStream; -import org.thoughtcrime.securesms.mms.MmsException; -import org.thoughtcrime.securesms.transport.UndeliverableMessageException; -import org.thoughtcrime.securesms.util.MediaUtil; - -import java.io.IOException; -import java.util.LinkedList; -import java.util.List; - -public abstract class SendJob extends BaseJob { - - @SuppressWarnings("unused") - private final static String TAG = SendJob.class.getSimpleName(); - - public SendJob(Job.Parameters parameters) { - super(parameters); - } - - @Override - public final void onRun() throws Exception { - Log.i(TAG, "Starting message send attempt"); - onSend(); - Log.i(TAG, "Message send completed"); - } - - protected abstract void onSend() throws Exception; - - protected void markAttachmentsUploaded(long messageId, @NonNull List attachments) { - AttachmentDatabase database = DatabaseFactory.getAttachmentDatabase(context); - - for (Attachment attachment : attachments) { - database.markAttachmentUploaded(messageId, attachment); - } - } - - protected void log(@NonNull String tag, @NonNull String message) { - Log.i(tag, JobLogger.format(this, message)); - } - - protected void warn(@NonNull String tag, @NonNull String message) { - warn(tag, message, null); - } - - protected void warn(@NonNull String tag, @Nullable Throwable t) { - warn(tag, "", t); - } - - protected void warn(@NonNull String tag, @NonNull String message, @Nullable Throwable t) { - Log.w(tag, JobLogger.format(this, message), t); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java deleted file mode 100644 index 98d1cbb80d..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SendReadReceiptJob.java +++ /dev/null @@ -1,123 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - - -import androidx.annotation.NonNull; - -import org.session.libsession.messaging.jobs.Data; -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.session.libsession.messaging.threads.Address; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; -import org.session.libsignal.utilities.logging.Log; -import org.session.libsession.messaging.threads.recipients.Recipient; -import org.session.libsession.utilities.TextSecurePreferences; -import org.session.libsignal.service.api.SignalServiceMessageSender; -import org.session.libsignal.service.api.messages.SignalServiceReceiptMessage; -import org.session.libsignal.service.api.push.SignalServiceAddress; -import org.session.libsignal.service.api.push.exceptions.PushNetworkException; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class SendReadReceiptJob extends BaseJob implements InjectableType { - - public static final String KEY = "SendReadReceiptJob"; - - private static final String TAG = SendReadReceiptJob.class.getSimpleName(); - - private static final String KEY_ADDRESS = "address"; - private static final String KEY_MESSAGE_IDS = "message_ids"; - private static final String KEY_TIMESTAMP = "timestamp"; - - @Inject SignalServiceMessageSender messageSender; - - private String address; - private List messageIds; - private long timestamp; - - public SendReadReceiptJob(Address address, List messageIds) { - this(new Job.Parameters.Builder() - .addConstraint(NetworkConstraint.KEY) - .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(Parameters.UNLIMITED) - .build(), - address, - messageIds, - System.currentTimeMillis()); - } - - private SendReadReceiptJob(@NonNull Job.Parameters parameters, - @NonNull Address address, - @NonNull List messageIds, - long timestamp) - { - super(parameters); - - this.address = address.serialize(); - this.messageIds = messageIds; - this.timestamp = timestamp; - } - - @Override - public @NonNull - Data serialize() { - long[] ids = new long[messageIds.size()]; - for (int i = 0; i < ids.length; i++) { - ids[i] = messageIds.get(i); - } - - return new Data.Builder().putString(KEY_ADDRESS, address) - .putLongArray(KEY_MESSAGE_IDS, ids) - .putLong(KEY_TIMESTAMP, timestamp) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws IOException { - if (!TextSecurePreferences.isReadReceiptsEnabled(context) || messageIds.isEmpty()) return; - - SignalServiceAddress remoteAddress = new SignalServiceAddress(address); - SignalServiceReceiptMessage receiptMessage = new SignalServiceReceiptMessage(SignalServiceReceiptMessage.Type.READ, messageIds, timestamp); - - messageSender.sendReceipt(remoteAddress, - UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(address), false)), - receiptMessage); - } - - @Override - public boolean onShouldRetry(@NonNull Exception e) { - if (e instanceof PushNetworkException) return true; - return false; - } - - @Override - public void onCanceled() { - Log.w(TAG, "Failed to send read receipts to: " + address); - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull SendReadReceiptJob create(@NonNull Parameters parameters, @NonNull Data data) { - Address address = Address.fromSerialized(data.getString(KEY_ADDRESS)); - long timestamp = data.getLong(KEY_TIMESTAMP); - long[] ids = data.hasLongArray(KEY_MESSAGE_IDS) ? data.getLongArray(KEY_MESSAGE_IDS) : new long[0]; - List messageIds = new ArrayList<>(ids.length); - - for (long id : ids) { - messageIds.add(id); - } - - return new SendReadReceiptJob(parameters, address, messageIds, timestamp); - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/TypingSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/TypingSendJob.java deleted file mode 100644 index ad0325045b..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/TypingSendJob.java +++ /dev/null @@ -1,116 +0,0 @@ -package org.thoughtcrime.securesms.jobs; - -import androidx.annotation.NonNull; - -import com.annimon.stream.Stream; - -import org.session.libsession.messaging.jobs.Data; -import org.session.libsignal.libsignal.util.guava.Optional; -import org.session.libsignal.service.api.crypto.UnidentifiedAccess; -import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.dependencies.InjectableType; -import org.thoughtcrime.securesms.jobmanager.Job; -import org.session.libsignal.utilities.logging.Log; -import org.session.libsignal.service.api.SignalServiceMessageSender; -import org.session.libsignal.service.api.messages.SignalServiceTypingMessage; -import org.session.libsignal.service.api.messages.SignalServiceTypingMessage.Action; -import org.session.libsignal.service.api.push.SignalServiceAddress; - -import org.session.libsession.messaging.threads.recipients.Recipient; -import org.session.libsession.utilities.TextSecurePreferences; - -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -public class TypingSendJob extends BaseJob implements InjectableType { - - public static final String KEY = "TypingSendJob"; - - private static final String TAG = TypingSendJob.class.getSimpleName(); - - private static final String KEY_THREAD_ID = "thread_id"; - private static final String KEY_TYPING = "typing"; - - private long threadId; - private boolean typing; - - @Inject SignalServiceMessageSender messageSender; - - public TypingSendJob(long threadId, boolean typing) { - this(new Job.Parameters.Builder() - .setQueue("TYPING_" + threadId) - .setMaxAttempts(1) - .setLifespan(TimeUnit.SECONDS.toMillis(5)) - .build(), - threadId, - typing); - } - - private TypingSendJob(@NonNull Job.Parameters parameters, long threadId, boolean typing) { - super(parameters); - - this.threadId = threadId; - this.typing = typing; - } - - - @Override - public @NonNull - Data serialize() { - return new Data.Builder().putLong(KEY_THREAD_ID, threadId) - .putBoolean(KEY_TYPING, typing) - .build(); - } - - @Override - public @NonNull String getFactoryKey() { - return KEY; - } - - @Override - public void onRun() throws Exception { - if (!TextSecurePreferences.isTypingIndicatorsEnabled(context)) { - return; - } - - Log.d(TAG, "Sending typing " + (typing ? "started" : "stopped") + " for thread " + threadId); - - Recipient recipient = DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId); - - if (recipient == null) { - throw new IllegalStateException("Tried to send a typing indicator to a non-existent thread."); - } - - List recipients = Collections.singletonList(recipient); - - if (recipient.isGroupRecipient()) { - recipients = DatabaseFactory.getGroupDatabase(context).getGroupMembers(recipient.getAddress().toGroupString(), false); - } - - List addresses = Stream.of(recipients).map(r -> new SignalServiceAddress(r.getAddress().serialize())).toList(); - List> unidentifiedAccess = Stream.of(recipients).map(r -> UnidentifiedAccessUtil.getAccessFor(context, r)).toList(); - SignalServiceTypingMessage typingMessage = new SignalServiceTypingMessage(typing ? Action.STARTED : Action.STOPPED, System.currentTimeMillis()); - - messageSender.sendTyping(addresses, unidentifiedAccess, typingMessage); - } - - @Override - public void onCanceled() { - } - - @Override - protected boolean onShouldRetry(@NonNull Exception exception) { - return false; - } - - public static final class Factory implements Job.Factory { - @Override - public @NonNull TypingSendJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new TypingSendJob(parameters, data.getLong(KEY_THREAD_ID), data.getBoolean(KEY_TYPING)); - } - } -}