Add double tap editing feature.

This commit is contained in:
mtang-signal
2024-04-25 17:24:21 -04:00
committed by Greyson Parrelli
parent 84e654efb2
commit ffc1463cda
13 changed files with 220 additions and 15 deletions

View File

@@ -40,6 +40,7 @@ import android.text.style.StyleSpan;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.TouchDelegate;
@@ -256,6 +257,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
private final UrlClickListener urlClickListener = new UrlClickListener();
private final Rect thumbnailMaskingRect = new Rect();
private final TouchDelegateChangedListener touchDelegateChangedListener = new TouchDelegateChangedListener();
private final DoubleTapEditTouchListener doubleTapEditTouchListener = new DoubleTapEditTouchListener();
private final GiftMessageViewCallback giftMessageViewCallback = new GiftMessageViewCallback();
private final Context context;
@@ -351,6 +353,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
setOnClickListener(new ClickListener(null));
bodyText.setOnTouchListener(doubleTapEditTouchListener);
bodyText.setOnLongClickListener(passthroughClickListener);
bodyText.setOnClickListener(passthroughClickListener);
footer.setOnTouchDelegateChangedListener(touchDelegateChangedListener);
@@ -2438,6 +2441,24 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
}
}
private class DoubleTapEditTouchListener implements View.OnTouchListener {
private final GestureDetector gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDoubleTap(MotionEvent e) {
if (eventListener != null && batchSelected.isEmpty()) {
eventListener.onItemDoubleClick(getMultiselectPartForLatestTouch());
return true;
}
return false;
}
});
@Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
}
private class AttachmentDownloadClickListener implements SlidesClickedListener {
@Override
public void onClick(View v, final List<Slide> slides) {

View File

@@ -277,6 +277,7 @@ class ScheduledMessagesBottomSheet : FixedRoundedCornerBottomSheetDialogFragment
override fun onShowSafetyTips(forGroup: Boolean) = Unit
override fun onReportSpamLearnMoreClicked() = Unit
override fun onMessageRequestAcceptOptionsClicked() = Unit
override fun onItemDoubleClick(item: MultiselectPart) = Unit
}
companion object {

View File

@@ -261,6 +261,7 @@ class MessageQuotesBottomSheet : FixedRoundedCornerBottomSheetDialogFragment() {
override fun onShowSafetyTips(forGroup: Boolean) = Unit
override fun onReportSpamLearnMoreClicked() = Unit
override fun onMessageRequestAcceptOptionsClicked() = Unit
override fun onItemDoubleClick(item: MultiselectPart) = Unit
}
companion object {

View File

@@ -168,6 +168,7 @@ class EditMessageHistoryDialog : FixedRoundedCornerBottomSheetDialogFragment() {
override fun onShowSafetyTips(forGroup: Boolean) = Unit
override fun onReportSpamLearnMoreClicked() = Unit
override fun onMessageRequestAcceptOptionsClicked() = Unit
override fun onItemDoubleClick(item: MultiselectPart) = Unit
}
companion object {

View File

@@ -351,7 +351,8 @@ class ConversationFragment :
ConversationBottomSheetCallback,
SafetyNumberBottomSheet.Callbacks,
EnableCallNotificationSettingsDialog.Callback,
MultiselectForwardBottomSheet.Callback {
MultiselectForwardBottomSheet.Callback,
DoubleTapEditEducationSheet.Callback {
companion object {
private val TAG = Log.tag(ConversationFragment::class.java)
@@ -2755,6 +2756,20 @@ class ConversationFragment :
RecipientBottomSheetDialogFragment.show(childFragmentManager, recipientId, groupId)
}
override fun onItemDoubleClick(item: MultiselectPart) {
Log.d(TAG, "onItemDoubleClick")
if (!isValidEditMessageSend(item.getMessageRecord(), System.currentTimeMillis())) {
return
}
if (SignalStore.uiHints().hasSeenDoubleTapEditEducationSheet) {
onDoubleTapEditEducationSheetNext(item.conversationMessage)
return
}
DoubleTapEditEducationSheet(item).show(childFragmentManager, DoubleTapEditEducationSheet.KEY)
}
override fun onMessageWithErrorClicked(messageRecord: MessageRecord) {
val recipientId = viewModel.recipientSnapshot?.id ?: return
if (messageRecord.isIdentityMismatchFailure) {
@@ -4307,4 +4322,8 @@ class ConversationFragment :
}
}
}
override fun onDoubleTapEditEducationSheetNext(conversationMessage: ConversationMessage) {
handleEditMessage(conversationMessage)
}
}

View File

@@ -0,0 +1,48 @@
package org.thoughtcrime.securesms.conversation.v2
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.button.MaterialButton
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.FixedRoundedCornerBottomSheetDialogFragment
import org.thoughtcrime.securesms.conversation.ConversationMessage
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.fragments.requireListener
/**
* Shows an education sheet to users explaining how double tapping a sent message within 24hrs will allow them to edit it
*/
class DoubleTapEditEducationSheet(private val item: MultiselectPart) : FixedRoundedCornerBottomSheetDialogFragment() {
companion object {
const val KEY = "DOUBLE_TAP_EDIT_EDU"
}
override val peekHeightPercentage: Float = 1f
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.conversation_item_double_tap_edit_education_sheet, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
SignalStore.uiHints().hasSeenDoubleTapEditEducationSheet = true
view.findViewById<MaterialButton>(R.id.got_it).setOnClickListener {
requireListener<Callback>().onDoubleTapEditEducationSheetNext(item.conversationMessage)
dismissAllowingStateLoss()
}
}
override fun onCancel(dialog: DialogInterface) {
super.onCancel(dialog)
requireListener<Callback>().onDoubleTapEditEducationSheetNext(item.conversationMessage)
}
interface Callback {
fun onDoubleTapEditEducationSheetNext(conversationMessage: ConversationMessage)
}
}

View File

@@ -18,6 +18,8 @@ import android.text.style.ClickableSpan
import android.text.style.ForegroundColorSpan
import android.text.style.URLSpan
import android.util.TypedValue
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
@@ -110,6 +112,19 @@ open class V2ConversationItemTextOnlyViewHolder<Model : MappingModel<Model>>(
private val senderDrawable = ChatColorsDrawable(conversationContext::getChatColorsData)
private val bodyBubbleLayoutTransition = BodyBubbleLayoutTransition()
private val gestureDetector = GestureDetector(
context,
object : GestureDetector.SimpleOnGestureListener() {
override fun onDoubleTap(e: MotionEvent): Boolean {
if (conversationContext.selectedItems.isEmpty()) {
conversationContext.clickListener.onItemDoubleClick(getMultiselectPartForLatestTouch())
return true
}
return false
}
}
)
protected lateinit var shape: V2ConversationItemShape.MessageShape
private val replyDelegate = object : V2ConversationItemLayout.OnMeasureListener {
@@ -139,6 +154,7 @@ open class V2ConversationItemTextOnlyViewHolder<Model : MappingModel<Model>>(
)
}
binding.body.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event) }
binding.root.setOnClickListener { onBubbleClicked() }
binding.root.setOnLongClickListener {
conversationContext.clickListener.onItemLongClick(binding.root, getMultiselectPartForLatestTouch())