From 6d452e19ef4e9a0fe2d7b3eebd21ebe58b52cf28 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 28 Jun 2021 15:41:23 +1000 Subject: [PATCH] Detect double taps --- .../v2/messages/VisibleMessageContentView.kt | 3 +++ .../v2/messages/VisibleMessageView.kt | 23 ++++++++++++++++++- .../v2/messages/VoiceMessageView.kt | 14 +++++------ 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index 9427f9e38b..548ebb31f4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -28,6 +28,7 @@ import kotlin.math.roundToInt class VisibleMessageContentView : LinearLayout { var onContentClick: (() -> Unit)? = null + var onContentDoubleTap: (() -> Unit)? = null // region Lifecycle constructor(context: Context) : super(context) { initialize() } @@ -52,6 +53,7 @@ class VisibleMessageContentView : LinearLayout { // Body mainContainer.removeAllViews() onContentClick = null + onContentDoubleTap = null if (message is MmsMessageRecord && message.linkPreviews.isNotEmpty()) { val linkPreviewView = LinkPreviewView(context) linkPreviewView.bind(message, glide, isStartOfMessageCluster, isEndOfMessageCluster) @@ -77,6 +79,7 @@ class VisibleMessageContentView : LinearLayout { // We have to use onContentClick (rather than a click listener directly on the voice // message view) so as to not interfere with all the other gestures. onContentClick = { voiceMessageView.togglePlayback() } + onContentDoubleTap = { voiceMessageView.handleDoubleTap() } } else if (message is MmsMessageRecord && message.slideDeck.documentSlide != null) { val documentView = DocumentView(context) documentView.bind(message, VisibleMessageContentView.getTextColor(context, message)) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt index e3d7482b64..6a56fb83ab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt @@ -9,6 +9,7 @@ import android.os.Build import android.os.Handler import android.os.Looper import android.util.AttributeSet +import android.util.Log import android.view.* import android.widget.LinearLayout import androidx.core.content.ContextCompat @@ -40,8 +41,10 @@ class VisibleMessageView : LinearLayout { private var dx = 0.0f private var previousTranslationX = 0.0f private val gestureHandler = Handler(Looper.getMainLooper()) + private var pressCallback: Runnable? = null private var longPressCallback: Runnable? = null private var onDownTimestamp = 0L + private var onDoubleTap: (() -> Unit)? = null var snIsSelected = false set(value) { field = value; handleIsSelectedChanged()} var onPress: (() -> Unit)? = null @@ -52,6 +55,7 @@ class VisibleMessageView : LinearLayout { const val swipeToReplyThreshold = 80.0f // dp const val longPressMovementTreshold = 10.0f // dp const val longPressDurationThreshold = 250L // ms + const val maxDoubleTapInterval = 200L } // region Lifecycle @@ -137,6 +141,7 @@ class VisibleMessageView : LinearLayout { if (profilePictureContainer.visibility != View.GONE) { maxWidth -= profilePictureContainer.width } // Populate content view messageContentView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster, glide, maxWidth, thread) + onDoubleTap = { messageContentView.onContentDoubleTap?.invoke() } } private fun setMessageSpacing(isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean) { @@ -266,7 +271,18 @@ class VisibleMessageView : LinearLayout { onSwipeToReply?.invoke() } else if ((Date().time - onDownTimestamp) < VisibleMessageView.longPressDurationThreshold) { longPressCallback?.let { gestureHandler.removeCallbacks(it) } - onPress?.invoke() + val pressCallback = this.pressCallback + if (pressCallback != null) { + // If we're here and pressCallback isn't null, it means that we tapped again within + // maxDoubleTapInterval ms and we should count this as a double tap + gestureHandler.removeCallbacks(pressCallback) + this.pressCallback = null + onDoubleTap?.invoke() + } else { + val newPressCallback = Runnable { onPress() } + this.pressCallback = newPressCallback + gestureHandler.postDelayed(newPressCallback, VisibleMessageView.maxDoubleTapInterval) + } } resetPosition() } @@ -291,6 +307,11 @@ class VisibleMessageView : LinearLayout { onLongPress?.invoke() } + private fun onPress() { + onPress?.invoke() + pressCallback = null + } + fun onContentClick() { messageContentView.onContentClick?.invoke() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt index d63c4a9701..0cd0e8c05e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VoiceMessageView.kt @@ -2,24 +2,18 @@ package org.thoughtcrime.securesms.conversation.v2.messages import android.content.Context import android.graphics.Canvas -import android.graphics.drawable.Drawable -import android.os.Handler -import android.os.Looper import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.ViewOutlineProvider +import android.util.Log +import android.view.* import android.widget.LinearLayout import android.widget.RelativeLayout import androidx.core.view.isVisible import kotlinx.android.synthetic.main.view_voice_message.view.* import network.loki.messenger.R -import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.thoughtcrime.securesms.audio.AudioSlidePlayer import org.thoughtcrime.securesms.components.CornerMask import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities -import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.model.MmsMessageRecord -import org.thoughtcrime.securesms.mms.AudioSlide import java.util.concurrent.TimeUnit import kotlin.math.roundToInt import kotlin.math.roundToLong @@ -109,5 +103,9 @@ class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener { player.stop() } } + + fun handleDoubleTap() { + Log.d("Test", "handleDoubleTap()") + } // endregion }