From 76a9342afaee4befc2054451593122b2f51d51b7 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 30 Mar 2022 09:25:36 -0300 Subject: [PATCH] Safety number check and profile refresh for group sends. --- .../reply/group/StoryGroupReplyFragment.kt | 59 +++++++++++++++- .../reply/group/StoryGroupReplySender.kt | 70 +++++++++++-------- 2 files changed, 98 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/group/StoryGroupReplyFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/group/StoryGroupReplyFragment.kt index 2898605101..c0530e0724 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/group/StoryGroupReplyFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/group/StoryGroupReplyFragment.kt @@ -11,17 +11,25 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.kotlin.subscribeBy +import org.signal.core.util.concurrent.SignalExecutors +import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.emoji.MediaKeyboard import org.thoughtcrime.securesms.components.mention.MentionAnnotation import org.thoughtcrime.securesms.components.settings.DSLConfiguration import org.thoughtcrime.securesms.components.settings.configure import org.thoughtcrime.securesms.conversation.colors.Colorizer +import org.thoughtcrime.securesms.conversation.ui.error.SafetyNumberChangeDialog import org.thoughtcrime.securesms.conversation.ui.mentions.MentionsPickerFragment import org.thoughtcrime.securesms.conversation.ui.mentions.MentionsPickerViewModel +import org.thoughtcrime.securesms.database.model.Mention +import org.thoughtcrime.securesms.jobs.RetrieveProfileJob import org.thoughtcrime.securesms.keyboard.KeyboardPage import org.thoughtcrime.securesms.keyboard.KeyboardPagerViewModel import org.thoughtcrime.securesms.keyboard.emoji.EmojiKeyboardCallback +import org.thoughtcrime.securesms.mediasend.v2.UntrustedRecords import org.thoughtcrime.securesms.reactions.any.ReactWithAnyEmojiBottomSheetDialogFragment import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId @@ -51,7 +59,8 @@ class StoryGroupReplyFragment : BottomSheetBehaviorDelegate, StoryReplyComposer.Callback, EmojiKeyboardCallback, - ReactWithAnyEmojiBottomSheetDialogFragment.Callback { + ReactWithAnyEmojiBottomSheetDialogFragment.Callback, + SafetyNumberChangeDialog.Callback { private val viewModel: StoryGroupReplyViewModel by viewModels( factoryProducer = { @@ -81,6 +90,10 @@ class StoryGroupReplyFragment : private lateinit var composer: StoryReplyComposer override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + SignalExecutors.BOUNDED.execute { + RetrieveProfileJob.enqueue(groupRecipientId) + } + recyclerView = view.findViewById(R.id.recycler) composer = view.findViewById(R.id.composer) @@ -194,9 +207,12 @@ class StoryGroupReplyFragment : recyclerView.isNestedScrollingEnabled = child == StoryViewsAndRepliesPagerParent.Child.REPLIES } + private var resendBody: CharSequence? = null + private var resendMentions: List = emptyList() + override fun onSendActionClicked() { val (body, mentions) = composer.consumeInput() - lifecycleDisposable += StoryGroupReplySender.sendReply(requireContext(), storyId, body, mentions).subscribe() + performSend(body, mentions) } override fun onPickReactionClicked() { @@ -299,6 +315,8 @@ class StoryGroupReplyFragment : } companion object { + private val TAG = Log.tag(StoryGroupReplyFragment::class.java) + private const val ARG_STORY_ID = "arg.story.id" private const val ARG_GROUP_RECIPIENT_ID = "arg.group.recipient.id" @@ -312,6 +330,43 @@ class StoryGroupReplyFragment : } } + private fun performSend(body: CharSequence, mentions: List) { + lifecycleDisposable += StoryGroupReplySender.sendReply(requireContext(), storyId, body, mentions) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeBy( + onError = { + if (it is UntrustedRecords.UntrustedRecordsException) { + resendBody = body + resendMentions = mentions + + SafetyNumberChangeDialog.show(childFragmentManager, it.untrustedRecords) + } else { + Log.w(TAG, "Failed to send reply", it) + val context = context + if (context != null) { + Toast.makeText(context, R.string.message_details_recipient__failed_to_send, Toast.LENGTH_SHORT).show() + } + } + } + ) + } + + override fun onSendAnywayAfterSafetyNumberChange(changedRecipients: MutableList) { + val resendBody = resendBody + if (resendBody != null) { + performSend(resendBody, resendMentions) + } + } + + override fun onMessageResentAfterSafetyNumberChange() { + error("Should never get here.") + } + + override fun onCanceled() { + resendBody = null + resendMentions = emptyList() + } + interface Callback { fun onStartDirectReply(recipientId: RecipientId) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/group/StoryGroupReplySender.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/group/StoryGroupReplySender.kt index d8f095ad1c..046b96cbab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/group/StoryGroupReplySender.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/reply/group/StoryGroupReplySender.kt @@ -2,11 +2,14 @@ package org.thoughtcrime.securesms.stories.viewer.reply.group import android.content.Context import io.reactivex.rxjava3.core.Completable +import io.reactivex.rxjava3.core.Single import io.reactivex.rxjava3.schedulers.Schedulers +import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.Mention import org.thoughtcrime.securesms.database.model.ParentStoryId import org.thoughtcrime.securesms.database.model.StoryType +import org.thoughtcrime.securesms.mediasend.v2.UntrustedRecords import org.thoughtcrime.securesms.mms.OutgoingMediaMessage import org.thoughtcrime.securesms.sms.MessageSender @@ -24,38 +27,47 @@ object StoryGroupReplySender { } private fun sendInternal(context: Context, storyId: Long, body: CharSequence, mentions: List, isReaction: Boolean): Completable { - return Completable.create { - + val messageAndRecipient = Single.fromCallable { val message = SignalDatabase.mms.getMessageRecord(storyId) val recipient = SignalDatabase.threads.getRecipientForThreadId(message.threadId)!! - MessageSender.send( - context, - OutgoingMediaMessage( - recipient, - body.toString(), - emptyList(), - System.currentTimeMillis(), - 0, - 0L, - false, - 0, - StoryType.NONE, - ParentStoryId.GroupReply(message.id), - isReaction, - null, - emptyList(), - emptyList(), - mentions, - emptySet(), - emptySet() - ), - message.threadId, - false, - null - ) { - it.onComplete() - } + message to recipient + } + + return messageAndRecipient.flatMapCompletable { (message, recipient) -> + UntrustedRecords.checkForBadIdentityRecords(setOf(ContactSearchKey.KnownRecipient(recipient.id))) + .andThen( + Completable.create { + + MessageSender.send( + context, + OutgoingMediaMessage( + recipient, + body.toString(), + emptyList(), + System.currentTimeMillis(), + 0, + 0L, + false, + 0, + StoryType.NONE, + ParentStoryId.GroupReply(message.id), + isReaction, + null, + emptyList(), + emptyList(), + mentions, + emptySet(), + emptySet() + ), + message.threadId, + false, + null + ) { + it.onComplete() + } + } + ) }.subscribeOn(Schedulers.io()) } }