Merge pull request #3182 from vector-im/feature/ons/message_composer_ui

Message Composer Design Update
This commit is contained in:
Benoit Marty 2021-06-09 18:26:55 +02:00 committed by GitHub
commit 5d69bc934a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 130 additions and 114 deletions

View File

@ -0,0 +1 @@
Update Message Composer design

View File

@ -547,15 +547,15 @@ class RoomDetailFragment @Inject constructor(
.fromRootView(views.rootConstraintLayout)
.setKeyboardAnimationStyle(R.style.emoji_fade_animation_style)
.setOnEmojiPopupShownListener {
views.composerLayout.views.composerEmojiButton.let {
it.setImageResource(R.drawable.ic_keyboard)
it.contentDescription = getString(R.string.a11y_close_emoji_picker)
views.composerLayout.views.composerEmojiButton.apply {
contentDescription = getString(R.string.a11y_close_emoji_picker)
setImageResource(R.drawable.ic_keyboard)
}
}
.setOnEmojiPopupDismissListener {
views.composerLayout.views.composerEmojiButton.let {
it.setImageResource(R.drawable.ic_insert_emoji)
it.contentDescription = getString(R.string.a11y_open_emoji_picker)
views.composerLayout.views.composerEmojiButton.apply {
contentDescription = getString(R.string.a11y_open_emoji_picker)
setImageResource(R.drawable.ic_insert_emoji)
}
}
.build(views.composerLayout.views.composerEditText)
@ -1188,6 +1188,10 @@ class RoomDetailFragment @Inject constructor(
override fun onRichContentSelected(contentUri: Uri): Boolean {
return sendUri(contentUri)
}
override fun onTextEmptyStateChanged(isEmpty: Boolean) {
// No op
}
}
}
@ -1201,6 +1205,7 @@ class RoomDetailFragment @Inject constructor(
views.composerLayout.collapse(true)
lockSendButton = true
roomDetailViewModel.handle(RoomDetailAction.SendMessage(text, vectorPreferences.isMarkdownEnabled()))
emojiPopup.dismiss()
}
}
@ -1250,7 +1255,7 @@ class RoomDetailFragment @Inject constructor(
if (state.tombstoneEvent == null) {
if (state.canSendMessage) {
views.composerLayout.visibility = View.VISIBLE
views.composerLayout.setRoomEncrypted(summary.isEncrypted, summary.roomEncryptionTrustLevel)
views.composerLayout.setRoomEncrypted(summary.isEncrypted)
views.notificationAreaView.render(NotificationAreaView.State.Hidden)
} else {
views.composerLayout.visibility = View.GONE

View File

@ -37,9 +37,11 @@ class ComposerEditText @JvmOverloads constructor(context: Context, attrs: Attrib
interface Callback {
fun onRichContentSelected(contentUri: Uri): Boolean
fun onTextEmptyStateChanged(isEmpty: Boolean)
}
var callback: Callback? = null
private var isEmptyText = true
override fun onCreateInputConnection(editorInfo: EditorInfo): InputConnection? {
val ic = super.onCreateInputConnection(editorInfo) ?: return null
@ -93,6 +95,11 @@ class ComposerEditText @JvmOverloads constructor(context: Context, attrs: Attrib
}
spanToRemove = null
}
// Report blank status of EditText to be able to arrange other elements of the composer
if (s.isEmpty() != isEmptyText) {
isEmptyText = !isEmptyText
callback?.onTextEmptyStateChanged(isEmptyText)
}
}
}
)

View File

@ -24,6 +24,7 @@ import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.text.toSpannable
import androidx.core.view.isVisible
import androidx.transition.ChangeBounds
import androidx.transition.Fade
import androidx.transition.Transition
@ -32,8 +33,6 @@ import androidx.transition.TransitionSet
import im.vector.app.R
import im.vector.app.databinding.ComposerLayoutBinding
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
/**
* Encapsulate the timeline composer UX.
*
@ -70,6 +69,10 @@ class TextComposerView @JvmOverloads constructor(
override fun onRichContentSelected(contentUri: Uri): Boolean {
return callback?.onRichContentSelected(contentUri) ?: false
}
override fun onTextEmptyStateChanged(isEmpty: Boolean) {
views.sendButton.isVisible = currentConstraintSetId == R.layout.composer_layout_constraint_set_expanded || !isEmpty
}
}
views.composerRelatedMessageCloseButton.setOnClickListener {
collapse()
@ -93,6 +96,7 @@ class TextComposerView @JvmOverloads constructor(
}
currentConstraintSetId = R.layout.composer_layout_constraint_set_compact
applyNewConstraintSet(animate, transitionComplete)
views.sendButton.isVisible = !views.composerEditText.text.isNullOrEmpty()
}
fun expand(animate: Boolean = true, transitionComplete: (() -> Unit)? = null) {
@ -102,6 +106,7 @@ class TextComposerView @JvmOverloads constructor(
}
currentConstraintSetId = R.layout.composer_layout_constraint_set_expanded
applyNewConstraintSet(animate, transitionComplete)
views.sendButton.isVisible = true
}
private fun applyNewConstraintSet(animate: Boolean, transitionComplete: (() -> Unit)?) {
@ -110,8 +115,6 @@ class TextComposerView @JvmOverloads constructor(
}
ConstraintSet().also {
it.clone(context, currentConstraintSetId)
// in case shield is hidden, we will have glitch without this
it.getConstraint(R.id.composerShieldImageView).propertySet.visibility = views.composerShieldImageView.visibility
it.applyTo(this)
}
}
@ -139,13 +142,11 @@ class TextComposerView @JvmOverloads constructor(
TransitionManager.beginDelayedTransition((parent as? ViewGroup ?: this), transition)
}
fun setRoomEncrypted(isEncrypted: Boolean, roomEncryptionTrustLevel: RoomEncryptionTrustLevel?) {
fun setRoomEncrypted(isEncrypted: Boolean) {
if (isEncrypted) {
views.composerEditText.setHint(R.string.room_message_placeholder)
views.composerShieldImageView.render(roomEncryptionTrustLevel)
} else {
views.composerEditText.setHint(R.string.room_message_placeholder)
views.composerShieldImageView.render(null)
}
}
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<stroke
android:width="1dp"
android:color="?vctr_content_quinary" />
<corners android:radius="8dp" />
</shape>

View File

@ -4,18 +4,18 @@
android:viewportWidth="32"
android:viewportHeight="32">
<path
android:pathData="M16,16m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0"
android:fillColor="#E3E8F0"/>
android:fillColor="?vctr_content_quinary"
android:pathData="M16,16m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0" />
<path
android:fillColor="#00000000"
android:pathData="M10.0009,16H22.0009"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#61708B"
android:strokeColor="?vctr_content_secondary"
android:strokeLineCap="round" />
<path
android:fillColor="#00000000"
android:pathData="M16.0009,10V22"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#61708B"
android:strokeColor="?vctr_content_secondary"
android:strokeLineCap="round" />
</vector>

View File

@ -1,21 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="32"
android:viewportHeight="32">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M16,16m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0"
android:fillColor="#E3E8F0"/>
<path
android:pathData="M9.276,19.6667C10.7004,21.8728 13.1806,23.3333 16.0019,23.3333C18.8233,23.3333 21.3035,21.8728 22.7278,19.6667"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#61708B"
android:strokeLineCap="round"/>
<path
android:pathData="M18.6667,12.6667a2,2.6667 0,1 0,4 0a2,2.6667 0,1 0,-4 0z"
android:fillColor="#61708B"/>
<path
android:pathData="M8.6667,12.6667a2,2.6667 0,1 0,4 0a2,2.6667 0,1 0,-4 0z"
android:fillColor="#61708B"/>
android:fillColor="#F00"
android:fillType="evenOdd"
android:pathData="M12,22C17.5228,22 22,17.5228 22,12C22,6.4771 17.5228,2 12,2C6.4771,2 2,6.4771 2,12C2,17.5228 6.4771,22 12,22ZM15.389,13.7659C15.6794,13.3162 16.2793,13.187 16.729,13.4774C17.1788,13.7677 17.308,14.3676 17.0176,14.8174C15.9565,16.461 14.1059,17.5526 11.9996,17.5526C9.8934,17.5526 8.0428,16.461 6.9817,14.8174C6.6913,14.3677 6.8205,13.7677 7.2702,13.4774C7.72,13.187 8.3199,13.3162 8.6103,13.7659C9.3295,14.88 10.5791,15.6141 11.9996,15.6141C13.4202,15.6141 14.6698,14.88 15.389,13.7659ZM10,10C10,10.8284 9.4404,11.5 8.75,11.5C8.0596,11.5 7.5,10.8284 7.5,10C7.5,9.1716 8.0596,8.5 8.75,8.5C9.4404,8.5 10,9.1716 10,10ZM15.25,11.5C15.9404,11.5 16.5,10.8284 16.5,10C16.5,9.1716 15.9404,8.5 15.25,8.5C14.5596,8.5 14,9.1716 14,10C14,10.8284 14.5596,11.5 15.25,11.5Z" />
<group>
<clip-path
android:fillType="evenOdd"
android:pathData="M12,22C17.5228,22 22,17.5228 22,12C22,6.4771 17.5228,2 12,2C6.4771,2 2,6.4771 2,12C2,17.5228 6.4771,22 12,22ZM15.389,13.7659C15.6794,13.3162 16.2793,13.187 16.729,13.4774C17.1788,13.7677 17.308,14.3676 17.0176,14.8174C15.9565,16.461 14.1059,17.5526 11.9996,17.5526C9.8934,17.5526 8.0428,16.461 6.9817,14.8174C6.6913,14.3677 6.8205,13.7677 7.2702,13.4774C7.72,13.187 8.3199,13.3162 8.6103,13.7659C9.3295,14.88 10.5791,15.6141 11.9996,15.6141C13.4202,15.6141 14.6698,14.88 15.389,13.7659ZM10,10C10,10.8284 9.4404,11.5 8.75,11.5C8.0596,11.5 7.5,10.8284 7.5,10C7.5,9.1716 8.0596,8.5 8.75,8.5C9.4404,8.5 10,9.1716 10,10ZM15.25,11.5C15.9404,11.5 16.5,10.8284 16.5,10C16.5,9.1716 15.9404,8.5 15.25,8.5C14.5596,8.5 14,9.1716 14,10C14,10.8284 14.5596,11.5 15.25,11.5Z" />
</group>
</vector>

View File

@ -1,4 +1,9 @@
<vector android:height="32dp" android:viewportHeight="24"
android:viewportWidth="24" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#E3E8F0" android:pathData="M20,5L4,5c-1.1,0 -1.99,0.9 -1.99,2L2,17c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,7c0,-1.1 -0.9,-2 -2,-2zM11,8h2v2h-2L11,8zM11,11h2v2h-2v-2zM8,8h2v2L8,10L8,8zM8,11h2v2L8,13v-2zM7,13L5,13v-2h2v2zM7,10L5,10L5,8h2v2zM16,17L8,17v-2h8v2zM16,13h-2v-2h2v2zM16,10h-2L14,8h2v2zM19,13h-2v-2h2v2zM19,10h-2L17,8h2v2z"/>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#F00"
android:pathData="M20,5L4,5c-1.1,0 -1.99,0.9 -1.99,2L2,17c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,7c0,-1.1 -0.9,-2 -2,-2zM11,8h2v2h-2L11,8zM11,11h2v2h-2v-2zM8,8h2v2L8,10L8,8zM8,11h2v2L8,13v-2zM7,13L5,13v-2h2v2zM7,10L5,10L5,8h2v2zM16,17L8,17v-2h8v2zM16,13h-2v-2h2v2zM16,10h-2L14,8h2v2zM19,13h-2v-2h2v2zM19,10h-2L17,8h2v2z" />
</vector>

View File

@ -25,13 +25,6 @@
android:background="?vctr_list_separator"
tools:ignore="MissingConstraints" />
<View
android:id="@+id/related_message_background_bottom_separator"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="?vctr_list_separator"
tools:ignore="MissingConstraints" />
<ImageView
android:id="@+id/composerRelatedMessageAvatar"
android:layout_width="0dp"
@ -103,11 +96,11 @@
android:src="@drawable/ic_attachment"
tools:ignore="MissingConstraints" />
<im.vector.app.core.ui.views.ShieldImageView
android:id="@+id/composerShieldImageView"
<FrameLayout
android:id="@+id/composerEditTextOuterBorder"
android:layout_width="0dp"
android:layout_height="0dp"
tools:ignore="MissingConstraints" />
android:background="@drawable/bg_composer_edit_text" />
<im.vector.app.features.home.room.detail.composer.ComposerEditText
android:id="@+id/composerEditText"
@ -126,7 +119,8 @@
android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/a11y_open_emoji_picker"
android:src="@drawable/ic_insert_emoji"
tools:ignore="MissingConstraints" />
app:tint="?vctr_content_tertiary"
tools:ignore="MissingConstraints,MissingPrefix" />
<ImageButton
android:id="@+id/sendButton"

View File

@ -28,16 +28,6 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<View
android:id="@+id/related_message_background_bottom_separator"
android:layout_width="0dp"
android:layout_height="1dp"
android:background="?vctr_list_separator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/composerRelatedMessageAvatar"
android:layout_width="40dp"
@ -125,20 +115,23 @@
android:contentDescription="@string/option_send_files"
android:src="@drawable/ic_attachment"
app:layout_constraintBottom_toBottomOf="@id/sendButton"
app:layout_constraintEnd_toStartOf="@+id/composerShieldImageView"
app:layout_constraintEnd_toStartOf="@+id/composerEditTextOuterBorder"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/sendButton"
app:layout_goneMarginBottom="57dp"
tools:ignore="MissingPrefix" />
<im.vector.app.core.ui.views.ShieldImageView
android:id="@+id/composerShieldImageView"
android:layout_width="16dp"
android:layout_height="16dp"
app:layout_constraintBottom_toBottomOf="@id/sendButton"
app:layout_constraintEnd_toStartOf="@+id/composerEditText"
app:layout_constraintStart_toEndOf="@+id/attachmentButton"
app:layout_constraintTop_toTopOf="@id/sendButton"
tools:visibility="visible" />
<FrameLayout
android:id="@+id/composerEditTextOuterBorder"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/sendButton"
app:layout_constraintStart_toEndOf="@id/attachmentButton"
app:layout_constraintTop_toTopOf="parent"
app:layout_goneMarginEnd="12dp" />
<im.vector.app.features.home.room.detail.composer.ComposerEditText
android:id="@+id/composerEditText"
@ -150,23 +143,25 @@
android:nextFocusUp="@id/composerEditText"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/composerEmojiButton"
app:layout_constraintStart_toEndOf="@+id/composerShieldImageView"
app:layout_constraintStart_toStartOf="@+id/composerEditTextOuterBorder"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/lorem/random" />
<ImageButton
android:id="@+id/composerEmojiButton"
android:layout_width="52dp"
android:layout_height="52dp"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_margin="1dp"
android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/a11y_open_emoji_picker"
android:src="@drawable/ic_insert_emoji"
app:layout_constraintBottom_toBottomOf="@id/sendButton"
app:layout_constraintEnd_toStartOf="@+id/sendButton"
app:layout_constraintBottom_toBottomOf="@id/attachmentButton"
app:layout_constraintEnd_toEndOf="@+id/composerEditTextOuterBorder"
app:layout_constraintStart_toEndOf="@id/composerEditText"
app:layout_constraintTop_toTopOf="@id/sendButton"
app:layout_goneMarginEnd="8dp" />
app:layout_constraintTop_toTopOf="@id/attachmentButton"
app:layout_goneMarginEnd="8dp"
app:tint="?vctr_content_quaternary"
tools:ignore="MissingPrefix" />
<ImageButton
android:id="@+id/sendButton"
@ -177,9 +172,10 @@
android:contentDescription="@string/send"
android:scaleType="center"
android:src="@drawable/ic_send"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/composerEmojiButton"
tools:ignore="MissingPrefix" />
tools:ignore="MissingPrefix"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -26,15 +26,6 @@
app:layout_constraintStart_toStartOf="@+id/related_message_background"
app:layout_constraintTop_toTopOf="@id/related_message_background" />
<View
android:id="@+id/related_message_background_bottom_separator"
android:layout_width="0dp"
android:layout_height="1dp"
android:background="?vctr_list_separator"
app:layout_constraintBottom_toBottomOf="@id/related_message_background"
app:layout_constraintEnd_toEndOf="@id/related_message_background"
app:layout_constraintStart_toStartOf="@+id/related_message_background" />
<ImageView
android:id="@+id/composerRelatedMessageAvatar"
android:layout_width="40dp"
@ -139,15 +130,18 @@
app:layout_constraintTop_toTopOf="@+id/sendButton"
tools:ignore="MissingPrefix" />
<im.vector.app.core.ui.views.ShieldImageView
android:id="@+id/composerShieldImageView"
android:layout_width="16dp"
android:layout_height="16dp"
<FrameLayout
android:id="@+id/composerEditTextOuterBorder"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="@+id/sendButton"
app:layout_constraintEnd_toStartOf="@+id/composerEditText"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/sendButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/sendButton" />
app:layout_constraintTop_toBottomOf="@id/composer_preview_barrier"
app:layout_goneMarginEnd="12dp" />
<im.vector.app.features.home.room.detail.composer.ComposerEditText
android:id="@+id/composerEditText"
@ -158,23 +152,26 @@
android:nextFocusUp="@id/composerEditText"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/composerEmojiButton"
app:layout_constraintStart_toEndOf="@+id/composerShieldImageView"
app:layout_constraintStart_toStartOf="@+id/composerEditTextOuterBorder"
app:layout_constraintTop_toBottomOf="@id/composer_preview_barrier"
tools:text="@tools:sample/lorem/random" />
<ImageButton
android:id="@+id/composerEmojiButton"
android:layout_width="52dp"
android:layout_height="52dp"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_margin="1dp"
android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/a11y_open_emoji_picker"
android:src="@drawable/ic_insert_emoji"
app:layout_constraintBottom_toBottomOf="@id/sendButton"
app:layout_constraintEnd_toStartOf="@+id/sendButton"
app:layout_constraintEnd_toEndOf="@+id/composerEditTextOuterBorder"
app:layout_constraintStart_toEndOf="@id/composerEditText"
app:layout_constraintTop_toTopOf="@id/sendButton"
app:layout_goneMarginEnd="8dp" />
app:layout_goneMarginBottom="52dp"
app:layout_goneMarginEnd="8dp"
app:tint="?vctr_content_quaternary"
tools:ignore="MissingPrefix" />
<ImageButton
android:id="@+id/sendButton"
@ -185,10 +182,12 @@
android:contentDescription="@string/send"
android:scaleType="center"
android:src="@drawable/ic_send"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/composer_preview_barrier"
app:layout_constraintVertical_bias="1"
tools:ignore="MissingPrefix" />
tools:ignore="MissingPrefix"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -173,6 +173,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:colorBackground"
android:minHeight="56dp"
android:transitionName="composer"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"

View File

@ -225,7 +225,7 @@
<style name="ComposerEditTextStyle" parent="Widget.AppCompat.EditText">
<item name="android:background">@android:color/transparent</item>
<item name="android:inputType">textCapSentences|textMultiLine</item>
<item name="android:maxLines">6</item>
<item name="android:maxLines">12</item>
<item name="android:minHeight">48dp</item>
<item name="android:padding">8dp</item>
<item name="android:textSize">15sp</item>