Scroll to message upon tapping quote & fix various bugs

pull/597/head
Niels Andriesse 3 years ago
parent 7ce124118f
commit bef7413055

@ -3,15 +3,14 @@ package org.thoughtcrime.securesms.conversation.v2
import android.Manifest
import android.animation.FloatEvaluator
import android.animation.ValueAnimator
import android.content.Context
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.res.Resources
import android.database.Cursor
import android.graphics.Rect
import android.graphics.Typeface
import android.os.Bundle
import android.net.Uri
import android.os.*
import android.text.TextUtils
@ -72,6 +71,7 @@ import org.thoughtcrime.securesms.conversation.v2.input_bar.mentions.MentionCand
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCallback
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationActionModeCallbackDelegate
import org.thoughtcrime.securesms.conversation.v2.menus.ConversationMenuHelper
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageContentViewDelegate
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
import org.thoughtcrime.securesms.conversation.v2.utilities.AttachmentManager
import org.thoughtcrime.securesms.database.DatabaseFactory
@ -84,11 +84,11 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel.LinkPreviewState
import org.thoughtcrime.securesms.loki.utilities.ActivityDispatcher
import org.thoughtcrime.securesms.loki.utilities.push
import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity
import org.thoughtcrime.securesms.loki.activities.SelectContactsActivity.Companion.selectedContactsKey
import org.thoughtcrime.securesms.loki.utilities.ActivityDispatcher
import org.thoughtcrime.securesms.loki.utilities.MentionUtilities
import org.thoughtcrime.securesms.loki.utilities.push
import org.thoughtcrime.securesms.loki.utilities.toPx
import org.thoughtcrime.securesms.mediasend.Media
import org.thoughtcrime.securesms.mediasend.MediaSendActivity
@ -108,7 +108,7 @@ import kotlin.math.*
class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDelegate,
InputBarRecordingViewDelegate, AttachmentManager.AttachmentListener, ActivityDispatcher,
ConversationActionModeCallbackDelegate {
ConversationActionModeCallbackDelegate, VisibleMessageContentViewDelegate {
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
private var linkPreviewViewModel: LinkPreviewViewModel? = null
private var threadID: Long = -1
@ -147,6 +147,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
},
glide
)
adapter.visibleMessageContentViewDelegate = this
adapter
}
@ -737,6 +738,11 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
this.previousText = newText
}
override fun scrollToMessageIfPossible(timestamp: Long) {
val lastSeenItemPosition = adapter.getItemPositionForTimestamp(timestamp) ?: return
conversationRecyclerView.scrollToPosition(lastSeenItemPosition)
}
override fun sendMessage() {
if (thread.isContactRecipient && thread.isBlocked) {
BlockedDialog(thread).show(supportFragmentManager, "Blocked Dialog")
@ -905,10 +911,18 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
override fun startRecordingVoiceMessage() {
showVoiceMessageUI()
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
audioRecorder.startRecording()
stopAudioHandler.postDelayed(stopVoiceMessageRecordingTask, 60000) // Limit voice messages to 1 minute each
if (Permissions.hasAll(this, Manifest.permission.RECORD_AUDIO)) {
showVoiceMessageUI()
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
audioRecorder.startRecording()
stopAudioHandler.postDelayed(stopVoiceMessageRecordingTask, 60000) // Limit voice messages to 1 minute each
} else {
Permissions.with(this)
.request(Manifest.permission.RECORD_AUDIO)
.withRationaleDialog(getString(R.string.ConversationActivity_to_send_audio_messages_allow_signal_access_to_your_microphone), R.drawable.ic_baseline_mic_48)
.withPermanentDenialDialog(getString(R.string.ConversationActivity_signal_requires_the_microphone_permission_in_order_to_send_audio_messages))
.execute()
}
}
override fun sendVoiceMessage() {

@ -8,6 +8,7 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import kotlinx.android.synthetic.main.view_visible_message.view.*
import org.thoughtcrime.securesms.conversation.v2.messages.ControlMessageView
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageContentViewDelegate
import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter
import org.thoughtcrime.securesms.database.DatabaseFactory
@ -20,6 +21,7 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPr
: CursorRecyclerViewAdapter<ViewHolder>(context, cursor) {
private val messageDB = DatabaseFactory.getMmsSmsDatabase(context)
var selectedItems = mutableSetOf<MessageRecord>()
var visibleMessageContentViewDelegate: VisibleMessageContentViewDelegate? = null
sealed class ViewType(val rawValue: Int) {
object Visible : ViewType(0)
@ -72,6 +74,7 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPr
view.onPress = { rawX, rawY -> onItemPress(message, viewHolder.adapterPosition, view, Rect(rawX, rawY, rawX, rawY)) }
view.onSwipeToReply = { onItemSwipeToReply(message, viewHolder.adapterPosition) }
view.onLongPress = { onItemLongPress(message, viewHolder.adapterPosition) }
view.contentViewDelegate = visibleMessageContentViewDelegate
}
is ControlMessageViewHolder -> viewHolder.view.bind(message)
}
@ -113,8 +116,19 @@ class ConversationAdapter(context: Context, cursor: Cursor, private val onItemPr
if (lastSeenTimestamp <= 0L || cursor == null || !isActiveCursor) return null
for (i in 0 until itemCount) {
cursor.moveToPosition(i)
val messageRecord = messageDB.readerFor(cursor).current
if (messageRecord.isOutgoing || messageRecord.dateReceived <= lastSeenTimestamp) { return i }
val message = messageDB.readerFor(cursor).current
if (message.isOutgoing || message.dateReceived <= lastSeenTimestamp) { return i }
}
return null
}
fun getItemPositionForTimestamp(timestamp: Long): Int? {
val cursor = this.cursor
if (timestamp <= 0L || cursor == null || !isActiveCursor) return null
for (i in 0 until itemCount) {
cursor.moveToPosition(i)
val message = messageDB.readerFor(cursor).current
if (message.dateSent == timestamp) { return i }
}
return null
}

@ -6,10 +6,12 @@ import android.text.SpannableStringBuilder
import android.text.style.StyleSpan
import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.dialog_join_open_group.view.*
import network.loki.messenger.R
import org.session.libsession.messaging.open_groups.OpenGroupV2
import org.session.libsession.utilities.OpenGroupUrlParser
import org.session.libsignal.utilities.ThreadUtils
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
import org.thoughtcrime.securesms.loki.api.OpenGroupManager
import org.thoughtcrime.securesms.loki.protocol.MultiDeviceProtocol
@ -33,8 +35,11 @@ class JoinOpenGroupDialog(private val name: String, private val url: String) : B
private fun join() {
val openGroup = OpenGroupUrlParser.parseUrl(url)
OpenGroupManager.add(openGroup.server, openGroup.room, openGroup.serverPublicKey, requireContext())
MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(requireContext())
val activity = requireContext() as AppCompatActivity
ThreadUtils.queue {
OpenGroupManager.add(openGroup.server, openGroup.room, openGroup.serverPublicKey, activity)
MultiDeviceProtocol.forceSyncConfigurationNowIfNeeded(activity)
}
dismiss()
}
}

@ -32,6 +32,9 @@ class ControlMessageView : LinearLayout {
if (message.isExpirationTimerUpdate) {
iconImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_timer, context.theme))
iconImageView.visibility = View.VISIBLE
} else if (message.isMediaSavedNotification) {
iconImageView.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_file_download_white_36dp, context.theme))
iconImageView.visibility = View.VISIBLE
}
textView.text = message.getDisplayBody(context)
}

