diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index d7e845d858..03378a99d8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -2,19 +2,23 @@ package org.thoughtcrime.securesms.conversation.v2 import android.animation.FloatEvaluator import android.animation.ValueAnimator +import android.content.res.Resources import android.database.Cursor import android.os.Bundle +import android.util.Log import android.view.ActionMode import android.view.Menu import android.view.MenuItem +import android.view.MotionEvent import android.widget.RelativeLayout -import androidx.core.view.isVisible import androidx.loader.app.LoaderManager import androidx.loader.content.Loader import androidx.recyclerview.widget.LinearLayoutManager import kotlinx.android.synthetic.main.activity_conversation_v2.* +import kotlinx.android.synthetic.main.activity_conversation_v2.view.* import kotlinx.android.synthetic.main.activity_conversation_v2_action_bar.* import kotlinx.android.synthetic.main.view_input_bar.view.* +import kotlinx.android.synthetic.main.view_input_bar_recording.view.* import network.loki.messenger.R import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarDelegate @@ -22,8 +26,9 @@ import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCa import org.thoughtcrime.securesms.conversation.v2.menus.ConversationMenuHelper import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.model.MessageRecord -import org.thoughtcrime.securesms.loki.views.NewConversationButtonSetView import org.thoughtcrime.securesms.mms.GlideApp +import kotlin.math.abs +import kotlin.math.sqrt class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDelegate { private var threadID: Long = -1 @@ -57,6 +62,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe private val glide by lazy { GlideApp.with(this) } + private val screenWidth by lazy { + Resources.getSystem().displayMetrics.widthPixels + } + // region Settings companion object { const val THREAD_ID = "thread_id" @@ -167,5 +176,45 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe } } } + + override fun onMicrophoneButtonMove(event: MotionEvent) { + val rawX = event.rawX + val chevronImageView = inputBarRecordingView.inputBarChevronImageView + val slideToCancelTextView = inputBarRecordingView.inputBarSlideToCancelTextView + if (rawX < screenWidth / 2) { + val translationX = rawX - screenWidth / 2 + val sign = -1.0f + val chevronDamping = 4.0f + val labelDamping = 3.0f + val chevronX = (chevronDamping * (sqrt(abs(translationX)) / sqrt(chevronDamping))) * sign + val labelX = (labelDamping * (sqrt(abs(translationX)) / sqrt(labelDamping))) * sign + chevronImageView.translationX = chevronX + slideToCancelTextView.translationX = labelX + } else { + chevronImageView.translationX = 0.0f + slideToCancelTextView.translationX = 0.0f + } + } + + override fun onMicrophoneButtonCancel(event: MotionEvent) { + resetVoiceMessageUI() + } + + override fun onMicrophoneButtonUp(event: MotionEvent) { + resetVoiceMessageUI() + } + + private fun resetVoiceMessageUI() { + val chevronImageView = inputBarRecordingView.inputBarChevronImageView + val slideToCancelTextView = inputBarRecordingView.inputBarSlideToCancelTextView + listOf( chevronImageView, slideToCancelTextView ).forEach { view -> + val animation = ValueAnimator.ofObject(FloatEvaluator(), view.translationX, 0.0f) + animation.duration = 250L + animation.addUpdateListener { animator -> + view.translationX = animator.animatedValue as Float + } + animation.start() + } + } // endregion } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt index 00af1e6198..86ff46a647 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBar.kt @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.conversation.v2.input_bar import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater +import android.view.MotionEvent import android.widget.RelativeLayout import androidx.core.view.isVisible import kotlinx.android.synthetic.main.view_input_bar.view.* @@ -34,6 +35,9 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate { microphoneButton.onLongPress = { showVoiceMessageUI() } + microphoneButton.onMove = { delegate?.onMicrophoneButtonMove(it) } + microphoneButton.onCancel = { delegate?.onMicrophoneButtonCancel(it) } + microphoneButton.onUp = { delegate?.onMicrophoneButtonUp(it) } // Send button microphoneOrSendButtonContainer.addView(sendButton) sendButton.layoutParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT) @@ -69,4 +73,7 @@ interface InputBarDelegate { fun inputBarHeightChanged(newValue: Int) fun showVoiceMessageUI() + fun onMicrophoneButtonMove(event: MotionEvent) + fun onMicrophoneButtonCancel(event: MotionEvent) + fun onMicrophoneButtonUp(event: MotionEvent) } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarButton.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarButton.kt index f42273b482..08a2ece80c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarButton.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarButton.kt @@ -32,6 +32,9 @@ class InputBarButton : RelativeLayout { private var onDownTimestamp = 0L var onPress: (() -> Unit)? = null + var onMove: ((MotionEvent) -> Unit)? = null + var onCancel: ((MotionEvent) -> Unit)? = null + var onUp: ((MotionEvent) -> Unit)? = null var onLongPress: (() -> Unit)? = null companion object { @@ -112,6 +115,7 @@ class InputBarButton : RelativeLayout { override fun onTouchEvent(event: MotionEvent): Boolean { when (event.action) { MotionEvent.ACTION_DOWN -> onDown(event) + MotionEvent.ACTION_MOVE -> onMove(event) MotionEvent.ACTION_UP -> onUp(event) MotionEvent.ACTION_CANCEL -> onCancel(event) } @@ -132,12 +136,18 @@ class InputBarButton : RelativeLayout { onDownTimestamp = Date().time } + private fun onMove(event: MotionEvent) { + onMove?.invoke(event) + } + private fun onCancel(event: MotionEvent) { + onCancel?.invoke(event) collapse() longPressCallback?.let { gestureHandler.removeCallbacks(it) } } private fun onUp(event: MotionEvent) { + onUp?.invoke(event) collapse() if ((Date().time - onDownTimestamp) < VisibleMessageView.longPressDurationThreshold) { longPressCallback?.let { gestureHandler.removeCallbacks(it) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarRecordingView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarRecordingView.kt index 836e995dfe..9c09c68771 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarRecordingView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarRecordingView.kt @@ -1,19 +1,17 @@ package org.thoughtcrime.securesms.conversation.v2.input_bar -import android.animation.Animator import android.animation.FloatEvaluator import android.animation.ValueAnimator import android.content.Context import android.util.AttributeSet -import android.util.Log import android.view.LayoutInflater import android.widget.RelativeLayout import androidx.core.view.isVisible import kotlinx.android.synthetic.main.view_input_bar_recording.view.* import network.loki.messenger.R import org.thoughtcrime.securesms.loki.utilities.animateSizeChange +import org.thoughtcrime.securesms.loki.utilities.disableClipping import org.thoughtcrime.securesms.loki.utilities.toPx -import kotlin.math.roundToInt class InputBarRecordingView : RelativeLayout { @@ -23,6 +21,7 @@ class InputBarRecordingView : RelativeLayout { private fun initialize() { LayoutInflater.from(context).inflate(R.layout.view_input_bar_recording, this) + inputBarMiddleContentContainer.disableClipping() } fun show() { diff --git a/app/src/main/res/layout/view_input_bar_recording.xml b/app/src/main/res/layout/view_input_bar_recording.xml index cab339afbf..fc87155a2a 100644 --- a/app/src/main/res/layout/view_input_bar_recording.xml +++ b/app/src/main/res/layout/view_input_bar_recording.xml @@ -50,13 +50,16 @@