No longer use dialog for chat message long press

This commit is contained in:
Sylvain Berfini 2024-07-18 12:19:50 +02:00
parent a73e483478
commit 4c9bdec61e
8 changed files with 668 additions and 550 deletions

View file

@ -21,15 +21,9 @@ package org.linphone.ui.main.chat.fragment
import android.Manifest
import android.app.Activity
import android.app.Dialog
import android.content.ActivityNotFoundException
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Bundle
import android.text.Editable
@ -40,18 +34,13 @@ import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.view.Window
import android.view.WindowManager
import android.widget.PopupWindow
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.UiThread
import androidx.core.app.ActivityCompat
import androidx.core.content.FileProvider
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.doOnPreDraw
import androidx.core.view.updatePadding
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
@ -75,7 +64,6 @@ import org.linphone.R
import org.linphone.compatibility.Compatibility
import org.linphone.core.ChatMessage
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatBubbleLongPressMenuBinding
import org.linphone.databinding.ChatConversationFragmentBinding
import org.linphone.databinding.ChatConversationPopupMenuBinding
import org.linphone.ui.GenericActivity
@ -88,6 +76,7 @@ import org.linphone.ui.main.chat.model.MessageDeliveryModel
import org.linphone.ui.main.chat.model.MessageModel
import org.linphone.ui.main.chat.model.MessageReactionsModel
import org.linphone.ui.main.chat.view.RichEditText
import org.linphone.ui.main.chat.viewmodel.ChatMessageLongPressViewModel
import org.linphone.ui.main.chat.viewmodel.ConversationViewModel
import org.linphone.ui.main.chat.viewmodel.ConversationViewModel.Companion.SCROLLING_POSITION_NOT_SET
import org.linphone.ui.main.chat.viewmodel.SendMessageInConversationViewModel
@ -119,12 +108,12 @@ open class ConversationFragment : SlidingPaneChildFragment() {
protected lateinit var sendMessageViewModel: SendMessageInConversationViewModel
protected lateinit var messageLongPressViewModel: ChatMessageLongPressViewModel
private lateinit var adapter: ConversationEventAdapter
private lateinit var bottomSheetAdapter: MessageBottomSheetAdapter
private var messageLongPressDialog: Dialog? = null
private val args: ConversationFragmentArgs by navArgs()
private var bottomSheetDialog: BottomSheetDialogFragment? = null
@ -364,6 +353,11 @@ open class ConversationFragment : SlidingPaneChildFragment() {
binding.sendMessageViewModel = sendMessageViewModel
observeToastEvents(sendMessageViewModel)
messageLongPressViewModel = ViewModelProvider(this)[ChatMessageLongPressViewModel::class.java]
binding.messageLongPressViewModel = messageLongPressViewModel
observeToastEvents(messageLongPressViewModel)
messageLongPressViewModel.setupEmojiPicker(binding.longPressMenu.emojiPickerBottomSheet)
binding.setBackClickListener {
goBack()
}
@ -617,7 +611,7 @@ open class ConversationFragment : SlidingPaneChildFragment() {
viewModel.fileToDisplayEvent.observe(viewLifecycleOwner) {
it.consume { model ->
if (messageLongPressDialog != null) return@consume
if (messageLongPressViewModel.visible.value == true) return@consume
Log.i("$TAG User clicked on file [${model.path}], let's display it in file viewer")
goToFileViewer(model)
}
@ -625,7 +619,7 @@ open class ConversationFragment : SlidingPaneChildFragment() {
viewModel.conferenceToJoinEvent.observe(viewLifecycleOwner) {
it.consume { conferenceUri ->
if (messageLongPressDialog != null) return@consume
if (messageLongPressViewModel.visible.value == true) return@consume
Log.i("$TAG Requesting to go to waiting room for conference URI [$conferenceUri]")
sharedViewModel.goToMeetingWaitingRoomEvent.value = Event(conferenceUri)
}
@ -633,7 +627,7 @@ open class ConversationFragment : SlidingPaneChildFragment() {
viewModel.openWebBrowserEvent.observe(viewLifecycleOwner) {
it.consume { url ->
if (messageLongPressDialog != null) return@consume
if (messageLongPressViewModel.visible.value == true) return@consume
Log.i("$TAG Requesting to open web browser on page [$url]")
try {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
@ -648,7 +642,7 @@ open class ConversationFragment : SlidingPaneChildFragment() {
viewModel.contactToDisplayEvent.observe(viewLifecycleOwner) {
it.consume { friendRefKey ->
if (messageLongPressDialog != null) return@consume
if (messageLongPressViewModel.visible.value == true) return@consume
Log.i("$TAG Navigating to contact with ref key [$friendRefKey]")
sharedViewModel.navigateToContactsEvent.value = Event(true)
sharedViewModel.showContactEvent.value = Event(friendRefKey)
@ -671,6 +665,49 @@ open class ConversationFragment : SlidingPaneChildFragment() {
}
}
messageLongPressViewModel.replyToMessageEvent.observe(viewLifecycleOwner) {
it.consume {
val model = messageLongPressViewModel.messageModel.value
if (model != null) {
sendMessageViewModel.replyToMessage(model)
// Open keyboard & focus edit text
binding.sendArea.messageToSend.showKeyboard()
}
}
}
messageLongPressViewModel.deleteMessageEvent.observe(viewLifecycleOwner) {
it.consume {
val model = messageLongPressViewModel.messageModel.value
if (model != null) {
viewModel.deleteChatMessage(model)
}
}
}
messageLongPressViewModel.forwardMessageEvent.observe(viewLifecycleOwner) {
it.consume {
val model = messageLongPressViewModel.messageModel.value
if (model != null) {
// Remove observer before setting the message to forward
// as we don't want to forward it in this chat room
sharedViewModel.messageToForwardEvent.removeObservers(viewLifecycleOwner)
sharedViewModel.messageToForwardEvent.postValue(Event(model))
if (findNavController().currentDestination?.id == R.id.conversationFragment) {
val action = ConversationFragmentDirections.actionConversationFragmentToConversationForwardMessageFragment()
findNavController().navigate(action)
}
}
}
}
messageLongPressViewModel.onDismissedEvent.observe(viewLifecycleOwner) {
it.consume {
Compatibility.removeBlurRenderEffect(binding.coordinatorLayout)
}
}
sharedViewModel.richContentUri.observe(
viewLifecycleOwner
) {
@ -1008,116 +1045,16 @@ open class ConversationFragment : SlidingPaneChildFragment() {
popupWindow.showAsDropDown(view, 0, 0, Gravity.BOTTOM)
}
private fun dismissDialog() {
messageLongPressDialog?.dismiss()
messageLongPressDialog = null
}
@UiThread
private fun showChatMessageLongPressMenu(chatMessageModel: MessageModel) {
Compatibility.setBlurRenderEffect(binding.root)
val layout: ChatBubbleLongPressMenuBinding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.chat_bubble_long_press_menu,
null,
false
)
val emojiSheetBehavior = BottomSheetBehavior.from(layout.emojiPickerBottomSheet.root)
emojiSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
emojiSheetBehavior.skipCollapsed = true
layout.root.setOnClickListener {
dismissDialog()
}
layout.setDeleteClickListener {
Log.i("$TAG Deleting message")
viewModel.deleteChatMessage(chatMessageModel)
dismissDialog()
}
layout.setCopyClickListener {
Log.i("$TAG Copying message text into clipboard")
val text = chatMessageModel.text.value?.toString()
val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val label = "Message"
clipboard.setPrimaryClip(ClipData.newPlainText(label, text))
dismissDialog()
}
layout.setPickEmojiClickListener {
Log.i("$TAG Opening emoji-picker for reaction")
emojiSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
}
layout.setResendClickListener {
Log.i("$TAG Re-sending message in error state")
chatMessageModel.resend()
dismissDialog()
}
layout.setForwardClickListener {
Log.i("$TAG Forwarding message")
// Remove observer before setting the message to forward
// as we don't want to forward it in this chat room
sharedViewModel.messageToForwardEvent.removeObservers(viewLifecycleOwner)
sharedViewModel.messageToForwardEvent.postValue(Event(chatMessageModel))
dismissDialog()
if (findNavController().currentDestination?.id == R.id.conversationFragment) {
val action = ConversationFragmentDirections.actionConversationFragmentToConversationForwardMessageFragment()
findNavController().navigate(action)
}
}
layout.setReplyClickListener {
Log.i("$TAG Updating sending area to reply to selected message")
sendMessageViewModel.replyToMessage(chatMessageModel)
dismissDialog()
// Open keyboard & focus edit text
binding.sendArea.messageToSend.showKeyboard()
}
layout.model = chatMessageModel
Compatibility.setBlurRenderEffect(binding.coordinatorLayout)
messageLongPressViewModel.setMessage(chatMessageModel)
chatMessageModel.dismissLongPressMenuEvent.observe(viewLifecycleOwner) {
dismissDialog()
}
val dialog = Dialog(requireContext(), R.style.Theme_LinphoneDialog)
dialog.apply {
setOnDismissListener {
Compatibility.removeBlurRenderEffect(binding.root)
}
requestWindowFeature(Window.FEATURE_NO_TITLE)
window?.apply {
setLayout(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT
)
setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
val d: Drawable = ColorDrawable(
requireContext().getColor(R.color.grey_300)
)
d.alpha = 102
setBackgroundDrawable(d)
}
setContentView(layout.root)
ViewCompat.setOnApplyWindowInsetsListener(layout.root) { v, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
v.updatePadding(0, 0, 0, insets.bottom)
WindowInsetsCompat.CONSUMED
it.consume {
messageLongPressViewModel.dismiss()
}
}
dialog.show()
messageLongPressDialog = dialog
messageLongPressViewModel.visible.value = true
}
@UiThread

View file

@ -0,0 +1,146 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.ui.main.chat.viewmodel
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.view.View
import androidx.annotation.UiThread
import androidx.lifecycle.MutableLiveData
import com.google.android.material.bottomsheet.BottomSheetBehavior
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatBubbleEmojiPickerBottomSheetBinding
import org.linphone.ui.GenericViewModel
import org.linphone.ui.main.chat.model.MessageModel
import org.linphone.utils.Event
class ChatMessageLongPressViewModel : GenericViewModel() {
companion object {
const val TAG = "[Chat Message LongPress ViewModel]"
}
val visible = MutableLiveData<Boolean>()
val hideForward = MutableLiveData<Boolean>()
val horizontalBias = MutableLiveData<Float>()
val isChatRoomReadOnly = MutableLiveData<Boolean>()
val messageModel = MutableLiveData<MessageModel>()
val isMessageOutgoing = MutableLiveData<Boolean>()
val isMessageInError = MutableLiveData<Boolean>()
val replyToMessageEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val forwardMessageEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val deleteMessageEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val onDismissedEvent = MutableLiveData<Event<Boolean>>()
private lateinit var emojiBottomSheet: ChatBubbleEmojiPickerBottomSheetBinding
private lateinit var emojiBottomSheetBehavior: BottomSheetBehavior<View>
init {
visible.value = false
}
@UiThread
fun setupEmojiPicker(bottomSheet: ChatBubbleEmojiPickerBottomSheetBinding) {
emojiBottomSheetBehavior = BottomSheetBehavior.from(bottomSheet.root)
emojiBottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
emojiBottomSheetBehavior.skipCollapsed = true
}
@UiThread
fun setMessage(model: MessageModel) {
isChatRoomReadOnly.value = model.chatRoomIsReadOnly
isMessageOutgoing.value = model.isOutgoing
isMessageInError.value = model.isInError
horizontalBias.value = if (model.isOutgoing) 1f else 0f
messageModel.value = model
emojiBottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
}
@UiThread
fun dismiss() {
onDismissedEvent.value = Event(true)
emojiBottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
visible.value = false
}
@UiThread
fun resend() {
Log.i("$TAG Re-sending message in error state")
messageModel.value?.resend()
dismiss()
}
@UiThread
fun reply() {
Log.i("$TAG Replying to message")
replyToMessageEvent.value = Event(true)
dismiss()
}
@UiThread
fun copyClickListener() {
Log.i("$TAG Copying message text into clipboard")
val text = messageModel.value?.text?.value?.toString()
val clipboard = coreContext.context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val label = "Message"
clipboard.setPrimaryClip(ClipData.newPlainText(label, text))
dismiss()
}
@UiThread
fun forwardClickListener() {
Log.i("$TAG Forwarding message")
forwardMessageEvent.value = Event(true)
dismiss()
}
@UiThread
fun deleteClickListener() {
Log.i("$TAG Deleting message")
deleteMessageEvent.value = Event(true)
dismiss()
}
@UiThread
fun pickEmoji() {
Log.i("$TAG Opening emoji-picker for reaction")
emojiBottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}

View file

@ -22,6 +22,12 @@
<variable
name="model"
type="org.linphone.ui.main.chat.model.MessageModel" />
<variable
name="inflatedVisibility"
type="Integer" />
<variable
name="skipGroupMargins"
type="Boolean" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
@ -30,8 +36,10 @@
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="@{model.groupedWithNextMessage ? @dimen/chat_bubble_grouped_bottom_margin : @dimen/chat_bubble_bottom_margin, default=@dimen/chat_bubble_bottom_margin}"
android:layout_marginTop="@{model.groupedWithPreviousMessage ? @dimen/chat_bubble_grouped_top_margin : @dimen/chat_bubble_top_margin, default=@dimen/chat_bubble_top_margin}">
android:layout_marginBottom="@{skipGroupMargins || model.groupedWithNextMessage ? @dimen/chat_bubble_grouped_bottom_margin : @dimen/chat_bubble_bottom_margin, default=@dimen/chat_bubble_bottom_margin}"
android:layout_marginTop="@{skipGroupMargins || model.groupedWithPreviousMessage ? @dimen/chat_bubble_grouped_top_margin : @dimen/chat_bubble_top_margin, default=@dimen/chat_bubble_top_margin}"
android:visibility="@{inflatedVisibility == View.VISIBLE ? View.VISIBLE : View.GONE}"
inflatedLifecycleOwner="@{true}">
<include
android:id="@+id/avatar"

View file

@ -6,186 +6,171 @@
<data>
<import type="android.view.View" />
<variable
name="resendClickListener"
type="View.OnClickListener" />
<variable
name="replyClickListener"
type="View.OnClickListener" />
<variable
name="copyClickListener"
type="View.OnClickListener" />
<variable
name="forwardClickListener"
type="View.OnClickListener" />
<variable
name="deleteClickListener"
type="View.OnClickListener" />
<variable
name="pickEmojiClickListener"
type="View.OnClickListener" />
<variable
name="model"
type="org.linphone.ui.main.chat.model.MessageModel" />
<variable
name="hideForward"
type="Boolean" />
name="viewModel"
type="org.linphone.ui.main.chat.viewmodel.ChatMessageLongPressViewModel" />
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:onClick="@{() -> viewModel.dismiss()}">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraint_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.Barrier
android:id="@+id/bubble_top_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="incoming_bubble, outgoing_bubble" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/bottom_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="action_reply, action_resend" />
<ViewStub
android:id="@+id/outgoing_bubble"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="12dp"
android:layout="@layout/chat_bubble_outgoing"
android:visibility="@{model.outgoing ? View.VISIBLE : View.GONE, default=gone}"
bind:inflatedVisibility="@{model.outgoing ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toBottomOf="@id/emojis"
app:layout_constraintBottom_toTopOf="@id/bottom_barrier"
app:layout_constraintHeight_max="@dimen/chat_bubble_max_height_long_press"
bind:model="@{model}"/>
<ViewStub
android:id="@+id/incoming_bubble"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="12dp"
android:layout="@layout/chat_bubble_incoming"
android:visibility="@{model.outgoing ? View.GONE : View.VISIBLE}"
bind:inflatedVisibility="@{model.outgoing ? View.GONE : View.VISIBLE}"
app:layout_constraintTop_toBottomOf="@id/emojis"
app:layout_constraintBottom_toBottomOf="@id/bottom_barrier"
app:layout_constraintHeight_max="@dimen/chat_bubble_max_height_long_press"
bind:model="@{model}"/>
android:layout_height="match_parent"
android:background="@color/gray_300_alpha_40">
<include
android:id="@+id/emojis"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:visibility="@{model.chatRoomIsReadOnly ? View.GONE : View.VISIBLE}"
android:layout_marginBottom="5dp"
android:visibility="@{viewModel.isChatRoomReadOnly ? View.GONE : View.VISIBLE}"
layout="@layout/chat_emoji_reaction_picker"
bind:model="@{model}"
bind:pickEmojiClickListener="@{pickEmojiClickListener}"
app:layout_constraintVertical_bias="1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/bubble_top_barrier"
bind:model="@{viewModel.messageModel}"
bind:pickEmojiClickListener="@{() -> viewModel.pickEmoji()}"
app:layout_constraintHorizontal_bias="@{viewModel.horizontalBias, default=0}"
app:layout_constraintWidth_max="@dimen/dialog_max_width"
app:layout_constraintBottom_toTopOf="@id/bubbles"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<View
android:id="@+id/actions_background"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="?attr/color_main2_200"
app:layout_constraintTop_toTopOf="@id/bottom_barrier"
app:layout_constraintBottom_toBottomOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/context_menu_action_label_style"
android:id="@+id/action_resend"
android:onClick="@{resendClickListener}"
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/bubbles"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="1dp"
android:text="@string/menu_resend_chat_message"
android:background="@drawable/menu_item_background"
android:drawableStart="@drawable/paper_plane_right"
android:visibility="@{model.isInError &amp;&amp; model.isOutgoing ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toTopOf="@id/action_reply"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="5dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/actions">
<androidx.appcompat.widget.AppCompatTextView
style="@style/context_menu_action_label_style"
android:id="@+id/action_reply"
android:onClick="@{replyClickListener}"
<ViewStub
android:id="@+id/outgoing_bubble"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/chat_bubble_outgoing"
android:visibility="@{viewModel.isMessageOutgoing ? View.VISIBLE : View.GONE, default=gone}"
bind:inflatedVisibility="@{viewModel.isMessageOutgoing ? View.VISIBLE : View.GONE, default=gone}"
bind:model="@{viewModel.messageModel}"
bind:skipGroupMargins="@{true}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="@dimen/chat_bubble_max_height_long_press"/>
<ViewStub
android:id="@+id/incoming_bubble"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/chat_bubble_incoming"
android:visibility="@{viewModel.isMessageOutgoing ? View.GONE : View.VISIBLE}"
bind:inflatedVisibility="@{viewModel.isMessageOutgoing ? View.GONE : View.VISIBLE}"
bind:model="@{viewModel.messageModel}"
bind:skipGroupMargins="@{true}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="@dimen/chat_bubble_max_height_long_press"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="@+id/actions"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="1dp"
android:text="@string/menu_reply_to_chat_message"
android:visibility="@{model.chatRoomIsReadOnly ? View.GONE : View.VISIBLE}"
android:background="@drawable/menu_item_background"
android:drawableStart="@drawable/reply"
app:layout_constraintBottom_toTopOf="@id/action_copy"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/context_menu_action_label_style"
android:id="@+id/action_copy"
android:onClick="@{copyClickListener}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="1dp"
android:text="@string/menu_copy_chat_message"
android:background="@drawable/menu_item_background"
android:drawableStart="@drawable/copy"
app:layout_constraintBottom_toTopOf="@id/action_forward"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/context_menu_action_label_style"
android:id="@+id/action_forward"
android:onClick="@{forwardClickListener}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="1dp"
android:text="@string/menu_forward_chat_message"
android:background="@drawable/menu_item_background"
android:drawableStart="@drawable/forward"
android:visibility="@{hideForward ? View.GONE : View.VISIBLE}"
app:layout_constraintBottom_toTopOf="@id/action_delete"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/context_menu_danger_action_label_style"
android:id="@+id/action_delete"
android:onClick="@{deleteClickListener}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="1dp"
android:text="@string/menu_delete_selected_item"
android:background="@drawable/menu_item_background"
android:drawableStart="@drawable/trash_simple"
android:orientation="vertical"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="60dp"
android:background="@drawable/shape_squircle_white_background"
app:layout_constraintWidth_max="250dp"
app:layout_constraintHorizontal_bias="@{viewModel.horizontalBias, default=0}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
app:layout_constraintEnd_toEndOf="parent">
<androidx.appcompat.widget.AppCompatTextView
style="@style/popup_menu_action_label_style"
android:id="@+id/action_resend"
android:onClick="@{() -> viewModel.resend()}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/menu_resend_chat_message"
android:drawableEnd="@drawable/paper_plane_right"
android:visibility="@{viewModel.isMessageInError &amp;&amp; viewModel.isMessageOutgoing ? View.VISIBLE : View.GONE, default=gone}" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/color_main2_200"
android:visibility="@{viewModel.isMessageInError &amp;&amp; viewModel.isMessageOutgoing ? View.VISIBLE : View.GONE, default=gone}"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/popup_menu_action_label_style"
android:id="@+id/action_reply"
android:onClick="@{() -> viewModel.reply()}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/menu_reply_to_chat_message"
android:visibility="@{viewModel.isChatRoomReadOnly ? View.GONE : View.VISIBLE}"
android:drawableEnd="@drawable/reply" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/color_main2_200"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/popup_menu_action_label_style"
android:id="@+id/action_copy"
android:onClick="@{() -> viewModel.copyClickListener()}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/menu_copy_chat_message"
android:drawableEnd="@drawable/copy" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/color_main2_200"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/popup_menu_action_label_style"
android:id="@+id/action_forward"
android:onClick="@{() -> viewModel.forwardClickListener()}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/menu_forward_chat_message"
android:drawableEnd="@drawable/forward"
android:visibility="@{viewModel.hideForward ? View.GONE : View.VISIBLE}" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/color_main2_200"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/popup_menu_danger_action_label_style"
android:id="@+id/action_delete"
android:onClick="@{() -> viewModel.deleteClickListener()}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/menu_delete_selected_item"
android:drawableEnd="@drawable/trash_simple" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<include
android:id="@+id/emoji_picker_bottom_sheet"
layout="@layout/chat_bubble_emoji_picker_bottom_sheet"
bind:model="@{model}"/>
bind:model="@{viewModel.messageModel}"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -22,6 +22,9 @@
<variable
name="model"
type="org.linphone.ui.main.chat.model.MessageModel" />
<variable
name="inflatedVisibility"
type="Integer" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
@ -31,7 +34,9 @@
android:layout_marginStart="32dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="@{model.groupedWithNextMessage ? @dimen/chat_bubble_grouped_bottom_margin : @dimen/chat_bubble_bottom_margin, default=@dimen/chat_bubble_bottom_margin}"
android:layout_marginTop="@{model.groupedWithPreviousMessage ? @dimen/chat_bubble_grouped_top_margin : @dimen/chat_bubble_top_margin, default=@dimen/chat_bubble_top_margin}">
android:layout_marginTop="@{model.groupedWithPreviousMessage ? @dimen/chat_bubble_grouped_top_margin : @dimen/chat_bubble_top_margin, default=@dimen/chat_bubble_top_margin}"
android:visibility="@{inflatedVisibility == View.VISIBLE ? View.VISIBLE : View.GONE}"
inflatedLifecycleOwner="@{true}">
<androidx.constraintlayout.widget.Group
android:id="@+id/reply_group"

View file

@ -35,301 +35,317 @@
<variable
name="sendMessageViewModel"
type="org.linphone.ui.main.chat.viewmodel.SendMessageInConversationViewModel" />
<variable
name="messageLongPressViewModel"
type="org.linphone.ui.main.chat.viewmodel.ChatMessageLongPressViewModel" />
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/color_main2_000">
android:layout_height="match_parent">
<androidx.constraintlayout.widget.Group
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="avatar, title, show_menu"
android:visibility="@{viewModel.searchBarVisible ? View.GONE : View.VISIBLE}" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/color_main2_000">
<androidx.constraintlayout.widget.Group
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="cancel_search, search, clear_field"
android:visibility="@{viewModel.searchBarVisible ? View.VISIBLE : View.GONE, default=gone}" />
<androidx.constraintlayout.widget.Group
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="avatar, title, show_menu"
android:visibility="@{viewModel.searchBarVisible ? View.GONE : View.VISIBLE}" />
<ImageView
android:id="@+id/back"
android:layout_width="wrap_content"
android:layout_height="@dimen/top_bar_height"
android:padding="15dp"
android:adjustViewBounds="true"
android:onClick="@{backClickListener}"
android:visibility="@{viewModel.isInCallConversation || viewModel.showBackButton &amp;&amp; !viewModel.searchBarVisible ? View.VISIBLE : View.GONE}"
android:src="@drawable/caret_left"
android:contentDescription="@string/content_description_go_back_icon"
app:tint="?attr/color_main1_500"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.constraintlayout.widget.Group
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="cancel_search, search, clear_field"
android:visibility="@{viewModel.searchBarVisible ? View.VISIBLE : View.GONE, default=gone}" />
<include
android:id="@+id/avatar"
android:onClick="@{goToInfoClickListener}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="5dp"
layout="@layout/contact_avatar"
bind:model="@{viewModel.avatarModel}"
app:layout_constraintStart_toEndOf="@id/back"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/back"
android:layout_width="wrap_content"
android:layout_height="@dimen/top_bar_height"
android:padding="15dp"
android:adjustViewBounds="true"
android:onClick="@{backClickListener}"
android:visibility="@{viewModel.isInCallConversation || viewModel.showBackButton &amp;&amp; !viewModel.searchBarVisible ? View.VISIBLE : View.GONE}"
android:src="@drawable/caret_left"
android:contentDescription="@string/content_description_go_back_icon"
app:tint="?attr/color_main1_500"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style"
android:id="@+id/title"
android:onClick="@{goToInfoClickListener}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="5dp"
android:maxLines="1"
android:ellipsize="end"
android:text="@{viewModel.isGroup ? viewModel.subject : viewModel.avatarModel.name, default=`John Doe`}"
android:textSize="16sp"
android:textColor="?attr/color_main2_600"
android:gravity="center_vertical"
app:layout_constraintEnd_toStartOf="@id/start_call"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toTopOf="@id/avatar"
app:layout_constraintBottom_toTopOf="@id/subtitle_barrier"/>
<include
android:id="@+id/avatar"
android:onClick="@{goToInfoClickListener}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="5dp"
layout="@layout/contact_avatar"
bind:model="@{viewModel.avatarModel}"
app:layout_constraintStart_toEndOf="@id/back"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/subtitle_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="muted, ephemeral_enabled" />
<ImageView
android:id="@+id/muted"
android:layout_width="@dimen/small_icon_size"
android:layout_height="@dimen/small_icon_size"
android:layout_marginEnd="5dp"
android:src="@drawable/bell_simple_slash"
android:contentDescription="@string/content_description_chat_muted"
android:visibility="@{viewModel.isMuted ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintEnd_toStartOf="@id/ephemeral_enabled"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintBottom_toBottomOf="@id/avatar"
app:tint="@color/main1_500"/>
<ImageView
android:id="@+id/ephemeral_enabled"
android:layout_width="@dimen/small_icon_size"
android:layout_height="@dimen/small_icon_size"
android:layout_marginEnd="5dp"
android:src="@drawable/clock_countdown"
android:contentDescription="@string/content_description_chat_ephemeral_enabled"
android:visibility="@{viewModel.ephemeralLifetime > 0L ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintStart_toEndOf="@id/muted"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintBottom_toBottomOf="@id/avatar"
app:tint="@color/main1_500"/>
<ImageView
android:id="@+id/show_menu"
android:onClick="@{showMenuClickListener}"
android:layout_width="wrap_content"
android:layout_height="@dimen/top_bar_height"
android:padding="15dp"
android:adjustViewBounds="true"
android:src="@drawable/dots_three_vertical"
android:contentDescription="@string/content_description_show_popup_menu"
android:visibility="@{viewModel.isInCallConversation ? View.GONE : View.VISIBLE}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:tint="?attr/color_main2_500"/>
<ImageView
android:id="@+id/start_call"
android:onClick="@{() -> viewModel.startCall()}"
android:layout_width="wrap_content"
android:layout_height="@dimen/top_bar_height"
android:padding="15dp"
android:src="@drawable/phone"
android:contentDescription="@string/content_description_call_start"
android:visibility="@{viewModel.isInCallConversation || viewModel.isReadOnly || viewModel.searchBarVisible ? View.GONE : View.VISIBLE}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@id/show_menu"
app:tint="?attr/color_main2_500" />
<ImageView
android:id="@+id/cancel_search"
android:onClick="@{() -> viewModel.closeSearchBar()}"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:padding="15dp"
android:src="@drawable/caret_left"
android:contentDescription="@string/content_description_cancel_filter"
app:layout_constraintBottom_toBottomOf="@id/search"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/search"
app:tint="?attr/color_main2_500" />
<com.google.android.material.textfield.TextInputLayout
style="?attr/textInputFilledStyle"
android:id="@+id/search"
android:layout_width="0dp"
android:layout_height="@dimen/top_bar_height"
android:gravity="center_vertical"
android:textColorHint="?attr/color_main2_400"
app:hintEnabled="false"
app:hintAnimationEnabled="false"
app:hintTextColor="?attr/color_main2_400"
app:boxStrokeWidth="0dp"
app:boxStrokeWidthFocused="0dp"
app:layout_constraintEnd_toStartOf="@id/clear_field"
app:layout_constraintStart_toEndOf="@id/cancel_search"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textCursorDrawable="@null"
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style"
android:id="@+id/title"
android:onClick="@{goToInfoClickListener}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="5dp"
android:maxLines="1"
android:ellipsize="end"
android:text="@{viewModel.isGroup ? viewModel.subject : viewModel.avatarModel.name, default=`John Doe`}"
android:textSize="16sp"
android:inputType="text"
android:paddingVertical="1dp"
android:text="@={viewModel.searchFilter}"
android:background="@android:color/transparent" />
android:textColor="?attr/color_main2_600"
android:gravity="center_vertical"
app:layout_constraintEnd_toStartOf="@id/start_call"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintTop_toTopOf="@id/avatar"
app:layout_constraintBottom_toTopOf="@id/subtitle_barrier"/>
</com.google.android.material.textfield.TextInputLayout>
<androidx.constraintlayout.widget.Barrier
android:id="@+id/subtitle_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="muted, ephemeral_enabled" />
<ImageView
android:id="@+id/clear_field"
android:onClick="@{() -> viewModel.clearFilter()}"
android:enabled="@{viewModel.searchFilter.length() > 0}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="6dp"
android:layout_marginEnd="9dp"
android:src="@drawable/x"
android:contentDescription="@string/content_description_clear_filter"
app:layout_constraintBottom_toBottomOf="@id/search"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/search"
app:tint="?attr/color_main2_500" />
<ImageView
android:id="@+id/muted"
android:layout_width="@dimen/small_icon_size"
android:layout_height="@dimen/small_icon_size"
android:layout_marginEnd="5dp"
android:src="@drawable/bell_simple_slash"
android:contentDescription="@string/content_description_chat_muted"
android:visibility="@{viewModel.isMuted ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintStart_toStartOf="@id/title"
app:layout_constraintEnd_toStartOf="@id/ephemeral_enabled"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintBottom_toBottomOf="@id/avatar"
app:tint="@color/main1_500"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/events_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="55dp"
android:paddingBottom="5dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/composing"/>
<ImageView
android:id="@+id/ephemeral_enabled"
android:layout_width="@dimen/small_icon_size"
android:layout_height="@dimen/small_icon_size"
android:layout_marginEnd="5dp"
android:src="@drawable/clock_countdown"
android:contentDescription="@string/content_description_chat_ephemeral_enabled"
android:visibility="@{viewModel.ephemeralLifetime > 0L ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintStart_toEndOf="@id/muted"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintBottom_toBottomOf="@id/avatar"
app:tint="@color/main1_500"/>
<ImageView
android:id="@+id/show_menu"
android:onClick="@{showMenuClickListener}"
android:layout_width="wrap_content"
android:layout_height="@dimen/top_bar_height"
android:padding="15dp"
android:adjustViewBounds="true"
android:src="@drawable/dots_three_vertical"
android:contentDescription="@string/content_description_show_popup_menu"
android:visibility="@{viewModel.isInCallConversation ? View.GONE : View.VISIBLE}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:tint="?attr/color_main2_500"/>
<ImageView
android:id="@+id/start_call"
android:onClick="@{() -> viewModel.startCall()}"
android:layout_width="wrap_content"
android:layout_height="@dimen/top_bar_height"
android:padding="15dp"
android:src="@drawable/phone"
android:contentDescription="@string/content_description_call_start"
android:visibility="@{viewModel.isInCallConversation || viewModel.isReadOnly || viewModel.searchBarVisible ? View.GONE : View.VISIBLE}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@id/show_menu"
app:tint="?attr/color_main2_500" />
<ImageView
android:id="@+id/cancel_search"
android:onClick="@{() -> viewModel.closeSearchBar()}"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:padding="15dp"
android:src="@drawable/caret_left"
android:contentDescription="@string/content_description_cancel_filter"
app:layout_constraintBottom_toBottomOf="@id/search"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/search"
app:tint="?attr/color_main2_500" />
<com.google.android.material.textfield.TextInputLayout
style="?attr/textInputFilledStyle"
android:id="@+id/search"
android:layout_width="0dp"
android:layout_height="@dimen/top_bar_height"
android:gravity="center_vertical"
android:textColorHint="?attr/color_main2_400"
app:hintEnabled="false"
app:hintAnimationEnabled="false"
app:hintTextColor="?attr/color_main2_400"
app:boxStrokeWidth="0dp"
app:boxStrokeWidthFocused="0dp"
app:layout_constraintEnd_toStartOf="@id/clear_field"
app:layout_constraintStart_toEndOf="@id/cancel_search"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textCursorDrawable="@null"
android:textSize="16sp"
android:inputType="text"
android:paddingVertical="1dp"
android:text="@={viewModel.searchFilter}"
android:background="@android:color/transparent" />
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/clear_field"
android:onClick="@{() -> viewModel.clearFilter()}"
android:enabled="@{viewModel.searchFilter.length() > 0}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="6dp"
android:layout_marginEnd="9dp"
android:src="@drawable/x"
android:contentDescription="@string/content_description_clear_filter"
app:layout_constraintBottom_toBottomOf="@id/search"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/search"
app:tint="?attr/color_main2_500" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/events_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="55dp"
android:paddingBottom="5dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/composing"/>
<include
android:id="@+id/secured_event"
android:onClick="@{endToEndEncryptedEventClickListener}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="@{viewModel.isEmpty &amp;&amp; viewModel.isEndToEndEncrypted ? View.VISIBLE : View.GONE}"
layout="@layout/chat_conversation_secured_first_event"
app:layout_constraintTop_toTopOf="@id/events_list"
app:layout_constraintStart_toStartOf="@id/events_list"
app:layout_constraintEnd_toEndOf="@id/events_list" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_800"
android:id="@+id/no_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/list_filter_no_result_found"
android:textColor="?attr/color_main2_600"
android:textSize="16sp"
android:visibility="@{viewModel.noMatchingResultForFilter ? View.VISIBLE : View.GONE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/search"
app:layout_constraintBottom_toTopOf="@id/send_area"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_300"
android:id="@+id/composing"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:paddingBottom="5dp"
android:background="?attr/color_main2_000"
android:text="@{viewModel.composingLabel, default=`John Doe is composing...`}"
android:textSize="12sp"
android:textColor="?attr/color_main2_400"
android:visibility="@{viewModel.composingLabel.length() == 0 ? View.GONE : View.VISIBLE}"
app:layout_constraintBottom_toTopOf="@id/warning_disabled_not_secured"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<include
style="@style/default_text_style"
android:id="@+id/warning_disabled_not_secured"
android:onClick="@{warningConversationDisabledClickListener}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="@{viewModel.isDisabledBecauseNotSecured ? View.VISIBLE : View.GONE, default=gone}"
layout="@layout/chat_conversation_send_area_disabled_unsecured_warning"
app:layout_constraintBottom_toTopOf="@id/send_area"/>
<include
android:id="@+id/send_area"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="@{viewModel.isReadOnly || viewModel.isDisabledBecauseNotSecured ? View.GONE : View.VISIBLE}"
layout="@layout/chat_conversation_send_area"
app:layout_constraintBottom_toBottomOf="parent"
bind:openFilePickerClickListener="@{openFilePickerClickListener}"
bind:openCameraClickListener="@{openCameraClickListener}"
bind:viewModel="@{sendMessageViewModel}"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/scroll_to_bottom"
android:onClick="@{scrollToBottomClickListener}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:src="@drawable/caret_double_down"
android:contentDescription="@string/content_description_chat_scroll_to_bottom_or_first_unread"
android:visibility="@{viewModel.isUserScrollingUp ? View.VISIBLE : View.GONE}"
app:tint="?attr/color_on_main"
app:backgroundTint="?attr/color_main1_500"
app:shapeAppearanceOverlay="@style/rounded"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/send_area" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style"
android:id="@+id/unread_messages"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginTop="-13dp"
android:layout_marginStart="-11dp"
android:gravity="center"
android:background="@drawable/shape_red_round"
android:text="@{String.valueOf(viewModel.unreadMessagesCount), default=`1`}"
android:textColor="?attr/color_on_main"
android:textSize="13sp"
android:visibility="@{viewModel.isUserScrollingUp &amp;&amp; viewModel.unreadMessagesCount > 0 ? View.VISIBLE : View.GONE}"
app:layout_constraintTop_toTopOf="@id/scroll_to_bottom"
app:layout_constraintStart_toEndOf="@id/scroll_to_bottom"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<include
android:id="@+id/secured_event"
android:onClick="@{endToEndEncryptedEventClickListener}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="@{viewModel.isEmpty &amp;&amp; viewModel.isEndToEndEncrypted ? View.VISIBLE : View.GONE}"
layout="@layout/chat_conversation_secured_first_event"
app:layout_constraintTop_toTopOf="@id/events_list"
app:layout_constraintStart_toStartOf="@id/events_list"
app:layout_constraintEnd_toEndOf="@id/events_list" />
android:id="@+id/message_bottom_sheet"
layout="@layout/chat_message_bottom_sheet" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_800"
android:id="@+id/no_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/list_filter_no_result_found"
android:textColor="?attr/color_main2_600"
android:textSize="16sp"
android:visibility="@{viewModel.noMatchingResultForFilter ? View.VISIBLE : View.GONE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/search"
app:layout_constraintBottom_toTopOf="@id/send_area"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_300"
android:id="@+id/composing"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:paddingBottom="5dp"
android:background="?attr/color_main2_000"
android:text="@{viewModel.composingLabel, default=`John Doe is composing...`}"
android:textSize="12sp"
android:textColor="?attr/color_main2_400"
android:visibility="@{viewModel.composingLabel.length() == 0 ? View.GONE : View.VISIBLE}"
app:layout_constraintBottom_toTopOf="@id/warning_disabled_not_secured"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<include
style="@style/default_text_style"
android:id="@+id/warning_disabled_not_secured"
android:onClick="@{warningConversationDisabledClickListener}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="@{viewModel.isDisabledBecauseNotSecured ? View.VISIBLE : View.GONE, default=gone}"
layout="@layout/chat_conversation_send_area_disabled_unsecured_warning"
app:layout_constraintBottom_toTopOf="@id/send_area"/>
<include
android:id="@+id/send_area"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="@{viewModel.isReadOnly || viewModel.isDisabledBecauseNotSecured ? View.GONE : View.VISIBLE}"
layout="@layout/chat_conversation_send_area"
app:layout_constraintBottom_toBottomOf="parent"
bind:openFilePickerClickListener="@{openFilePickerClickListener}"
bind:openCameraClickListener="@{openCameraClickListener}"
bind:viewModel="@{sendMessageViewModel}"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/scroll_to_bottom"
android:onClick="@{scrollToBottomClickListener}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:src="@drawable/caret_double_down"
android:contentDescription="@string/content_description_chat_scroll_to_bottom_or_first_unread"
android:visibility="@{viewModel.isUserScrollingUp ? View.VISIBLE : View.GONE}"
app:tint="?attr/color_on_main"
app:backgroundTint="?attr/color_main1_500"
app:shapeAppearanceOverlay="@style/rounded"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/send_area" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style"
android:id="@+id/unread_messages"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginTop="-13dp"
android:layout_marginStart="-11dp"
android:gravity="center"
android:background="@drawable/shape_red_round"
android:text="@{String.valueOf(viewModel.unreadMessagesCount), default=`1`}"
android:textColor="?attr/color_on_main"
android:textSize="13sp"
android:visibility="@{viewModel.isUserScrollingUp &amp;&amp; viewModel.unreadMessagesCount > 0 ? View.VISIBLE : View.GONE}"
app:layout_constraintTop_toTopOf="@id/scroll_to_bottom"
app:layout_constraintStart_toEndOf="@id/scroll_to_bottom"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<include
android:id="@+id/message_bottom_sheet"
layout="@layout/chat_message_bottom_sheet" />
android:id="@+id/long_press_menu"
android:visibility="@{messageLongPressViewModel.visible ? View.VISIBLE : View.GONE, default=gone}"
bind:viewModel="@{messageLongPressViewModel}"
layout="@layout/chat_bubble_long_press_menu" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</FrameLayout>
</layout>

View file

@ -25,6 +25,7 @@
<color name="gray_200">#EDEDED</color>
<color name="gray_200_night">#1F1F1F</color>
<color name="gray_300">#C9C9C9</color>
<color name="gray_300_alpha_40">#66C9C9C9</color>
<color name="gray_400">#949494</color>
<color name="gray_500">#4E4E4E</color>
<color name="gray_600">#2E3030</color>

View file

@ -117,6 +117,26 @@
<item name="android:drawableTint">?attr/color_danger_500</item>
<item name="android:drawablePadding">8dp</item>
</style>
<style name="popup_menu_action_label_style">
<item name="android:fontFamily">@font/noto_sans</item>
<item name="android:textSize">14sp</item>
<item name="android:textColor">?attr/color_main2_500</item>
<item name="android:maxLines">1</item>
<item name="android:ellipsize">end</item>
<item name="android:padding">15dp</item>
<item name="android:drawableTint">?attr/color_main2_500</item>
<item name="android:drawablePadding">8dp</item>
</style>
<style name="popup_menu_danger_action_label_style">
<item name="android:fontFamily">@font/noto_sans</item>
<item name="android:textSize">14sp</item>
<item name="android:textColor">?attr/color_danger_500</item>
<item name="android:maxLines">1</item>
<item name="android:ellipsize">end</item>
<item name="android:padding">15dp</item>
<item name="android:drawableTint">?attr/color_danger_500</item>
<item name="android:drawablePadding">8dp</item>
</style>
<style name="call_start_numpad_digits_style">
<item name="android:fontFamily">@font/noto_sans</item>
<item name="android:textColor">@color/gray_main2_600</item>