Add media send support to CFV2.

This commit is contained in:
Alex Hart
2023-06-21 13:02:19 -03:00
committed by Nicholas Tinsley
parent 53673be5cb
commit 09d17659b9
3 changed files with 125 additions and 18 deletions

View File

@@ -110,6 +110,7 @@ import org.thoughtcrime.securesms.components.mention.MentionAnnotation
import org.thoughtcrime.securesms.components.menu.ActionItem import org.thoughtcrime.securesms.components.menu.ActionItem
import org.thoughtcrime.securesms.components.menu.SignalBottomActionBar import org.thoughtcrime.securesms.components.menu.SignalBottomActionBar
import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager import org.thoughtcrime.securesms.components.recyclerview.SmoothScrollingLinearLayoutManager
import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.DonateToSignalFragment import org.thoughtcrime.securesms.components.settings.app.subscription.donate.DonateToSignalFragment
import org.thoughtcrime.securesms.components.settings.app.subscription.donate.DonateToSignalType import org.thoughtcrime.securesms.components.settings.app.subscription.donate.DonateToSignalType
import org.thoughtcrime.securesms.components.settings.conversation.ConversationSettingsActivity import org.thoughtcrime.securesms.components.settings.conversation.ConversationSettingsActivity
@@ -199,19 +200,21 @@ import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory.create
import org.thoughtcrime.securesms.mediapreview.MediaPreviewV2Activity import org.thoughtcrime.securesms.mediapreview.MediaPreviewV2Activity
import org.thoughtcrime.securesms.mediasend.Media import org.thoughtcrime.securesms.mediasend.Media
import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult import org.thoughtcrime.securesms.mediasend.MediaSendActivityResult
import org.thoughtcrime.securesms.mediasend.v2.MediaSelectionActivity
import org.thoughtcrime.securesms.messagedetails.MessageDetailsFragment import org.thoughtcrime.securesms.messagedetails.MessageDetailsFragment
import org.thoughtcrime.securesms.messagerequests.MessageRequestRepository import org.thoughtcrime.securesms.messagerequests.MessageRequestRepository
import org.thoughtcrime.securesms.messagerequests.MessageRequestState import org.thoughtcrime.securesms.messagerequests.MessageRequestState
import org.thoughtcrime.securesms.mms.AttachmentManager import org.thoughtcrime.securesms.mms.AttachmentManager
import org.thoughtcrime.securesms.mms.AudioSlide import org.thoughtcrime.securesms.mms.AudioSlide
import org.thoughtcrime.securesms.mms.GifSlide
import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.ImageSlide
import org.thoughtcrime.securesms.mms.MediaConstraints import org.thoughtcrime.securesms.mms.MediaConstraints
import org.thoughtcrime.securesms.mms.QuoteModel import org.thoughtcrime.securesms.mms.QuoteModel
import org.thoughtcrime.securesms.mms.Slide import org.thoughtcrime.securesms.mms.Slide
import org.thoughtcrime.securesms.mms.SlideDeck import org.thoughtcrime.securesms.mms.SlideDeck
import org.thoughtcrime.securesms.mms.SlideFactory import org.thoughtcrime.securesms.mms.SlideFactory
import org.thoughtcrime.securesms.mms.StickerSlide import org.thoughtcrime.securesms.mms.StickerSlide
import org.thoughtcrime.securesms.mms.VideoSlide
import org.thoughtcrime.securesms.notifications.v2.ConversationId import org.thoughtcrime.securesms.notifications.v2.ConversationId
import org.thoughtcrime.securesms.payments.preferences.PaymentsActivity import org.thoughtcrime.securesms.payments.preferences.PaymentsActivity
import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.permissions.Permissions
@@ -229,6 +232,7 @@ import org.thoughtcrime.securesms.registration.RegistrationNavigationActivity
import org.thoughtcrime.securesms.revealable.ViewOnceMessageActivity import org.thoughtcrime.securesms.revealable.ViewOnceMessageActivity
import org.thoughtcrime.securesms.revealable.ViewOnceUtil import org.thoughtcrime.securesms.revealable.ViewOnceUtil
import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet import org.thoughtcrime.securesms.safety.SafetyNumberBottomSheet
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.stickers.StickerLocator import org.thoughtcrime.securesms.stickers.StickerLocator
import org.thoughtcrime.securesms.stickers.StickerPackInstallEvent import org.thoughtcrime.securesms.stickers.StickerPackInstallEvent
import org.thoughtcrime.securesms.stickers.StickerPackPreviewActivity import org.thoughtcrime.securesms.stickers.StickerPackPreviewActivity
@@ -240,6 +244,7 @@ import org.thoughtcrime.securesms.util.CommunicationActions
import org.thoughtcrime.securesms.util.ContextUtil import org.thoughtcrime.securesms.util.ContextUtil
import org.thoughtcrime.securesms.util.Debouncer import org.thoughtcrime.securesms.util.Debouncer
import org.thoughtcrime.securesms.util.DeleteDialog import org.thoughtcrime.securesms.util.DeleteDialog
import org.thoughtcrime.securesms.util.Dialogs
import org.thoughtcrime.securesms.util.DrawableUtil import org.thoughtcrime.securesms.util.DrawableUtil
import org.thoughtcrime.securesms.util.FeatureFlags import org.thoughtcrime.securesms.util.FeatureFlags
import org.thoughtcrime.securesms.util.FullscreenHelper import org.thoughtcrime.securesms.util.FullscreenHelper
@@ -1190,7 +1195,9 @@ class ConversationFragment :
slideDeck: SlideDeck? = if (attachmentManager.isAttachmentPresent) attachmentManager.buildSlideDeck() else null, slideDeck: SlideDeck? = if (attachmentManager.isAttachmentPresent) attachmentManager.buildSlideDeck() else null,
contacts: List<Contact> = emptyList(), contacts: List<Contact> = emptyList(),
clearCompose: Boolean = true, clearCompose: Boolean = true,
linkPreviews: List<LinkPreview> = linkPreviewViewModel.onSend() linkPreviews: List<LinkPreview> = linkPreviewViewModel.onSend(),
preUploadResults: List<MessageSender.PreUploadResult> = emptyList(),
afterSendComplete: () -> Unit = {}
) { ) {
val metricId = viewModel.recipientSnapshot?.let { if (it.isGroup == true) SignalLocalMetrics.GroupMessageSend.start() else SignalLocalMetrics.IndividualMessageSend.start() } val metricId = viewModel.recipientSnapshot?.let { if (it.isGroup == true) SignalLocalMetrics.GroupMessageSend.start() else SignalLocalMetrics.IndividualMessageSend.start() }
@@ -1204,7 +1211,8 @@ class ConversationFragment :
mentions = mentions, mentions = mentions,
bodyRanges = bodyRanges, bodyRanges = bodyRanges,
contacts = contacts, contacts = contacts,
linkPreviews = linkPreviews linkPreviews = linkPreviews,
preUploadResults = preUploadResults
) )
disposables += send disposables += send
@@ -1222,7 +1230,10 @@ class ConversationFragment :
is RecipientFormattingException -> toast(R.string.ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation, Toast.LENGTH_LONG) is RecipientFormattingException -> toast(R.string.ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation, Toast.LENGTH_LONG)
} }
}, },
onComplete = this::onSendComplete onComplete = {
onSendComplete()
afterSendComplete()
}
) )
} }
@@ -2501,7 +2512,70 @@ class ConversationFragment :
} }
override fun onMediaSend(result: MediaSendActivityResult) { override fun onMediaSend(result: MediaSendActivityResult) {
// TODO [cfv2] media send val recipientSnapshot = viewModel.recipientSnapshot
if (result.recipientId != recipientSnapshot?.id) {
Log.w(TAG, "Result's recipientId did not match ours! Result: " + result.recipientId + ", Ours: " + recipientSnapshot?.id)
toast(R.string.ConversationActivity_error_sending_media)
return
}
if (result.isPushPreUpload) {
sendPreUploadMediaMessage(result)
return
}
val slides: List<Slide> = result.nonUploadedMedia.mapNotNull {
when {
MediaUtil.isVideoType(it.mimeType) -> VideoSlide(requireContext(), it.uri, it.size, it.isVideoGif, it.width, it.height, it.caption.orNull(), it.transformProperties.orNull())
MediaUtil.isGif(it.mimeType) -> GifSlide(requireContext(), it.uri, it.size, it.width, it.height, it.isBorderless, it.caption.orNull())
MediaUtil.isImageType(it.mimeType) -> ImageSlide(requireContext(), it.uri, it.mimeType, it.size, it.width, it.height, it.isBorderless, it.caption.orNull(), null, it.transformProperties.orNull())
else -> {
Log.w(TAG, "Asked to send an unexpected mimeType: '${it.mimeType}'. Skipping.")
null
}
}
}
sendMessage(
body = result.body,
mentions = result.mentions,
bodyRanges = result.bodyRanges,
messageToEdit = null,
quote = if (result.isViewOnce) null else inputPanel.quote.orNull(),
scheduledDate = result.scheduledTime,
slideDeck = SlideDeck().apply { slides.forEach { addSlide(it) } },
contacts = emptyList(),
clearCompose = true,
linkPreviews = emptyList()
) {
viewModel.deleteSlideData(slides)
}
}
private fun sendPreUploadMediaMessage(result: MediaSendActivityResult) {
if (ExpiredBuildReminder.isEligible()) {
/* TODO [cfv2] -- Show expired dialog */
return
}
if (SignalStore.uiHints().hasNotSeenTextFormattingAlert() && result.bodyRanges != null && result.bodyRanges.rangesCount > 0) {
Dialogs.showFormattedTextDialog(requireContext()) { sendPreUploadMediaMessage(result) }
return
}
sendMessage(
body = result.body,
mentions = result.mentions,
bodyRanges = result.bodyRanges,
messageToEdit = null,
quote = if (result.isViewOnce) null else inputPanel.quote.orNull(),
scheduledDate = result.scheduledTime,
slideDeck = null,
contacts = emptyList(),
clearCompose = true,
linkPreviews = emptyList(),
preUploadResults = result.preUploadResults
)
} }
} }
@@ -2951,7 +3025,7 @@ class ConversationFragment :
AttachmentKeyboardButton.PAYMENT -> AttachmentManager.selectPayment(this@ConversationFragment, viewModel.recipientSnapshot!!) AttachmentKeyboardButton.PAYMENT -> AttachmentManager.selectPayment(this@ConversationFragment, viewModel.recipientSnapshot!!)
} }
} else if (media != null) { } else if (media != null) {
startActivityForResult(MediaSelectionActivity.editor(requireActivity(), sendButton.selectedSendType, listOf(media), viewModel.recipientSnapshot!!.id, composeText.textTrimmed), 12) conversationActivityResultContracts.launchMediaEditor(listOf(media), viewModel.recipientSnapshot!!.id, composeText.textTrimmed)
} }
container.hideInput() container.hideInput()