@ -23,6 +23,7 @@ import androidx.core.graphics.BlendModeCompat
import androidx.core.text.getSpans
import androidx.core.text.toSpannable
import androidx.core.text.util.LinkifyCompat
import kotlinx.android.synthetic.main.view_link_preview.view.*
import kotlinx.android.synthetic.main.view_visible_message_content.view.*
import network.loki.messenger.R
import org.session.libsession.utilities.ThemeUtil
@ -41,6 +42,7 @@ import kotlin.math.roundToInt
class VisibleMessageContentView : LinearLayout {
var onContentClick: ((rawRect: Rect) -> Unit)? = null
var onContentDoubleTap: (() -> Unit)? = null
var delegate: VisibleMessageContentViewDelegate? = null
// region Lifecycle
constructor(context: Context) : super(context) { initialize() }
@ -87,6 +89,13 @@ class VisibleMessageContentView : LinearLayout {
val bodyTextView = VisibleMessageContentView.getBodyTextView(context, message)
ViewUtil.setPaddingTop(bodyTextView, 0)
mainContainer.addView(bodyTextView)
onContentClick = { rect ->
val r = Rect()
quoteView.getGlobalVisibleRect(r)
if (r.contains(rect)) {
delegate?.scrollToMessageIfPossible(quote.id)
}
}
} else if (message is MmsMessageRecord && message.slideDeck.audioSlide != null) {
val voiceMessageView = VoiceMessageView(context)
voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster)
@ -188,4 +197,9 @@ class VisibleMessageContentView : LinearLayout {
}
}
// endregion
}
interface VisibleMessageContentViewDelegate {
fun scrollToMessageIfPossible(timestamp: Long)
}

@ -48,6 +48,7 @@ class VisibleMessageView : LinearLayout {
var onPress: ((rawX: Int, rawY: Int) -> Unit)? = null
var onSwipeToReply: (() -> Unit)? = null
var onLongPress: (() -> Unit)? = null
var contentViewDelegate: VisibleMessageContentViewDelegate? = null
companion object {
const val swipeToReplyThreshold = 80.0f // dp
@ -139,6 +140,7 @@ class VisibleMessageView : LinearLayout {
if (profilePictureContainer.visibility != View.GONE) { maxWidth -= profilePictureContainer.width }
// Populate content view
messageContentView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster, glide, maxWidth, thread)
messageContentView.delegate = contentViewDelegate
onDoubleTap = { messageContentView.onContentDoubleTap?.invoke() }
}
@ -239,6 +241,7 @@ class VisibleMessageView : LinearLayout {
} else {
longPressCallback?.let { gestureHandler.removeCallbacks(it) }
}
if (translationX > 0) { return } // Only allow swipes to the left
// The idea here is to asymptotically approach a maximum drag distance
val damping = 50.0f
val sign = -1.0f

@ -10,8 +10,8 @@
<ImageView
android:id="@+id/menu_badge_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_gravity="center"
android:src="@drawable/ic_timer"
android:background="@color/transparent"

@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:paddingVertical="@dimen/medium_spacing"
android:paddingHorizontal="@dimen/massive_spacing"
@ -12,7 +13,8 @@
android:id="@+id/iconImageView"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginBottom="@dimen/small_spacing" />
android:layout_marginBottom="@dimen/small_spacing"
app:tint="@color/text" />
<TextView
android:id="@+id/textView"

Loading…
Cancel
Save