diff --git a/app/src/main/java/org/thoughtcrime/securesms/InviteActivity.java b/app/src/main/java/org/thoughtcrime/securesms/InviteActivity.java index e8194084be..41f075a6da 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/InviteActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/InviteActivity.java @@ -283,7 +283,7 @@ public class InviteActivity extends PassphraseRequiredActivity implements Contac Recipient recipient = Recipient.resolved(recipientId); int subscriptionId = recipient.getDefaultSubscriptionId().or(-1); - MessageSender.send(context, new OutgoingTextMessage(recipient, message, subscriptionId), -1L, true, null); + MessageSender.send(context, new OutgoingTextMessage(recipient, message, subscriptionId), -1L, true, null, null); if (recipient.getContactUri() != null) { DatabaseFactory.getRecipientDatabase(context).setHasSentInvite(recipient.getId()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsRepository.kt index 37878a692b..b9a27f284e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsRepository.kt @@ -38,7 +38,7 @@ class ExpireTimerSettingsRepository(val context: Context) { } else { DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipientId, newExpirationTime) val outgoingMessage = OutgoingExpirationUpdateMessage(Recipient.resolved(recipientId), System.currentTimeMillis(), newExpirationTime * 1000L) - MessageSender.send(context, outgoingMessage, getThreadId(recipientId), false, null) + MessageSender.send(context, outgoingMessage, getThreadId(recipientId), false, null, null) consumer.invoke(Result.success(newExpirationTime)) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 14326dcce4..f774d4f072 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -276,6 +276,7 @@ import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MessageUtil; import org.thoughtcrime.securesms.util.PlayStoreUtil; import org.thoughtcrime.securesms.util.ServiceUtil; +import org.thoughtcrime.securesms.util.SignalLocalMetrics; import org.thoughtcrime.securesms.util.SmsUtil; import org.thoughtcrime.securesms.util.SpanUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -788,7 +789,8 @@ public class ConversationActivity extends PassphraseRequiredActivity result.isViewOnce(), subscriptionId, initiating, - true).addListener(new AssertedSuccessListener() { + true, + null).addListener(new AssertedSuccessListener() { @Override public void onSuccess(Void result) { AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { @@ -1280,7 +1282,7 @@ public class ConversationActivity extends PassphraseRequiredActivity new AsyncTask() { @Override protected Long doInBackground(OutgoingEndSessionMessage... messages) { - return MessageSender.send(context, messages[0], threadId, false, null); + return MessageSender.send(context, messages[0], threadId, false, null, null); } @Override @@ -1526,7 +1528,7 @@ public class ConversationActivity extends PassphraseRequiredActivity initializeIdentityRecords().addListener(new AssertedSuccessListener() { @Override public void onSuccess(Boolean result) { - sendMessage(); + sendMessage(null); } }); } @@ -2587,7 +2589,7 @@ public class ConversationActivity extends PassphraseRequiredActivity long expiresIn = TimeUnit.SECONDS.toMillis(recipient.get().getExpiresInSeconds()); boolean initiating = threadId == -1; - sendMediaMessage(recipient.getId(), isSmsForced(), "", attachmentManager.buildSlideDeck(), null, contacts, Collections.emptyList(), Collections.emptyList(), expiresIn, false, subscriptionId, initiating, false); + sendMediaMessage(recipient.getId(), isSmsForced(), "", attachmentManager.buildSlideDeck(), null, contacts, Collections.emptyList(), Collections.emptyList(), expiresIn, false, subscriptionId, initiating, false, null); } private void selectContactInfo(ContactData contactData) { @@ -2848,7 +2850,7 @@ public class ConversationActivity extends PassphraseRequiredActivity updateLinkPreviewState(); } - private void sendMessage() { + private void sendMessage(@Nullable String metricId) { if (inputPanel.isRecordingInLockedMode()) { inputPanel.releaseRecordingLock(); return; @@ -2892,9 +2894,9 @@ public class ConversationActivity extends PassphraseRequiredActivity } else if (!forceSms && (identityRecords.isUnverified(true) || identityRecords.isUntrusted(true))) { handleRecentSafetyNumberChange(); } else if (isMediaMessage) { - sendMediaMessage(forceSms, expiresIn, false, subscriptionId, initiating); + sendMediaMessage(forceSms, expiresIn, false, subscriptionId, initiating, metricId); } else { - sendTextMessage(forceSms, expiresIn, subscriptionId, initiating); + sendTextMessage(forceSms, expiresIn, subscriptionId, initiating, metricId); } } catch (RecipientFormattingException ex) { Toast.makeText(ConversationActivity.this, @@ -2934,7 +2936,7 @@ public class ConversationActivity extends PassphraseRequiredActivity }, this::sendComplete); } - private void sendMediaMessage(final boolean forceSms, final long expiresIn, final boolean viewOnce, final int subscriptionId, final boolean initiating) + private void sendMediaMessage(final boolean forceSms, final long expiresIn, final boolean viewOnce, final int subscriptionId, final boolean initiating, @Nullable String metricId) throws InvalidMessageException { Log.i(TAG, "Sending media message..."); @@ -2951,7 +2953,8 @@ public class ConversationActivity extends PassphraseRequiredActivity viewOnce, subscriptionId, initiating, - true); + true, + metricId); } private ListenableFuture sendMediaMessage(@NonNull RecipientId recipientId, @@ -2966,7 +2969,8 @@ public class ConversationActivity extends PassphraseRequiredActivity final boolean viewOnce, final int subscriptionId, final boolean initiating, - final boolean clearComposeBox) + final boolean clearComposeBox, + final @Nullable String metricId) { if (!isDefaultSms && (!isSecureText || forceSms) && recipient.get().hasSmsAddress()) { showDefaultSmsPrompt(); @@ -3013,7 +3017,7 @@ public class ConversationActivity extends PassphraseRequiredActivity final long id = fragment.stageOutgoingMessage(outgoingMessage); SimpleTask.run(() -> { - return MessageSender.send(context, outgoingMessage, thread, forceSms, () -> fragment.releaseOutgoingMessage(id)); + return MessageSender.send(context, outgoingMessage, thread, forceSms, metricId, () -> fragment.releaseOutgoingMessage(id)); }, result -> { sendComplete(result); future.set(null); @@ -3025,7 +3029,7 @@ public class ConversationActivity extends PassphraseRequiredActivity return future; } - private void sendTextMessage(final boolean forceSms, final long expiresIn, final int subscriptionId, final boolean initiating) + private void sendTextMessage(final boolean forceSms, final long expiresIn, final int subscriptionId, final boolean initiating, final @Nullable String metricId) throws InvalidMessageException { if (!isDefaultSms && (!isSecureText || forceSms) && recipient.get().hasSmsAddress()) { @@ -3054,7 +3058,7 @@ public class ConversationActivity extends PassphraseRequiredActivity .onAllGranted(() -> { final long id = new SecureRandom().nextLong(); SimpleTask.run(() -> { - return MessageSender.send(context, message, thread, forceSms, () -> fragment.releaseOutgoingMessage(id)); + return MessageSender.send(context, message, thread, forceSms, metricId, () -> fragment.releaseOutgoingMessage(id)); }, this::sendComplete); silentlySetComposeText(""); @@ -3283,7 +3287,8 @@ public class ConversationActivity extends PassphraseRequiredActivity false, subscriptionId, initiating, - true); + true, + null); sendResult.addListener(new AssertedSuccessListener() { @Override @@ -3319,7 +3324,7 @@ public class ConversationActivity extends PassphraseRequiredActivity slideDeck.addSlide(stickerSlide); - sendMediaMessage(recipient.getId(), transport.isSms(), "", slideDeck, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), expiresIn, false, subscriptionId, initiating, clearCompose); + sendMediaMessage(recipient.getId(), transport.isSms(), "", slideDeck, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), expiresIn, false, subscriptionId, initiating, clearCompose, null); } private void silentlySetComposeText(String text) { @@ -3455,7 +3460,9 @@ public class ConversationActivity extends PassphraseRequiredActivity private class SendButtonListener implements OnClickListener, TextView.OnEditorActionListener { @Override public void onClick(View v) { - sendMessage(); + String metricId = recipient.get().isGroup() ? SignalLocalMetrics.GroupMessageSend.start() + : SignalLocalMetrics.IndividualMessageSend.start(); + sendMessage(metricId); } @Override @@ -3940,7 +3947,8 @@ public class ConversationActivity extends PassphraseRequiredActivity false, subscriptionId, initiating, - false); + false, + null); } private class UnverifiedDismissedListener implements UnverifiedBannerView.DismissListener { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java index 0a47cf94fc..3313d47662 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.java @@ -1470,6 +1470,8 @@ public class MmsDatabase extends MessageDatabase { @Nullable SmsDatabase.InsertListener insertListener) throws MmsException { + SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); + long type = Types.BASE_SENDING_TYPE; if (message.isSecure()) type |= (Types.SECURE_MESSAGE_BIT | Types.PUSH_MESSAGE_BIT); @@ -1529,33 +1531,43 @@ public class MmsDatabase extends MessageDatabase { } MentionUtil.UpdatedBodyAndMentions updatedBodyAndMentions = MentionUtil.updateBodyAndMentionsWithPlaceholders(message.getBody(), message.getMentions()); - long messageId = insertMediaMessage(threadId, updatedBodyAndMentions.getBodyAsString(), message.getAttachments(), quoteAttachments, message.getSharedContacts(), message.getLinkPreviews(), updatedBodyAndMentions.getMentions(), contentValues, insertListener); - if (message.getRecipient().isGroup()) { - OutgoingGroupUpdateMessage outgoingGroupUpdateMessage = (message instanceof OutgoingGroupUpdateMessage) ? (OutgoingGroupUpdateMessage) message : null; + long messageId; - GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context); - Set members = new HashSet<>(); + db.beginTransaction(); + try { + messageId = insertMediaMessage(threadId, updatedBodyAndMentions.getBodyAsString(), message.getAttachments(), quoteAttachments, message.getSharedContacts(), message.getLinkPreviews(), updatedBodyAndMentions.getMentions(), contentValues, insertListener); - if (outgoingGroupUpdateMessage != null && outgoingGroupUpdateMessage.isV2Group()) { - MessageGroupContext.GroupV2Properties groupV2Properties = outgoingGroupUpdateMessage.requireGroupV2Properties(); - members.addAll(Stream.of(groupV2Properties.getAllActivePendingAndRemovedMembers()) - .distinct() - .map(uuid -> RecipientId.from(uuid, null)) - .toList()); - members.remove(Recipient.self().getId()); - } else { - members.addAll(Stream.of(DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_EXCLUDING_SELF)).map(Recipient::getId).toList()); + if (message.getRecipient().isGroup()) { + OutgoingGroupUpdateMessage outgoingGroupUpdateMessage = (message instanceof OutgoingGroupUpdateMessage) ? (OutgoingGroupUpdateMessage) message : null; + + GroupReceiptDatabase receiptDatabase = DatabaseFactory.getGroupReceiptDatabase(context); + Set members = new HashSet<>(); + + if (outgoingGroupUpdateMessage != null && outgoingGroupUpdateMessage.isV2Group()) { + MessageGroupContext.GroupV2Properties groupV2Properties = outgoingGroupUpdateMessage.requireGroupV2Properties(); + members.addAll(Stream.of(groupV2Properties.getAllActivePendingAndRemovedMembers()) + .distinct() + .map(uuid -> RecipientId.from(uuid, null)) + .toList()); + members.remove(Recipient.self().getId()); + } else { + members.addAll(Stream.of(DatabaseFactory.getGroupDatabase(context).getGroupMembers(message.getRecipient().requireGroupId(), GroupDatabase.MemberSet.FULL_MEMBERS_EXCLUDING_SELF)).map(Recipient::getId).toList()); + } + + receiptDatabase.insert(members, messageId, defaultReceiptStatus, message.getSentTimeMillis()); + + for (RecipientId recipientId : earlyDeliveryReceipts.keySet()) receiptDatabase.update(recipientId, messageId, GroupReceiptDatabase.STATUS_DELIVERED, -1); } - receiptDatabase.insert(members, messageId, defaultReceiptStatus, message.getSentTimeMillis()); + DatabaseFactory.getThreadDatabase(context).setLastSeenSilently(threadId); + DatabaseFactory.getThreadDatabase(context).setHasSentSilently(threadId, true); - for (RecipientId recipientId : earlyDeliveryReceipts.keySet()) receiptDatabase.update(recipientId, messageId, GroupReceiptDatabase.STATUS_DELIVERED, -1); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); } - DatabaseFactory.getThreadDatabase(context).setLastSeenSilently(threadId); - DatabaseFactory.getThreadDatabase(context).setHasSentSilently(threadId, true); - notifyConversationListeners(threadId); notifyConversationListListeners(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index d5b694fd81..1cfe36c731 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -1187,6 +1187,8 @@ public class SmsDatabase extends MessageDatabase { public long insertMessageOutbox(long threadId, OutgoingTextMessage message, boolean forceSms, long date, InsertListener insertListener) { + SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); + long type = Types.BASE_SENDING_TYPE; if (message.isKeyExchange()) type |= Types.KEY_EXCHANGE_BIT; @@ -1212,21 +1214,28 @@ public class SmsDatabase extends MessageDatabase { contentValues.put(EXPIRES_IN, message.getExpiresIn()); contentValues.put(DELIVERY_RECEIPT_COUNT, Stream.of(earlyDeliveryReceipts.values()).mapToLong(Long::longValue).sum()); - SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - long messageId = db.insert(TABLE_NAME, null, contentValues); + long messageId; - if (insertListener != null) { - insertListener.onComplete(); + db.beginTransaction(); + try { + messageId = db.insert(TABLE_NAME, null, contentValues); + + if (insertListener != null) { + insertListener.onComplete(); + } + + if (!message.isIdentityVerified() && !message.isIdentityDefault()) { + DatabaseFactory.getThreadDatabase(context).setLastScrolled(threadId, 0); + DatabaseFactory.getThreadDatabase(context).updateSilently(threadId, true); + DatabaseFactory.getThreadDatabase(context).setLastSeenSilently(threadId); + } + + DatabaseFactory.getThreadDatabase(context).setHasSentSilently(threadId, true); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); } - if (!message.isIdentityVerified() && !message.isIdentityDefault()) { - DatabaseFactory.getThreadDatabase(context).setLastScrolled(threadId, 0); - DatabaseFactory.getThreadDatabase(context).updateSilently(threadId, true); - DatabaseFactory.getThreadDatabase(context).setLastSeenSilently(threadId); - } - - DatabaseFactory.getThreadDatabase(context).setHasSentSilently(threadId, true); - notifyConversationListeners(threadId); notifyConversationListListeners(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV1.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV1.java index 9760459d28..10d013ea4e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV1.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV1.java @@ -177,7 +177,7 @@ final class GroupManagerV1 { } OutgoingGroupUpdateMessage outgoingMessage = new OutgoingGroupUpdateMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis(), 0, false, null, Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); - long threadId = MessageSender.send(context, outgoingMessage, -1, false, null); + long threadId = MessageSender.send(context, outgoingMessage, -1, false, null, null); return new GroupActionResult(groupRecipient, threadId, newMemberCount, Collections.emptyList()); } @@ -201,7 +201,7 @@ final class GroupManagerV1 { recipientDatabase.setExpireMessages(recipient.getId(), expirationTime); OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(recipient, System.currentTimeMillis(), expirationTime * 1000L); - MessageSender.send(context, outgoingMessage, threadId, false, null); + MessageSender.send(context, outgoingMessage, threadId, false, null, null); } @WorkerThread diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java index 780bd4ef5c..4cf09b02eb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupManagerV2.java @@ -1151,7 +1151,7 @@ final class GroupManagerV2 { ApplicationDependencies.getJobManager().add(PushGroupSilentUpdateSendJob.create(context, groupId, groupMutation.getNewGroupState(), outgoingMessage)); return new RecipientAndThread(groupRecipient, -1); } else { - long threadId = MessageSender.send(context, outgoingMessage, -1, false, null); + long threadId = MessageSender.send(context, outgoingMessage, -1, false, null, null); return new RecipientAndThread(groupRecipient, threadId); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/insights/InsightsRepository.java b/app/src/main/java/org/thoughtcrime/securesms/insights/InsightsRepository.java index fb5d44ee37..6106e3461c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/insights/InsightsRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/insights/InsightsRepository.java @@ -81,7 +81,7 @@ public class InsightsRepository implements InsightsDashboardViewModel.Repository int subscriptionId = resolved.getDefaultSubscriptionId().or(-1); String message = context.getString(R.string.InviteActivity_lets_switch_to_signal, context.getString(R.string.install_url)); - MessageSender.send(context, new OutgoingTextMessage(resolved, message, subscriptionId), -1L, true, null); + MessageSender.send(context, new OutgoingTextMessage(resolved, message, subscriptionId), -1L, true, null, null); RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context); database.setHasSentInvite(recipient.getId()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java index 6aae8bf8c4..4e095a5010 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupSendJob.java @@ -266,6 +266,12 @@ public final class PushGroupSendJob extends PushSendJob { SignalLocalMetrics.GroupMessageSend.onJobFinished(messageId); } + @Override + public void onRetry() { + SignalLocalMetrics.GroupMessageSend.cancel(messageId); + super.onRetry(); + } + @Override public void onFailure() { DatabaseFactory.getMmsDatabase(context).markAsSentFailed(messageId); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java index 28131b26ba..0bdf453b7d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -51,6 +51,7 @@ import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.Hex; import org.thoughtcrime.securesms.util.MediaUtil; +import org.thoughtcrime.securesms.util.SignalLocalMetrics; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.util.guava.Optional; @@ -125,7 +126,6 @@ public abstract class PushSendJob extends SendJob { @Override public void onRetry() { - super.onRetry(); Log.i(TAG, "onRetry()"); if (getRunAttempt() > 1) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java index f111747935..1f851db33f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushTextSendJob.java @@ -143,6 +143,12 @@ public class PushTextSendJob extends PushSendJob { SignalLocalMetrics.IndividualMessageSend.onJobFinished(messageId); } + @Override + public void onRetry() { + SignalLocalMetrics.IndividualMessageSend.cancel(messageId); + super.onRetry(); + } + @Override public void onFailure() { DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId); diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java index 0ad483c179..ed78a3d114 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/RemoteReplyReceiver.java @@ -92,17 +92,17 @@ public class RemoteReplyReceiver extends BroadcastReceiver { Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); - threadId = MessageSender.send(context, reply, -1, false, null); + threadId = MessageSender.send(context, reply, -1, false, null, null); break; } case SecureMessage: { OutgoingEncryptedMessage reply = new OutgoingEncryptedMessage(recipient, responseText.toString(), expiresIn); - threadId = MessageSender.send(context, reply, -1, false, null); + threadId = MessageSender.send(context, reply, -1, false, null, null); break; } case UnsecuredSmsMessage: { OutgoingTextMessage reply = new OutgoingTextMessage(recipient, responseText.toString(), expiresIn, subscriptionId); - threadId = MessageSender.send(context, reply, -1, true, null); + threadId = MessageSender.send(context, reply, -1, true, null, null); break; } default: diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java index 5baff4ee35..197ecd4191 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientUtil.java @@ -285,7 +285,7 @@ public class RecipientUtil { if (threadId == -1 || !DatabaseFactory.getMmsSmsDatabase(context).hasMeaningfulMessage(threadId)) { DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient.getId(), defaultTimer); OutgoingExpirationUpdateMessage outgoingMessage = new OutgoingExpirationUpdateMessage(recipient, System.currentTimeMillis(), defaultTimer * 1000L); - MessageSender.send(context, outgoingMessage, DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient), false, null); + MessageSender.send(context, outgoingMessage, DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient), false, null, null); return true; } return false; diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/QuickResponseService.java b/app/src/main/java/org/thoughtcrime/securesms/service/QuickResponseService.java index 2da38bf5bd..504a5e60a9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/QuickResponseService.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/QuickResponseService.java @@ -52,7 +52,7 @@ public class QuickResponseService extends IntentService { long expiresIn = TimeUnit.SECONDS.toMillis(recipient.getExpiresInSeconds()); if (!TextUtils.isEmpty(content)) { - MessageSender.send(this, new OutgoingTextMessage(recipient, content, expiresIn, subscriptionId), -1, false, null); + MessageSender.send(this, new OutgoingTextMessage(recipient, content, expiresIn, subscriptionId), -1, false, null, null); } } catch (URISyntaxException e) { Toast.makeText(this, R.string.QuickResponseService_problem_sending_message, Toast.LENGTH_LONG).show(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareSender.java b/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareSender.java index 7808167fdd..5f9ed40cff 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareSender.java +++ b/app/src/main/java/org/thoughtcrime/securesms/sharing/MultiShareSender.java @@ -179,9 +179,9 @@ public final class MultiShareSender { validatedMentions); if (recipient.isRegistered() && !forceSms) { - MessageSender.send(context, new OutgoingSecureMediaMessage(outgoingMediaMessage), threadId, false, null); + MessageSender.send(context, new OutgoingSecureMediaMessage(outgoingMediaMessage), threadId, false, null, null); } else { - MessageSender.send(context, outgoingMediaMessage, threadId, forceSms, null); + MessageSender.send(context, outgoingMediaMessage, threadId, forceSms, null, null); } } @@ -202,7 +202,7 @@ public final class MultiShareSender { outgoingTextMessage = new OutgoingTextMessage(recipient, multiShareArgs.getDraftText(), expiresIn, subscriptionId); } - MessageSender.send(context, outgoingTextMessage, threadId, forceSms, null); + MessageSender.send(context, outgoingTextMessage, threadId, forceSms, null, null); } private static @NonNull SlideDeck buildSlideDeck(@NonNull Context context, @NonNull MultiShareArgs multiShareArgs) throws SlideNotFoundException { diff --git a/app/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java b/app/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java index fed6d73024..174c9c3d6b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java +++ b/app/src/main/java/org/thoughtcrime/securesms/sms/MessageSender.java @@ -101,6 +101,7 @@ public class MessageSender { final OutgoingTextMessage message, final long threadId, final boolean forceSms, + @Nullable final String metricId, final SmsDatabase.InsertListener insertListener) { Log.i(TAG, "Sending text message to " + message.getRecipient().getId() + ", thread: " + threadId); @@ -115,7 +116,7 @@ public class MessageSender { System.currentTimeMillis(), insertListener); - SignalLocalMetrics.IndividualMessageSend.start(messageId); + SignalLocalMetrics.IndividualMessageSend.onInsertedIntoDatabase(messageId, metricId); sendTextMessage(context, recipient, forceSms, keyExchange, messageId); onMessageSent(); @@ -127,6 +128,7 @@ public class MessageSender { final OutgoingMediaMessage message, final long threadId, final boolean forceSms, + @Nullable final String metricId, final SmsDatabase.InsertListener insertListener) { Log.i(TAG, "Sending media message to " + message.getRecipient().getId() + ", thread: " + threadId); @@ -139,7 +141,9 @@ public class MessageSender { long messageId = database.insertMessageOutbox(applyUniversalExpireTimerIfNecessary(context, recipient, message, allocatedThreadId), allocatedThreadId, forceSms, insertListener); if (message.getRecipient().isGroup() && message.getAttachments().isEmpty() && message.getLinkPreviews().isEmpty() && message.getSharedContacts().isEmpty()) { - SignalLocalMetrics.GroupMessageSend.start(messageId); + SignalLocalMetrics.GroupMessageSend.onInsertedIntoDatabase(messageId, metricId); + } else { + SignalLocalMetrics.GroupMessageSend.cancel(metricId); } sendMediaMessage(context, recipient, forceSms, messageId, Collections.emptyList()); @@ -152,7 +156,6 @@ public class MessageSender { } } - public static long sendPushWithPreUploadedMedia(final Context context, final OutgoingMediaMessage message, final Collection preUploadResults, diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/LocalMetrics.kt b/app/src/main/java/org/thoughtcrime/securesms/util/LocalMetrics.kt index 6173fd1fef..2cc05edf9c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/LocalMetrics.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/LocalMetrics.kt @@ -79,7 +79,7 @@ object LocalMetrics { /** * Stop tracking an event you were previously tracking. All future calls to [split] and [end] will do nothing for this id. */ - fun drop(id: String) { + fun cancel(id: String) { executor.execute { eventsById.remove(id) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SignalLocalMetrics.java b/app/src/main/java/org/thoughtcrime/securesms/util/SignalLocalMetrics.java index db3100bd67..fc7135ba17 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SignalLocalMetrics.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SignalLocalMetrics.java @@ -1,6 +1,12 @@ package org.thoughtcrime.securesms.util; import androidx.annotation.MainThread; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; /** * A nice interface for {@link LocalMetrics} that gives us a place to define string constants and nicer method names. @@ -55,54 +61,15 @@ public final class SignalLocalMetrics { if (isConversationList) { LocalMetrics.getInstance().split(conversationListId, SPLIT_RENDER); LocalMetrics.getInstance().end(conversationListId); - LocalMetrics.getInstance().drop(otherId); + LocalMetrics.getInstance().cancel(otherId); } else { LocalMetrics.getInstance().split(otherId, SPLIT_RENDER); LocalMetrics.getInstance().end(otherId); - LocalMetrics.getInstance().drop(conversationListId); + LocalMetrics.getInstance().cancel(conversationListId); } } } - public static final class IndividualMessageSend { - private static final String NAME = "individual-message-send"; - - private static final String SPLIT_JOB_ENQUEUE = "job-enqueue"; - private static final String SPLIT_JOB_PRE_NETWORK = "job-pre-network"; - private static final String SPLIT_NETWORK = "network"; - private static final String SPLIT_JOB_POST_NETWORK = "job-post-network"; - private static final String SPLIT_UI_UPDATE = "ui-update"; - - public static void start(long messageId) { - LocalMetrics.getInstance().start(buildId(messageId), NAME); - } - - public static void onJobStarted(long messageId) { - LocalMetrics.getInstance().split(buildId(messageId), SPLIT_JOB_ENQUEUE); - } - - public static void onNetworkStarted(long messageId) { - LocalMetrics.getInstance().split(buildId(messageId), SPLIT_JOB_PRE_NETWORK); - } - - public static void onNetworkFinished(long messageId) { - LocalMetrics.getInstance().split(buildId(messageId), SPLIT_NETWORK); - } - - public static void onJobFinished(long messageId) { - LocalMetrics.getInstance().split(buildId(messageId), SPLIT_JOB_POST_NETWORK); - } - - public static void onUiUpdated(long messageId) { - LocalMetrics.getInstance().split(buildId(messageId), SPLIT_UI_UPDATE); - LocalMetrics.getInstance().end(buildId(messageId)); - } - - private static String buildId(long messageId) { - return NAME + "-" + messageId; - } - } - public static final class ConversationOpen { private static final String NAME = "conversation-open"; @@ -126,42 +93,143 @@ public final class SignalLocalMetrics { } } - public static final class GroupMessageSend { - private static final String NAME = "group-message-send"; + public static final class IndividualMessageSend { + private static final String NAME = "individual-message-send"; + private static final String SPLIT_DB_INSERT = "db-insert"; private static final String SPLIT_JOB_ENQUEUE = "job-enqueue"; private static final String SPLIT_JOB_PRE_NETWORK = "job-pre-network"; private static final String SPLIT_NETWORK = "network"; private static final String SPLIT_JOB_POST_NETWORK = "job-post-network"; private static final String SPLIT_UI_UPDATE = "ui-update"; - public static void start(long messageId) { - LocalMetrics.getInstance().start(buildId(messageId), NAME); + private static final Map ID_MAP = new HashMap<>(); + + public static @NonNull String start() { + String id = NAME + System.currentTimeMillis(); + LocalMetrics.getInstance().start(id, NAME); + return id; + } + + public static void onInsertedIntoDatabase(long messageId, String id) { + if (id != null) { + ID_MAP.put(messageId, id); + LocalMetrics.getInstance().split(requireId(messageId), SPLIT_DB_INSERT); + } } public static void onJobStarted(long messageId) { - LocalMetrics.getInstance().split(buildId(messageId), SPLIT_JOB_ENQUEUE); + if (!ID_MAP.containsKey(messageId)) return; + LocalMetrics.getInstance().split(requireId(messageId), SPLIT_JOB_ENQUEUE); } public static void onNetworkStarted(long messageId) { - LocalMetrics.getInstance().split(buildId(messageId), SPLIT_JOB_PRE_NETWORK); + if (!ID_MAP.containsKey(messageId)) return; + LocalMetrics.getInstance().split(requireId(messageId), SPLIT_JOB_PRE_NETWORK); } public static void onNetworkFinished(long messageId) { - LocalMetrics.getInstance().split(buildId(messageId), SPLIT_NETWORK); + if (!ID_MAP.containsKey(messageId)) return; + LocalMetrics.getInstance().split(requireId(messageId), SPLIT_NETWORK); } public static void onJobFinished(long messageId) { - LocalMetrics.getInstance().split(buildId(messageId), SPLIT_JOB_POST_NETWORK); + if (!ID_MAP.containsKey(messageId)) return; + LocalMetrics.getInstance().split(requireId(messageId), SPLIT_JOB_POST_NETWORK); } public static void onUiUpdated(long messageId) { - LocalMetrics.getInstance().split(buildId(messageId), SPLIT_UI_UPDATE); - LocalMetrics.getInstance().end(buildId(messageId)); + if (!ID_MAP.containsKey(messageId)) return; + LocalMetrics.getInstance().split(requireId(messageId), SPLIT_UI_UPDATE); + LocalMetrics.getInstance().end(requireId(messageId)); + + ID_MAP.remove(messageId); } - private static String buildId(long messageId) { - return NAME + "-" + messageId; + public static void cancel(@Nullable String id) { + if (id != null) { + LocalMetrics.getInstance().cancel(id); + } + } + + public static void cancel(long messageId) { + if (!ID_MAP.containsKey(messageId)) return; + LocalMetrics.getInstance().cancel(requireId(messageId)); + } + + + private static @NonNull String requireId(long messageId) { + return Objects.requireNonNull(ID_MAP.get(messageId)); + } + } + + public static final class GroupMessageSend { + private static final String NAME = "group-message-send"; + + private static final String SPLIT_DB_INSERT = "db-insert"; + private static final String SPLIT_JOB_ENQUEUE = "job-enqueue"; + private static final String SPLIT_JOB_PRE_NETWORK = "job-pre-network"; + private static final String SPLIT_NETWORK = "network"; + private static final String SPLIT_JOB_POST_NETWORK = "job-post-network"; + private static final String SPLIT_UI_UPDATE = "ui-update"; + + private static final Map ID_MAP = new HashMap<>(); + + public static @NonNull String start() { + String id = NAME + System.currentTimeMillis(); + LocalMetrics.getInstance().start(id, NAME); + return id; + } + + public static void onInsertedIntoDatabase(long messageId, String id) { + if (id != null) { + ID_MAP.put(messageId, id); + LocalMetrics.getInstance().split(requireId(messageId), SPLIT_DB_INSERT); + } + } + + public static void onJobStarted(long messageId) { + if (!ID_MAP.containsKey(messageId)) return; + LocalMetrics.getInstance().split(requireId(messageId), SPLIT_JOB_ENQUEUE); + } + + public static void onNetworkStarted(long messageId) { + if (!ID_MAP.containsKey(messageId)) return; + LocalMetrics.getInstance().split(requireId(messageId), SPLIT_JOB_PRE_NETWORK); + } + + public static void onNetworkFinished(long messageId) { + if (!ID_MAP.containsKey(messageId)) return; + LocalMetrics.getInstance().split(requireId(messageId), SPLIT_NETWORK); + } + + public static void onJobFinished(long messageId) { + if (!ID_MAP.containsKey(messageId)) return; + LocalMetrics.getInstance().split(requireId(messageId), SPLIT_JOB_POST_NETWORK); + } + + public static void onUiUpdated(long messageId) { + if (!ID_MAP.containsKey(messageId)) return; + LocalMetrics.getInstance().split(requireId(messageId), SPLIT_UI_UPDATE); + LocalMetrics.getInstance().end(requireId(messageId)); + + ID_MAP.remove(messageId); + } + + public static void cancel(@Nullable String id) { + if (id != null) { + LocalMetrics.getInstance().cancel(id); + } + } + + public static void cancel(long messageId) { + if (!ID_MAP.containsKey(messageId)) return; + LocalMetrics.getInstance().cancel(requireId(messageId)); + } + + + private static @NonNull String requireId(long messageId) { + return Objects.requireNonNull(ID_MAP.get(messageId)); } } }