View File

@@ -79,6 +79,7 @@ import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.mms.OutgoingMessage import org.thoughtcrime.securesms.mms.OutgoingMessage
import org.thoughtcrime.securesms.mms.PartAuthority import org.thoughtcrime.securesms.mms.PartAuthority
import org.thoughtcrime.securesms.mms.QuoteModel import org.thoughtcrime.securesms.mms.QuoteModel
import org.thoughtcrime.securesms.mms.Slide
import org.thoughtcrime.securesms.mms.SlideDeck import org.thoughtcrime.securesms.mms.SlideDeck
import org.thoughtcrime.securesms.profiles.spoofing.ReviewUtil import org.thoughtcrime.securesms.profiles.spoofing.ReviewUtil
import org.thoughtcrime.securesms.providers.BlobProvider import org.thoughtcrime.securesms.providers.BlobProvider
@@ -87,6 +88,7 @@ import org.thoughtcrime.securesms.recipients.RecipientFormattingException
import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.search.MessageResult import org.thoughtcrime.securesms.search.MessageResult
import org.thoughtcrime.securesms.sms.MessageSender import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.sms.MessageSender.PreUploadResult
import org.thoughtcrime.securesms.util.BitmapUtil import org.thoughtcrime.securesms.util.BitmapUtil
import org.thoughtcrime.securesms.util.DrawableUtil import org.thoughtcrime.securesms.util.DrawableUtil
import org.thoughtcrime.securesms.util.MediaUtil import org.thoughtcrime.securesms.util.MediaUtil
@@ -196,10 +198,11 @@ class ConversationRepository(
mentions: List<Mention>, mentions: List<Mention>,
bodyRanges: BodyRangeList?, bodyRanges: BodyRangeList?,
contacts: List<Contact>, contacts: List<Contact>,
linkPreviews: List<LinkPreview> linkPreviews: List<LinkPreview>,
preUploadResults: List<PreUploadResult>
): Completable { ): Completable {
val sendCompletable = Completable.create { emitter -> val sendCompletable = Completable.create { emitter ->
if (body.isEmpty() && slideDeck?.containsMediaSlide() != true) { if (body.isEmpty() && slideDeck?.containsMediaSlide() != true && preUploadResults.isEmpty()) {
emitter.onError(InvalidMessageException("Message is empty!")) emitter.onError(InvalidMessageException("Message is empty!"))
return@create return@create
} }
@@ -225,14 +228,25 @@ class ConversationRepository(
linkPreviews = linkPreviews linkPreviews = linkPreviews
) )
MessageSender.send( if (preUploadResults.isEmpty()) {
ApplicationDependencies.getApplication(), MessageSender.send(
message, ApplicationDependencies.getApplication(),
threadId, message,
MessageSender.SendType.SIGNAL, threadId,
metricId MessageSender.SendType.SIGNAL,
) { metricId
emitter.onComplete() ) {
emitter.onComplete()
}
} else {
MessageSender.sendPushWithPreUploadedMedia(
ApplicationDependencies.getApplication(),
message,
preUploadResults,
threadId
) {
emitter.onComplete()
}
} }
} }
@@ -526,6 +540,17 @@ class ConversationRepository(
return oldConversationRepository.resolveMessageToEdit(conversationMessage) return oldConversationRepository.resolveMessageToEdit(conversationMessage)
} }
fun deleteSlideData(slides: List<Slide>) {
SignalExecutors.BOUNDED_IO.execute {
slides
.mapNotNull(Slide::getUri)
.filter(BlobProvider::isAuthority)
.forEach {
BlobProvider.getInstance().delete(applicationContext, it)
}
}
}
/** /**
* Glide target for a contact photo which expects an error drawable, and publishes * Glide target for a contact photo which expects an error drawable, and publishes
* the result to the given emitter. * the result to the given emitter.

View File

@@ -51,10 +51,12 @@ import org.thoughtcrime.securesms.messagerequests.MessageRequestRepository
import org.thoughtcrime.securesms.messagerequests.MessageRequestState import org.thoughtcrime.securesms.messagerequests.MessageRequestState
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.mms.QuoteModel import org.thoughtcrime.securesms.mms.QuoteModel
import org.thoughtcrime.securesms.mms.Slide
import org.thoughtcrime.securesms.mms.SlideDeck import org.thoughtcrime.securesms.mms.SlideDeck
import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.search.MessageResult import org.thoughtcrime.securesms.search.MessageResult
import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.util.hasGiftBadge import org.thoughtcrime.securesms.util.hasGiftBadge
import org.thoughtcrime.securesms.util.rx.RxStore import org.thoughtcrime.securesms.util.rx.RxStore
@@ -299,7 +301,8 @@ class ConversationViewModel(
mentions: List<Mention>, mentions: List<Mention>,
bodyRanges: BodyRangeList?, bodyRanges: BodyRangeList?,
contacts: List<Contact>, contacts: List<Contact>,
linkPreviews: List<LinkPreview> linkPreviews: List<LinkPreview>,
preUploadResults: List<MessageSender.PreUploadResult>
): Completable { ): Completable {
return repository.sendMessage( return repository.sendMessage(
threadId = threadId, threadId = threadId,
@@ -313,7 +316,8 @@ class ConversationViewModel(
mentions = mentions, mentions = mentions,
bodyRanges = bodyRanges, bodyRanges = bodyRanges,
contacts = contacts, contacts = contacts,
linkPreviews = linkPreviews linkPreviews = linkPreviews,
preUploadResults = preUploadResults
).observeOn(AndroidSchedulers.mainThread()) ).observeOn(AndroidSchedulers.mainThread())
} }
@@ -354,4 +358,8 @@ class ConversationViewModel(
fun resolveMessageToEdit(conversationMessage: ConversationMessage): Single<ConversationMessage> { fun resolveMessageToEdit(conversationMessage: ConversationMessage): Single<ConversationMessage> {
return repository.resolveMessageToEdit(conversationMessage) return repository.resolveMessageToEdit(conversationMessage)
} }
fun deleteSlideData(slides: List<Slide>) {
repository.deleteSlideData(slides)
}
} }