mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-26 12:44:38 +00:00
CFV2 Add reply to message support.
This commit is contained in:
committed by
Cody Henthorne
parent
2bef5653b4
commit
5d546f46e4
@@ -29,8 +29,8 @@ public class ConversationItemSwipeCallback extends ItemTouchHelper.SimpleCallbac
|
||||
private final ConversationItemTouchListener itemTouchListener;
|
||||
private final OnSwipeListener onSwipeListener;
|
||||
|
||||
ConversationItemSwipeCallback(@NonNull SwipeAvailabilityProvider swipeAvailabilityProvider,
|
||||
@NonNull OnSwipeListener onSwipeListener)
|
||||
public ConversationItemSwipeCallback(@NonNull SwipeAvailabilityProvider swipeAvailabilityProvider,
|
||||
@NonNull OnSwipeListener onSwipeListener)
|
||||
{
|
||||
super(0, ItemTouchHelper.END);
|
||||
this.itemTouchListener = new ConversationItemTouchListener(this::updateLatestDownCoordinate);
|
||||
@@ -40,7 +40,7 @@ public class ConversationItemSwipeCallback extends ItemTouchHelper.SimpleCallbac
|
||||
this.canTriggerSwipe = true;
|
||||
}
|
||||
|
||||
void attachToRecyclerView(@NonNull RecyclerView recyclerView) {
|
||||
public void attachToRecyclerView(@NonNull RecyclerView recyclerView) {
|
||||
recyclerView.addOnItemTouchListener(itemTouchListener);
|
||||
new ItemTouchHelper(this).attachToRecyclerView(recyclerView);
|
||||
}
|
||||
@@ -193,11 +193,11 @@ public class ConversationItemSwipeCallback extends ItemTouchHelper.SimpleCallbac
|
||||
if (vibrator != null) vibrator.vibrate(SWIPE_SUCCESS_VIBE_TIME_MS);
|
||||
}
|
||||
|
||||
interface SwipeAvailabilityProvider {
|
||||
public interface SwipeAvailabilityProvider {
|
||||
boolean isSwipeAvailable(ConversationMessage conversationMessage);
|
||||
}
|
||||
|
||||
interface OnSwipeListener {
|
||||
public interface OnSwipeListener {
|
||||
void onSwipe(ConversationMessage conversationMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,11 +197,11 @@ public final class MenuState {
|
||||
.allMatch(collection -> multiselectParts.containsAll(collection.toSet()));
|
||||
}
|
||||
|
||||
static boolean canReplyToMessage(@NonNull Recipient conversationRecipient,
|
||||
boolean actionMessage,
|
||||
@NonNull MessageRecord messageRecord,
|
||||
boolean isDisplayingMessageRequest,
|
||||
boolean isNonAdminInAnnouncementGroup)
|
||||
public static boolean canReplyToMessage(@NonNull Recipient conversationRecipient,
|
||||
boolean actionMessage,
|
||||
@NonNull MessageRecord messageRecord,
|
||||
boolean isDisplayingMessageRequest,
|
||||
boolean isNonAdminInAnnouncementGroup)
|
||||
{
|
||||
return !actionMessage &&
|
||||
!isNonAdminInAnnouncementGroup &&
|
||||
@@ -215,7 +215,7 @@ public final class MenuState {
|
||||
!conversationRecipient.isReleaseNotes();
|
||||
}
|
||||
|
||||
static boolean isActionMessage(@NonNull MessageRecord messageRecord) {
|
||||
public static boolean isActionMessage(@NonNull MessageRecord messageRecord) {
|
||||
return messageRecord.isInMemoryMessageRecord() || messageRecord.isUpdate();
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@ import org.thoughtcrime.securesms.conversation.ConversationIntents
|
||||
import org.thoughtcrime.securesms.conversation.ConversationIntents.ConversationScreenType
|
||||
import org.thoughtcrime.securesms.conversation.ConversationItem
|
||||
import org.thoughtcrime.securesms.conversation.ConversationItemSelection
|
||||
import org.thoughtcrime.securesms.conversation.ConversationItemSwipeCallback
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage
|
||||
import org.thoughtcrime.securesms.conversation.ConversationOptionsMenu
|
||||
import org.thoughtcrime.securesms.conversation.ConversationReactionDelegate
|
||||
@@ -221,6 +222,7 @@ import org.thoughtcrime.securesms.util.WindowUtil
|
||||
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture
|
||||
import org.thoughtcrime.securesms.util.doAfterNextLayout
|
||||
import org.thoughtcrime.securesms.util.fragments.requireListener
|
||||
import org.thoughtcrime.securesms.util.getRecordQuoteType
|
||||
import org.thoughtcrime.securesms.util.hasAudio
|
||||
import org.thoughtcrime.securesms.util.hasGiftBadge
|
||||
import org.thoughtcrime.securesms.util.viewModel
|
||||
@@ -546,6 +548,11 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
|
||||
.getRequestReviewState()
|
||||
.subscribeBy { presentRequestReviewState(it) }
|
||||
.addTo(disposables)
|
||||
|
||||
ConversationItemSwipeCallback(
|
||||
SwipeAvailabilityProvider(),
|
||||
this::handleReplyToMessage
|
||||
).attachToRecyclerView(binding.conversationItemRecycler)
|
||||
}
|
||||
|
||||
private fun presentInputReadyState(inputReadyState: InputReadyState) {
|
||||
@@ -893,6 +900,7 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
|
||||
disposables += send
|
||||
.doOnSubscribe {
|
||||
composeText.setText("")
|
||||
inputPanel.clearQuote()
|
||||
}
|
||||
.subscribeBy(
|
||||
onError = { t ->
|
||||
@@ -1139,7 +1147,30 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
|
||||
//region Message action handling
|
||||
|
||||
private fun handleReplyToMessage(conversationMessage: ConversationMessage) {
|
||||
// TODO [cfv2] -- Not implemented yet.
|
||||
/*
|
||||
TODO [cfv2]
|
||||
if (isSearchRequested) {
|
||||
searchViewItem.collapseActionView();
|
||||
}
|
||||
*/
|
||||
|
||||
if (inputPanel.inEditMessageMode()) {
|
||||
inputPanel.exitEditMessageMode()
|
||||
}
|
||||
|
||||
val (slideDeck, body) = viewModel.getSlideDeckAndBodyForReply(requireContext(), conversationMessage)
|
||||
val author = conversationMessage.messageRecord.fromRecipient
|
||||
|
||||
inputPanel.setQuote(
|
||||
GlideApp.with(this),
|
||||
conversationMessage.messageRecord.dateSent,
|
||||
author,
|
||||
body,
|
||||
slideDeck,
|
||||
conversationMessage.messageRecord.getRecordQuoteType()
|
||||
)
|
||||
|
||||
inputPanel.clickOnComposeInput()
|
||||
}
|
||||
|
||||
private fun handleEditMessage(conversationMessage: ConversationMessage) {
|
||||
@@ -1207,6 +1238,20 @@ class ConversationFragment : LoggingFragment(R.layout.v2_conversation_fragment)
|
||||
).subscribe()
|
||||
}
|
||||
|
||||
private inner class SwipeAvailabilityProvider : ConversationItemSwipeCallback.SwipeAvailabilityProvider {
|
||||
override fun isSwipeAvailable(conversationMessage: ConversationMessage): Boolean {
|
||||
val recipient = viewModel.recipientSnapshot ?: return false
|
||||
|
||||
return actionMode == null && MenuState.canReplyToMessage(
|
||||
recipient,
|
||||
MenuState.isActionMessage(conversationMessage.messageRecord),
|
||||
conversationMessage.messageRecord,
|
||||
viewModel.hasMessageRequestState,
|
||||
conversationGroupViewModel.isNonAdminInAnnouncementGroup()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region Scroll Handling
|
||||
|
||||
@@ -32,6 +32,8 @@ import org.signal.paging.PagedData
|
||||
import org.signal.paging.PagingConfig
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.ShortcutLauncherActivity
|
||||
import org.thoughtcrime.securesms.attachments.TombstoneAttachment
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiStrings
|
||||
import org.thoughtcrime.securesms.components.reminder.BubbleOptOutReminder
|
||||
import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder
|
||||
import org.thoughtcrime.securesms.components.reminder.GroupsV1MigrationSuggestionsReminder
|
||||
@@ -39,6 +41,8 @@ import org.thoughtcrime.securesms.components.reminder.PendingGroupJoinRequestsRe
|
||||
import org.thoughtcrime.securesms.components.reminder.Reminder
|
||||
import org.thoughtcrime.securesms.components.reminder.ServiceOutageReminder
|
||||
import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder
|
||||
import org.thoughtcrime.securesms.contactshare.Contact
|
||||
import org.thoughtcrime.securesms.contactshare.ContactUtil
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage
|
||||
import org.thoughtcrime.securesms.conversation.colors.GroupAuthorNameColorHelper
|
||||
import org.thoughtcrime.securesms.conversation.colors.NameColor
|
||||
@@ -80,9 +84,13 @@ import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.sms.MessageSender
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil
|
||||
import org.thoughtcrime.securesms.util.DrawableUtil
|
||||
import org.thoughtcrime.securesms.util.MediaUtil
|
||||
import org.thoughtcrime.securesms.util.SignalLocalMetrics
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.thoughtcrime.securesms.util.hasLinkPreview
|
||||
import org.thoughtcrime.securesms.util.hasSharedContact
|
||||
import org.thoughtcrime.securesms.util.hasTextSlide
|
||||
import org.thoughtcrime.securesms.util.isViewOnceMessage
|
||||
import org.thoughtcrime.securesms.util.requireTextSlide
|
||||
import java.io.IOException
|
||||
import java.util.Optional
|
||||
@@ -432,6 +440,46 @@ class ConversationRepository(
|
||||
.subscribeOn(Schedulers.computation())
|
||||
}
|
||||
|
||||
fun getSlideDeckAndBodyForReply(context: Context, conversationMessage: ConversationMessage): Pair<SlideDeck, CharSequence> {
|
||||
val messageRecord = conversationMessage.messageRecord
|
||||
|
||||
return if (messageRecord.isMms && messageRecord.hasSharedContact()) {
|
||||
val contact: Contact = (messageRecord as MmsMessageRecord).sharedContacts.first()
|
||||
val displayName: String = ContactUtil.getDisplayName(contact)
|
||||
val body: String = context.getString(R.string.ConversationActivity_quoted_contact_message, EmojiStrings.BUST_IN_SILHOUETTE, displayName)
|
||||
val slideDeck = SlideDeck()
|
||||
|
||||
if (contact.avatarAttachment != null) {
|
||||
slideDeck.addSlide(MediaUtil.getSlideForAttachment(context, contact.avatarAttachment))
|
||||
}
|
||||
|
||||
slideDeck to body
|
||||
} else if (messageRecord.isMms && messageRecord.hasLinkPreview()) {
|
||||
val linkPreview = (messageRecord as MmsMessageRecord).linkPreviews.first()
|
||||
val slideDeck = SlideDeck()
|
||||
|
||||
linkPreview.thumbnail.ifPresent {
|
||||
slideDeck.addSlide(MediaUtil.getSlideForAttachment(context, it))
|
||||
}
|
||||
|
||||
slideDeck to conversationMessage.getDisplayBody(context)
|
||||
} else {
|
||||
var slideDeck = if (messageRecord.isMms) {
|
||||
(messageRecord as MmsMessageRecord).slideDeck
|
||||
} else {
|
||||
SlideDeck()
|
||||
}
|
||||
|
||||
if (messageRecord.isViewOnceMessage()) {
|
||||
val attachment = TombstoneAttachment(MediaUtil.VIEW_ONCE, true)
|
||||
slideDeck = SlideDeck()
|
||||
slideDeck.addSlide(MediaUtil.getSlideForAttachment(context, attachment))
|
||||
}
|
||||
|
||||
slideDeck to conversationMessage.getDisplayBody(context)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Glide target for a contact photo which expects an error drawable, and publishes
|
||||
* the result to the given emitter.
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.signal.core.util.concurrent.subscribeWithSubject
|
||||
import org.signal.core.util.orNull
|
||||
import org.signal.paging.ProxyPagingController
|
||||
import org.thoughtcrime.securesms.components.reminder.Reminder
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage
|
||||
import org.thoughtcrime.securesms.conversation.colors.GroupAuthorNameColorHelper
|
||||
import org.thoughtcrime.securesms.conversation.colors.NameColor
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart
|
||||
@@ -290,4 +291,8 @@ class ConversationViewModel(
|
||||
.distinctUntilChanged()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
fun getSlideDeckAndBodyForReply(context: Context, conversationMessage: ConversationMessage): Pair<SlideDeck, CharSequence> {
|
||||
return repository.getSlideDeckAndBodyForReply(context, conversationMessage)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user