Add back mentions business logic

This commit is contained in:
Niels Andriesse 2021-06-25 14:42:04 +10:00
parent 40317d9834
commit 6140be6e56
3 changed files with 100 additions and 29 deletions

View File

@ -7,6 +7,7 @@ import android.database.Cursor
import android.graphics.Rect import android.graphics.Rect
import android.graphics.Typeface import android.graphics.Typeface
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.util.TypedValue import android.util.TypedValue
import android.view.* import android.view.*
import android.widget.RelativeLayout import android.widget.RelativeLayout
@ -27,6 +28,7 @@ import kotlinx.android.synthetic.main.view_input_bar_recording.view.*
import network.loki.messenger.R import network.loki.messenger.R
import nl.komponents.kovenant.ui.successUi import nl.komponents.kovenant.ui.successUi
import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.mentions.Mention
import org.session.libsession.messaging.mentions.MentionsManager import org.session.libsession.messaging.mentions.MentionsManager
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2 import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
@ -65,10 +67,16 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
private var linkPreviewViewModel: LinkPreviewViewModel? = null private var linkPreviewViewModel: LinkPreviewViewModel? = null
private var threadID: Long = -1 private var threadID: Long = -1
private var actionMode: ActionMode? = null private var actionMode: ActionMode? = null
private var unreadCount = 0
// Attachments
private var isLockViewExpanded = false private var isLockViewExpanded = false
private var isShowingAttachmentOptions = false private var isShowingAttachmentOptions = false
// Mentions
private val mentions = mutableListOf<Mention>()
private var mentionCandidatesView: MentionCandidatesView? = null private var mentionCandidatesView: MentionCandidatesView? = null
private var unreadCount = 0 private var previousText: CharSequence = ""
private var currentMentionStartIndex = -1
private var isShowingMentionCandidatesView = false
private val layoutManager: LinearLayoutManager private val layoutManager: LinearLayoutManager
get() { return conversationRecyclerView.layoutManager as LinearLayoutManager } get() { return conversationRecyclerView.layoutManager as LinearLayoutManager }
@ -294,9 +302,11 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
// 36 DP is the exact height of the typing indicator view. It's also exactly 18 * 2, and 18 is the large message // 36 DP is the exact height of the typing indicator view. It's also exactly 18 * 2, and 18 is the large message
// corner radius. This makes 36 DP look "correct" in the context of other messages on the screen. // corner radius. This makes 36 DP look "correct" in the context of other messages on the screen.
val typingIndicatorHeight = if (typingIndicatorViewContainer.isVisible) toPx(36, resources) else 0 val typingIndicatorHeight = if (typingIndicatorViewContainer.isVisible) toPx(36, resources) else 0
// We * don't * want to move the recycler view up when showing the mention candidates view
val additionalContentContainerHeight = if (isShowingMentionCandidatesView) 0 else additionalContentContainer.height
// Recycler view // Recycler view
val recyclerViewLayoutParams = conversationRecyclerView.layoutParams as RelativeLayout.LayoutParams val recyclerViewLayoutParams = conversationRecyclerView.layoutParams as RelativeLayout.LayoutParams
recyclerViewLayoutParams.bottomMargin = newValue + additionalContentContainer.height + typingIndicatorHeight recyclerViewLayoutParams.bottomMargin = newValue + additionalContentContainerHeight + typingIndicatorHeight
conversationRecyclerView.layoutParams = recyclerViewLayoutParams conversationRecyclerView.layoutParams = recyclerViewLayoutParams
// Additional content container // Additional content container
val additionalContentContainerLayoutParams = additionalContentContainer.layoutParams as RelativeLayout.LayoutParams val additionalContentContainerLayoutParams = additionalContentContainer.layoutParams as RelativeLayout.LayoutParams
@ -317,40 +327,77 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
override fun inputBarEditTextContentChanged(newContent: CharSequence) { override fun inputBarEditTextContentChanged(newContent: CharSequence) {
linkPreviewViewModel?.onTextChanged(this, inputBar.text, 0, 0) linkPreviewViewModel?.onTextChanged(this, inputBar.text, 0, 0)
// TODO: Implement the full mention show/hide logic showOrHideMentionCandidatesIfNeeded(newContent)
if (newContent.contains("@")) {
showMentionCandidates()
} else {
hideMentionCandidates()
}
} }
private fun showMentionCandidates() { private fun showOrHideMentionCandidatesIfNeeded(text: CharSequence) {
additionalContentContainer.removeAllViews() val isBackspace = (text.length < previousText.length)
val mentionCandidatesView = MentionCandidatesView(this) if (isBackspace) {
mentionCandidatesView.glide = glide currentMentionStartIndex = -1
additionalContentContainer.addView(mentionCandidatesView) hideMentionCandidates()
val mentionCandidates = MentionsManager.getMentionCandidates("", threadID, thread.isOpenGroupRecipient) val mentionsToRemove = mentions.filter { !text.contains(it.displayName) }
this.mentionCandidatesView = mentionCandidatesView mentions.removeAll(mentionsToRemove)
mentionCandidatesView.show(mentionCandidates, threadID)
mentionCandidatesView.alpha = 0.0f
val animation = ValueAnimator.ofObject(FloatEvaluator(), mentionCandidatesView.alpha, 1.0f)
animation.duration = 250L
animation.addUpdateListener { animator ->
mentionCandidatesView.alpha = animator.animatedValue as Float
} }
animation.start() if (text.isNotEmpty()) {
if (currentMentionStartIndex > text.length) { resetMentions() } // Should never occur
val lastCharIndex = text.lastIndex
val lastChar = text[lastCharIndex]
val secondToLastChar = if (lastCharIndex > 0) text[lastCharIndex - 1] else ' '
if (lastChar == '@' && Character.isWhitespace(secondToLastChar)) {
currentMentionStartIndex = lastCharIndex
showOrUpdateMentionCandidatesIfNeeded()
} else if (Character.isWhitespace(lastChar)) {
currentMentionStartIndex = -1
hideMentionCandidates()
} else if (currentMentionStartIndex != -1) {
val query = text.substring(currentMentionStartIndex + 1) // + 1 to get rid of the "@"
showOrUpdateMentionCandidatesIfNeeded(query)
}
}
previousText = text
}
private fun showOrUpdateMentionCandidatesIfNeeded(query: String = "") {
if (!isShowingMentionCandidatesView) {
additionalContentContainer.removeAllViews()
val view = MentionCandidatesView(this)
view.glide = glide
additionalContentContainer.addView(view)
val candidates = MentionsManager.getMentionCandidates(query, threadID, thread.isOpenGroupRecipient)
this.mentionCandidatesView = view
view.show(candidates, threadID)
view.alpha = 0.0f
val animation = ValueAnimator.ofObject(FloatEvaluator(), view.alpha, 1.0f)
animation.duration = 250L
animation.addUpdateListener { animator ->
view.alpha = animator.animatedValue as Float
}
animation.start()
} else {
val candidates = MentionsManager.getMentionCandidates(query, threadID, thread.isOpenGroupRecipient)
this.mentionCandidatesView!!.setMentionCandidates(candidates)
}
isShowingMentionCandidatesView = true
} }
private fun hideMentionCandidates() { private fun hideMentionCandidates() {
val mentionCandidatesView = mentionCandidatesView ?: return if (isShowingMentionCandidatesView) {
val animation = ValueAnimator.ofObject(FloatEvaluator(), mentionCandidatesView.alpha, 0.0f) val mentionCandidatesView = mentionCandidatesView ?: return
animation.duration = 250L val animation = ValueAnimator.ofObject(FloatEvaluator(), mentionCandidatesView.alpha, 0.0f)
animation.addUpdateListener { animator -> animation.duration = 250L
mentionCandidatesView.alpha = animator.animatedValue as Float animation.addUpdateListener { animator ->
if (animator.animatedFraction == 1.0f) { additionalContentContainer.removeAllViews() } mentionCandidatesView.alpha = animator.animatedValue as Float
if (animator.animatedFraction == 1.0f) { additionalContentContainer.removeAllViews() }
}
animation.start()
} }
animation.start() isShowingMentionCandidatesView = false
}
private fun resetMentions() {
previousText = ""
currentMentionStartIndex = -1
mentions.clear()
} }
override fun toggleAttachmentOptions() { override fun toggleAttachmentOptions() {
@ -569,9 +616,27 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
private fun unblock() { private fun unblock() {
// TODO: Implement // TODO: Implement
} }
override fun send() {
}
// endregion // endregion
// region General // region General
private fun getMessageBody(): CharSequence {
var result = inputBar.inputBarEditText.text?.trim() ?: ""
for (mention in mentions) {
try {
val startIndex = result.indexOf("@" + mention.displayName)
val endIndex = startIndex + mention.displayName.count() + 1 // + 1 to include the "@"
result = result.substring(0, startIndex) + "@" + mention.publicKey + result.substring(endIndex)
} catch (exception: Exception) {
Log.d("Loki", "Failed to process mention due to error: $exception")
}
}
return result
}
private fun saveDraft() { private fun saveDraft() {
val text = inputBar.text.trim() val text = inputBar.text.trim()
if (text.isEmpty()) { return } if (text.isEmpty()) { return }

View File

@ -61,6 +61,7 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate, QuoteViewDelegate, Li
microphoneOrSendButtonContainer.addView(sendButton) microphoneOrSendButtonContainer.addView(sendButton)
sendButton.layoutParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT) sendButton.layoutParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT)
sendButton.isVisible = false sendButton.isVisible = false
sendButton.onUp = { delegate?.send() }
// Edit text // Edit text
inputBarEditText.imeOptions = inputBarEditText.imeOptions or 16777216 // Always use incognito keyboard inputBarEditText.imeOptions = inputBarEditText.imeOptions or 16777216 // Always use incognito keyboard
inputBarEditText.delegate = this inputBarEditText.delegate = this
@ -162,4 +163,5 @@ interface InputBarDelegate {
fun onMicrophoneButtonMove(event: MotionEvent) fun onMicrophoneButtonMove(event: MotionEvent)
fun onMicrophoneButtonCancel(event: MotionEvent) fun onMicrophoneButtonCancel(event: MotionEvent)
fun onMicrophoneButtonUp(event: MotionEvent) fun onMicrophoneButtonUp(event: MotionEvent)
fun send()
} }

View File

@ -65,6 +65,10 @@ class MentionCandidatesView(context: Context, attrs: AttributeSet?, defStyleAttr
openGroupServer = openGroup.server openGroupServer = openGroup.server
openGroupRoom = openGroup.room openGroupRoom = openGroup.room
} }
setMentionCandidates(candidates)
}
fun setMentionCandidates(candidates: List<Mention>) {
this.candidates = candidates this.candidates = candidates
val layoutParams = this.layoutParams as ViewGroup.LayoutParams val layoutParams = this.layoutParams as ViewGroup.LayoutParams
layoutParams.height = toPx(Math.min(candidates.count(), 4) * 44, resources) layoutParams.height = toPx(Math.min(candidates.count(), 4) * 44, resources)