Add preliminary quote draft view

This commit is contained in:
Niels Andriesse 2021-06-18 15:11:41 +10:00
parent be158eccc1
commit 9419bafe93
9 changed files with 138 additions and 47 deletions

View File

@ -22,7 +22,6 @@ import kotlinx.android.synthetic.main.view_input_bar_recording.*
import kotlinx.android.synthetic.main.view_input_bar_recording.view.* import kotlinx.android.synthetic.main.view_input_bar_recording.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.messaging.mentions.MentionsManager import org.session.libsession.messaging.mentions.MentionsManager
import org.session.libsession.messaging.mentions.MentionsManager.getMentionCandidates
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarButton import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarButton
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarDelegate import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarDelegate
@ -155,15 +154,15 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
override fun inputBarHeightChanged(newValue: Int) { override fun inputBarHeightChanged(newValue: Int) {
// Recycler view // Recycler view
val recyclerViewLayoutParams = conversationRecyclerView.layoutParams as RelativeLayout.LayoutParams val recyclerViewLayoutParams = conversationRecyclerView.layoutParams as RelativeLayout.LayoutParams
recyclerViewLayoutParams.bottomMargin = newValue + inputBarAdditionalContentContainer.height recyclerViewLayoutParams.bottomMargin = newValue + additionalContentContainer.height
conversationRecyclerView.layoutParams = recyclerViewLayoutParams conversationRecyclerView.layoutParams = recyclerViewLayoutParams
// Input bar additional content container // Additional content container
val inputBarAdditionalContentContainerLayoutParams = inputBarAdditionalContentContainer.layoutParams as RelativeLayout.LayoutParams val additionalContentContainerLayoutParams = additionalContentContainer.layoutParams as RelativeLayout.LayoutParams
inputBarAdditionalContentContainerLayoutParams.bottomMargin = newValue additionalContentContainerLayoutParams.bottomMargin = newValue
inputBarAdditionalContentContainer.layoutParams = inputBarAdditionalContentContainerLayoutParams additionalContentContainer.layoutParams = additionalContentContainerLayoutParams
// Attachment options // Attachment options
val attachmentButtonHeight = inputBar.attachmentsButtonContainer.height val attachmentButtonHeight = inputBar.attachmentsButtonContainer.height
val bottomMargin = (newValue - attachmentButtonHeight) / 2 val bottomMargin = (newValue - inputBar.inputBarAdditionalContentContainer.height - attachmentButtonHeight) / 2
val margin = toPx(8, resources) val margin = toPx(8, resources)
val attachmentOptionsContainerLayoutParams = attachmentOptionsContainer.layoutParams as RelativeLayout.LayoutParams val attachmentOptionsContainerLayoutParams = attachmentOptionsContainer.layoutParams as RelativeLayout.LayoutParams
attachmentOptionsContainerLayoutParams.bottomMargin = bottomMargin + attachmentButtonHeight + margin attachmentOptionsContainerLayoutParams.bottomMargin = bottomMargin + attachmentButtonHeight + margin
@ -180,10 +179,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
} }
private fun showMentionCandidates() { private fun showMentionCandidates() {
inputBarAdditionalContentContainer.removeAllViews() additionalContentContainer.removeAllViews()
val mentionCandidatesView = MentionCandidatesView(this) val mentionCandidatesView = MentionCandidatesView(this)
mentionCandidatesView.glide = glide mentionCandidatesView.glide = glide
inputBarAdditionalContentContainer.addView(mentionCandidatesView) additionalContentContainer.addView(mentionCandidatesView)
val mentionCandidates = MentionsManager.getMentionCandidates("", threadID, thread.isOpenGroupRecipient) val mentionCandidates = MentionsManager.getMentionCandidates("", threadID, thread.isOpenGroupRecipient)
this.mentionCandidatesView = mentionCandidatesView this.mentionCandidatesView = mentionCandidatesView
mentionCandidatesView.show(mentionCandidates, threadID) mentionCandidatesView.show(mentionCandidates, threadID)
@ -202,7 +201,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
animation.duration = 250L animation.duration = 250L
animation.addUpdateListener { animator -> animation.addUpdateListener { animator ->
mentionCandidatesView.alpha = animator.animatedValue as Float mentionCandidatesView.alpha = animator.animatedValue as Float
if (animator.animatedFraction == 1.0f) { inputBarAdditionalContentContainer.removeAllViews() } if (animator.animatedFraction == 1.0f) { additionalContentContainer.removeAllViews() }
} }
animation.start() animation.start()
} }
@ -269,7 +268,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
// `position` is the adapter position; not the visual position // `position` is the adapter position; not the visual position
private fun handleSwipeToReply(message: MessageRecord, position: Int) { private fun handleSwipeToReply(message: MessageRecord, position: Int) {
inputBar.draftQuote(message)
} }
// `position` is the adapter position; not the visual position // `position` is the adapter position; not the visual position

