Detect double taps

This commit is contained in:
Niels Andriesse 2021-06-28 15:41:23 +10:00
parent a53ce18404
commit 6d452e19ef
3 changed files with 31 additions and 9 deletions

View File

@ -28,6 +28,7 @@ import kotlin.math.roundToInt
class VisibleMessageContentView : LinearLayout { class VisibleMessageContentView : LinearLayout {
var onContentClick: (() -> Unit)? = null var onContentClick: (() -> Unit)? = null
var onContentDoubleTap: (() -> Unit)? = null
// region Lifecycle // region Lifecycle
constructor(context: Context) : super(context) { initialize() } constructor(context: Context) : super(context) { initialize() }
@ -52,6 +53,7 @@ class VisibleMessageContentView : LinearLayout {
// Body // Body
mainContainer.removeAllViews() mainContainer.removeAllViews()
onContentClick = null onContentClick = null
onContentDoubleTap = null
if (message is MmsMessageRecord && message.linkPreviews.isNotEmpty()) { if (message is MmsMessageRecord && message.linkPreviews.isNotEmpty()) {
val linkPreviewView = LinkPreviewView(context) val linkPreviewView = LinkPreviewView(context)
linkPreviewView.bind(message, glide, isStartOfMessageCluster, isEndOfMessageCluster) 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 // 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. // message view) so as to not interfere with all the other gestures.
onContentClick = { voiceMessageView.togglePlayback() } onContentClick = { voiceMessageView.togglePlayback() }
onContentDoubleTap = { voiceMessageView.handleDoubleTap() }
} else if (message is MmsMessageRecord && message.slideDeck.documentSlide != null) { } else if (message is MmsMessageRecord && message.slideDeck.documentSlide != null) {
val documentView = DocumentView(context) val documentView = DocumentView(context)
documentView.bind(message, VisibleMessageContentView.getTextColor(context, message)) documentView.bind(message, VisibleMessageContentView.getTextColor(context, message))

View File

@ -9,6 +9,7 @@ import android.os.Build
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log
import android.view.* import android.view.*
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
@ -40,8 +41,10 @@ class VisibleMessageView : LinearLayout {
private var dx = 0.0f private var dx = 0.0f
private var previousTranslationX = 0.0f private var previousTranslationX = 0.0f
private val gestureHandler = Handler(Looper.getMainLooper()) private val gestureHandler = Handler(Looper.getMainLooper())
private var pressCallback: Runnable? = null
private var longPressCallback: Runnable? = null private var longPressCallback: Runnable? = null
private var onDownTimestamp = 0L private var onDownTimestamp = 0L
private var onDoubleTap: (() -> Unit)? = null
var snIsSelected = false var snIsSelected = false
set(value) { field = value; handleIsSelectedChanged()} set(value) { field = value; handleIsSelectedChanged()}
var onPress: (() -> Unit)? = null var onPress: (() -> Unit)? = null
@ -52,6 +55,7 @@ class VisibleMessageView : LinearLayout {
const val swipeToReplyThreshold = 80.0f // dp const val swipeToReplyThreshold = 80.0f // dp
const val longPressMovementTreshold = 10.0f // dp const val longPressMovementTreshold = 10.0f // dp
const val longPressDurationThreshold = 250L // ms const val longPressDurationThreshold = 250L // ms
const val maxDoubleTapInterval = 200L
} }
// region Lifecycle // region Lifecycle
@ -137,6 +141,7 @@ class VisibleMessageView : LinearLayout {
if (profilePictureContainer.visibility != View.GONE) { maxWidth -= profilePictureContainer.width } if (profilePictureContainer.visibility != View.GONE) { maxWidth -= profilePictureContainer.width }
// Populate content view // Populate content view
messageContentView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster, glide, maxWidth, thread) messageContentView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster, glide, maxWidth, thread)
onDoubleTap = { messageContentView.onContentDoubleTap?.invoke() }
} }
private fun setMessageSpacing(isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean) { private fun setMessageSpacing(isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean) {
@ -266,7 +271,18 @@ class VisibleMessageView : LinearLayout {
onSwipeToReply?.invoke() onSwipeToReply?.invoke()
} else if ((Date().time - onDownTimestamp) < VisibleMessageView.longPressDurationThreshold) { } else if ((Date().time - onDownTimestamp) < VisibleMessageView.longPressDurationThreshold) {
longPressCallback?.let { gestureHandler.removeCallbacks(it) } 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() resetPosition()
} }
@ -291,6 +307,11 @@ class VisibleMessageView : LinearLayout {
onLongPress?.invoke() onLongPress?.invoke()
} }
private fun onPress() {
onPress?.invoke()
pressCallback = null
}
fun onContentClick() { fun onContentClick() {
messageContentView.onContentClick?.invoke() messageContentView.onContentClick?.invoke()
} }

View File

@ -2,24 +2,18 @@ package org.thoughtcrime.securesms.conversation.v2.messages
import android.content.Context import android.content.Context
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.util.Log
import android.view.ViewOutlineProvider import android.view.*
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.core.view.isVisible import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.view_voice_message.view.* import kotlinx.android.synthetic.main.view_voice_message.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
import org.thoughtcrime.securesms.audio.AudioSlidePlayer import org.thoughtcrime.securesms.audio.AudioSlidePlayer
import org.thoughtcrime.securesms.components.CornerMask import org.thoughtcrime.securesms.components.CornerMask
import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities 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.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.mms.AudioSlide
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.math.roundToLong import kotlin.math.roundToLong
@ -109,5 +103,9 @@ class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener {
player.stop() player.stop()
} }
} }
fun handleDoubleTap() {
Log.d("Test", "handleDoubleTap()")
}
// endregion // endregion
} }