View File

@ -9,6 +9,8 @@ import android.widget.RelativeLayout
import androidx.core.view.isVisible import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.view_input_bar.view.* import kotlinx.android.synthetic.main.view_input_bar.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.conversation.v2.messages.QuoteView
import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.loki.utilities.toDp import org.thoughtcrime.securesms.loki.utilities.toDp
import org.thoughtcrime.securesms.loki.utilities.toPx import org.thoughtcrime.securesms.loki.utilities.toPx
import kotlin.math.max import kotlin.math.max
@ -48,6 +50,15 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate {
} }
// endregion // endregion
// region General
private fun setHeight(newHeight: Int) {
val layoutParams = inputBarLinearLayout.layoutParams as LayoutParams
layoutParams.height = newHeight
inputBarLinearLayout.layoutParams = layoutParams
delegate?.inputBarHeightChanged(newHeight)
}
// endregion
// region Updating // region Updating
override fun inputBarEditTextContentChanged(text: CharSequence) { override fun inputBarEditTextContentChanged(text: CharSequence) {
sendButton.isVisible = text.isNotEmpty() sendButton.isVisible = text.isNotEmpty()
@ -57,11 +68,8 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate {
override fun inputBarEditTextHeightChanged(newValue: Int) { override fun inputBarEditTextHeightChanged(newValue: Int) {
val vMargin = toDp(4, resources) val vMargin = toDp(4, resources)
val layoutParams = inputBarLinearLayout.layoutParams as LayoutParams val newHeight = max(newValue + 2 * vMargin + inputBarAdditionalContentContainer.height, toPx(56, resources))
val newHeight = max(newValue + 2 * vMargin, toPx(56, resources)) setHeight(newHeight)
layoutParams.height = newHeight
inputBarLinearLayout.layoutParams = layoutParams
delegate?.inputBarHeightChanged(newHeight)
} }
private fun toggleAttachmentOptions() { private fun toggleAttachmentOptions() {
@ -71,6 +79,15 @@ class InputBar : RelativeLayout, InputBarEditTextDelegate {
private fun showVoiceMessageUI() { private fun showVoiceMessageUI() {
delegate?.showVoiceMessageUI() delegate?.showVoiceMessageUI()
} }
fun draftQuote(message: MessageRecord) {
inputBarAdditionalContentContainer.removeAllViews()
val quoteView = QuoteView(context)
inputBarAdditionalContentContainer.addView(quoteView)
quoteView.bind("", "", null, message.recipient)
val newHeight = height + quoteView.getIntrinsicHeight()
setHeight(newHeight)
}
// endregion // endregion
} }

View File

@ -6,6 +6,7 @@ import android.text.StaticLayout
import android.util.AttributeSet import android.util.AttributeSet
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.appcompat.widget.AppCompatEditText import androidx.appcompat.widget.AppCompatEditText
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities
import org.thoughtcrime.securesms.loki.utilities.toPx import org.thoughtcrime.securesms.loki.utilities.toPx
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@ -24,12 +25,7 @@ class InputBarEditText : AppCompatEditText {
override fun onTextChanged(text: CharSequence, start: Int, lengthBefore: Int, lengthAfter: Int) { override fun onTextChanged(text: CharSequence, start: Int, lengthBefore: Int, lengthAfter: Int) {
super.onTextChanged(text, start, lengthBefore, lengthAfter) super.onTextChanged(text, start, lengthBefore, lengthAfter)
delegate?.inputBarEditTextContentChanged(text) delegate?.inputBarEditTextContentChanged(text)
val builder = StaticLayout.Builder.obtain(text, 0, text.length, paint, width) val height = TextUtilities.getIntrinsicHeight(text, paint, width).toFloat()
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
.setLineSpacing(0.0f, 1.0f)
.setIncludePad(false)
val layout = builder.build()
val height = layout.height.toFloat()
val constrainedHeight = min(max(height, snMinHeight), snMaxHeight) val constrainedHeight = min(max(height, snMinHeight), snMaxHeight)
if (constrainedHeight.roundToInt() == this.height) { return } if (constrainedHeight.roundToInt() == this.height) { return }
val layoutParams = this.layoutParams as? RelativeLayout.LayoutParams ?: return val layoutParams = this.layoutParams as? RelativeLayout.LayoutParams ?: return

View File

@ -1,36 +1,51 @@
package org.thoughtcrime.securesms.conversation.v2.messages package org.thoughtcrime.securesms.conversation.v2.messages
import android.content.Context import android.content.Context
import android.content.res.Resources
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.RelativeLayout
import kotlinx.android.synthetic.main.view_quote.view.* import kotlinx.android.synthetic.main.view_quote.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities
import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.loki.utilities.toPx
import org.thoughtcrime.securesms.mms.SlideDeck
class QuoteView : LinearLayout { class QuoteView : LinearLayout {
private val screenWidth by lazy { Resources.getSystem().displayMetrics.widthPixels }
enum class Mode { Regular, Draft }
// region Lifecycle // region Lifecycle
constructor(context: Context) : super(context) { constructor(context: Context) : super(context) { initialize() }
setUpViewHierarchy() constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() }
} constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() }
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { private fun initialize() {
setUpViewHierarchy()
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
setUpViewHierarchy()
}
private fun setUpViewHierarchy() {
LayoutInflater.from(context).inflate(R.layout.view_quote, this) LayoutInflater.from(context).inflate(R.layout.view_quote, this)
} }
// endregion // endregion
// region General
fun getIntrinsicHeight(): Int {
var result = 0
val width = screenWidth
val author = quoteViewAuthorTextView.text
result += TextUtilities.getIntrinsicHeight(author, quoteViewAuthorTextView.paint, width)
val body = quoteViewBodyTextView.text
result += TextUtilities.getIntrinsicHeight(body, quoteViewBodyTextView.paint, width)
return result
}
// endregion
// region Updating // region Updating
fun bind(message: MessageRecord) { fun bind(authorPublicKey: String, body: String, attachments: SlideDeck?, thread: Recipient) {
textView.text = "I'm a quote" val accentLineLayoutParams = quoteViewAccentLine.layoutParams as RelativeLayout.LayoutParams
accentLineLayoutParams.height = getIntrinsicHeight()
quoteViewAccentLine.layoutParams = accentLineLayoutParams
} }
fun recycle() { fun recycle() {

View File

@ -63,7 +63,7 @@ class VisibleMessageContentView : LinearLayout {
mainContainer.addView(linkPreviewView) mainContainer.addView(linkPreviewView)
} else if (message is MmsMessageRecord && message.quote != null) { } else if (message is MmsMessageRecord && message.quote != null) {
val quoteView = QuoteView(context) val quoteView = QuoteView(context)
quoteView.bind(message) quoteView.bind(message.individualRecipient.address.toString(), "iuasfhiausfh", null, message.recipient)
mainContainer.addView(quoteView) mainContainer.addView(quoteView)
} else if (message is MmsMessageRecord && message.slideDeck.audioSlide != null) { } else if (message is MmsMessageRecord && message.slideDeck.audioSlide != null) {
val voiceMessageView = VoiceMessageView(context) val voiceMessageView = VoiceMessageView(context)

View File

@ -0,0 +1,17 @@
package org.thoughtcrime.securesms.conversation.v2.utilities
import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint
object TextUtilities {
fun getIntrinsicHeight(text: CharSequence, paint: TextPaint, width: Int): Int {
val builder = StaticLayout.Builder.obtain(text, 0, text.length, paint, width)
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
.setLineSpacing(0.0f, 1.0f)
.setIncludePad(false)
val layout = builder.build()
return layout.height
}
}

View File

@ -19,7 +19,7 @@
android:layout_alignParentBottom="true" /> android:layout_alignParentBottom="true" />
<FrameLayout <FrameLayout
android:id="@+id/inputBarAdditionalContentContainer" android:id="@+id/additionalContentContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"

View File

@ -12,6 +12,11 @@
android:layout_height="1px" android:layout_height="1px"
android:background="@color/separator" /> android:background="@color/separator" />
<FrameLayout
android:id="@+id/inputBarAdditionalContentContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">

View File

@ -1,16 +1,58 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:background="@color/input_bar_background"
android:gravity="center"> android:gravity="center_vertical">
<TextView <View
android:id="@+id/textView" android:id="@+id/quoteViewAccentLine"
android:layout_width="match_parent" android:layout_width="@dimen/accent_line_thickness"
android:layout_height="0dp"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:background="@color/text" />
<!-- The start margin below is the accent line thickness (4 dp) + small spacing (8 dp) -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="@dimen/medium_font_size" android:layout_marginStart="12dp"
android:textColor="@color/text" /> android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:orientation="vertical">
</LinearLayout> <TextView
android:id="@+id/quoteViewAuthorTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Spiderman"
android:textSize="@dimen/small_font_size"
android:textStyle="bold"
android:textColor="@color/text"
android:maxLines="1"
android:ellipsize="end" />
<TextView
android:id="@+id/quoteViewBodyTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Yo, I need your help here!"
android:textSize="@dimen/small_font_size"
android:textColor="@color/text"
android:maxLines="3"
android:ellipsize="end" />
</LinearLayout>
<ImageView
android:layout_width="32dp"
android:layout_height="32dp"
android:padding="8dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_x_28" />
</RelativeLayout>