Showing files in square area like media when more than one in a single chat message

This commit is contained in:
Sylvain Berfini 2025-05-19 13:09:10 +02:00
parent 7aae03f1f9
commit 4cb7ea1965
21 changed files with 616 additions and 436 deletions

View file

@ -30,10 +30,11 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.linphone.R
import org.linphone.databinding.ChatDocumentContentListCellBinding
import org.linphone.databinding.ChatBubbleSingleFileContentBinding
import org.linphone.databinding.ChatMediaContentGridCellBinding
import org.linphone.databinding.MeetingsListDecorationBinding
import org.linphone.ui.main.chat.model.FileModel
import org.linphone.utils.AppUtils
import org.linphone.utils.HeaderAdapter
class ConversationsFilesAdapter :
@ -46,6 +47,9 @@ class ConversationsFilesAdapter :
const val DOCUMENT_FILE = 2
}
private val topBottomPadding = AppUtils.getDimension(R.dimen.chat_documents_list_padding_top_bottom).toInt()
private val startEndPadding = AppUtils.getDimension(R.dimen.chat_documents_list_padding_start_end).toInt()
override fun displayHeaderForPosition(position: Int): Boolean {
if (position == 0) return true
@ -89,15 +93,16 @@ class ConversationsFilesAdapter :
}
private fun createDocumentFileViewHolder(parent: ViewGroup): RecyclerView.ViewHolder {
val binding: ChatDocumentContentListCellBinding = DataBindingUtil.inflate(
val binding: ChatBubbleSingleFileContentBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.chat_document_content_list_cell,
R.layout.chat_bubble_single_file_content,
parent,
false
)
val viewHolder = DocumentFileViewHolder(binding)
binding.apply {
lifecycleOwner = parent.findViewTreeLifecycleOwner()
root.setPadding(startEndPadding, topBottomPadding, startEndPadding, topBottomPadding)
}
return viewHolder
}
@ -123,7 +128,7 @@ class ConversationsFilesAdapter :
}
inner class DocumentFileViewHolder(
val binding: ChatDocumentContentListCellBinding
val binding: ChatBubbleSingleFileContentBinding
) : RecyclerView.ViewHolder(binding.root) {
@UiThread
fun bind(fileModel: FileModel) {

View file

@ -128,22 +128,20 @@ open class ConversationFragment : SlidingPaneChildFragment() {
)
) { list ->
sendMessageViewModel.closeFilePickerBottomSheet()
if (list.isNotEmpty()) {
val filesToAttach = arrayListOf<String>()
lifecycleScope.launch {
for (uri in list) {
lifecycleScope.launch {
withContext(Dispatchers.IO) {
val path = FileUtils.getFilePath(requireContext(), uri, false)
Log.i("$TAG Picked file [$uri] matching path is [$path]")
if (path != null) {
withContext(Dispatchers.Main) {
sendMessageViewModel.addAttachment(path)
}
}
withContext(Dispatchers.IO) {
val path = FileUtils.getFilePath(requireContext(), uri, false)
Log.i("$TAG Picked file [$uri] matching path is [$path]")
if (path != null) {
filesToAttach.add(path)
}
}
}
} else {
Log.w("$TAG No file picked")
withContext(Dispatchers.Main) {
sendMessageViewModel.addAttachments(filesToAttach)
}
}
}
@ -153,16 +151,20 @@ open class ConversationFragment : SlidingPaneChildFragment() {
ActivityResultContracts.OpenMultipleDocuments()
) { files ->
sendMessageViewModel.closeFilePickerBottomSheet()
for (fileUri in files) {
lifecycleScope.launch {
val filesToAttach = arrayListOf<String>()
lifecycleScope.launch {
for (fileUri in files) {
val path = FileUtils.getFilePath(requireContext(), fileUri, false).orEmpty()
if (path.isNotEmpty()) {
Log.i("$TAG Picked file [$path]")
sendMessageViewModel.addAttachment(path)
filesToAttach.add(path)
} else {
Log.e("$TAG Failed to pick file [$fileUri]")
}
}
withContext(Dispatchers.Main) {
sendMessageViewModel.addAttachments(filesToAttach)
}
}
}
@ -174,7 +176,7 @@ open class ConversationFragment : SlidingPaneChildFragment() {
if (path != null) {
if (captured) {
Log.i("$TAG Image was captured and saved in [$path]")
sendMessageViewModel.addAttachment(path)
sendMessageViewModel.addAttachments(arrayListOf(path))
} else {
Log.w("$TAG Image capture was aborted")
lifecycleScope.launch {
@ -892,7 +894,7 @@ open class ConversationFragment : SlidingPaneChildFragment() {
Log.i("$TAG Rich content URI [$uri] matching path is [$path]")
if (path != null) {
withContext(Dispatchers.Main) {
sendMessageViewModel.addAttachment(path)
sendMessageViewModel.addAttachments(arrayListOf(path))
}
}
}
@ -920,7 +922,7 @@ open class ConversationFragment : SlidingPaneChildFragment() {
if (files.isNotEmpty()) {
Log.i("$TAG Found [${files.size}] files to share from intent")
for (path in files) {
sendMessageViewModel.addAttachment(path)
sendMessageViewModel.addAttachments(arrayListOf(path))
}
sharedViewModel.filesToShareFromIntent.value = arrayListOf()

View file

@ -91,7 +91,6 @@ class FileModel
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
init {
mediaPreviewAvailable.postValue(false)
updateTransferProgress(-1)
formattedFileSize.postValue(FileUtils.bytesToDisplayableSize(fileSize))

View file

@ -409,16 +409,11 @@ class MessageModel
filesList.postValue(arrayListOf())
var displayableContentFound = false
var filesContentCount = 0
var contentIndex = 0
val filesPath = arrayListOf<FileModel>()
val contents = chatMessage.contents
allFilesDownloaded = true
val notMediaContent = contents.find {
it.isIcalendar || it.isVoiceRecording || (it.isText && !it.isFile) || it.isFileTransfer || (it.isFile && !(it.type == "video" || it.type == "image"))
}
val allContentsAreMedia = notMediaContent == null
val exactly4Contents = contents.size == 4
for (content in contents) {
@ -443,7 +438,7 @@ class MessageModel
} else {
if (content.isFile) {
Log.d("$TAG Found file content with type [${content.type}/${content.subtype}]")
filesContentCount += 1
contentIndex += 1
checkAndRepairFilePathIfNeeded(content)
@ -462,7 +457,7 @@ class MessageModel
"$TAG Found file ready to be displayed [$path] with MIME [${content.type}/${content.subtype}] for message [${chatMessage.messageId}]"
)
val wrapBefore = allContentsAreMedia && exactly4Contents && filesContentCount == 3
val wrapBefore = exactly4Contents && contentIndex == 3
val fileSize = content.fileSize.toLong()
val timestamp = content.creationTimestamp
val fileModel = FileModel(
@ -488,7 +483,7 @@ class MessageModel
"$TAG Found file content (not downloaded yet) with type [${content.type}/${content.subtype}] and name [${content.name}]"
)
allFilesDownloaded = false
filesContentCount += 1
contentIndex += 1
val name = content.name ?: ""
val timestamp = content.creationTimestamp
if (name.isNotEmpty()) {

View file

@ -379,35 +379,40 @@ class SendMessageInConversationViewModel
}
@UiThread
fun addAttachment(file: String) {
if (attachments.value.orEmpty().size >= MAX_FILES_TO_ATTACH) {
Log.w(
"$TAG Max number of attachments [$MAX_FILES_TO_ATTACH] reached, file [$file] won't be attached"
)
showRedToast(R.string.conversation_maximum_number_of_attachments_reached, R.drawable.warning_circle)
viewModelScope.launch {
Log.i("$TAG Deleting temporary file [$file]")
FileUtils.deleteFile(file)
}
return
}
fun addAttachments(files: ArrayList<String>) {
val list = arrayListOf<FileModel>()
list.addAll(attachments.value.orEmpty())
val fileName = FileUtils.getNameFromFilePath(file)
val timestamp = System.currentTimeMillis() / 1000
val model = FileModel(file, fileName, 0, timestamp, false, file, false) { model ->
removeAttachment(model.path)
}
for (file in files) {
if (list.size >= MAX_FILES_TO_ATTACH) {
Log.w(
"$TAG Max number of attachments [$MAX_FILES_TO_ATTACH] reached, file [$file] won't be attached"
)
showRedToast(
R.string.conversation_maximum_number_of_attachments_reached,
R.drawable.warning_circle
)
viewModelScope.launch {
Log.i("$TAG Deleting temporary file [$file]")
FileUtils.deleteFile(file)
}
return
}
list.add(model)
val fileName = FileUtils.getNameFromFilePath(file)
val timestamp = System.currentTimeMillis() / 1000
val model = FileModel(file, fileName, 0, timestamp, false, file, false) { model ->
removeAttachment(model.path)
}
list.add(model)
}
attachments.value = list
maxNumberOfAttachmentsReached.value = list.size >= MAX_FILES_TO_ATTACH
if (list.isNotEmpty()) {
isFileAttachmentsListOpen.value = true
Log.i("$TAG [${list.size}] attachment(s) added")
Log.i("$TAG [${files.size}] attachment(s) added, in total ${list.size}] file(s) are attached")
} else {
Log.w("$TAG No attachment to display!")
}

View file

@ -330,7 +330,7 @@ private fun loadImageForChatBubble(
val isVideo = FileUtils.isExtensionVideo(file)
if (isImage || isVideo) {
val dimen = if (grid) {
imageView.resources.getDimension(R.dimen.chat_bubble_grid_image_size).toInt()
imageView.resources.getDimension(R.dimen.chat_bubble_grid_file_size).toInt()
} else {
imageView.resources.getDimension(R.dimen.chat_bubble_big_image_max_size).toInt()
}

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:topLeftRadius="5dp" android:topRightRadius="5dp" />
<solid android:color="?attr/color_chat_bubble_file" />
</shape>

View file

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

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:topRightRadius="10dp" android:bottomRightRadius="10dp" />
<solid android:color="?attr/color_main2_000" />
<solid android:color="?attr/color_main2_000"/>
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<corners android:radius="15dp" />
<solid android:color="?attr/color_grey_100"/>
</shape>

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
xmlns:bind="http://schemas.android.com/tools">
<data>
<import type="android.view.View" />
@ -13,205 +13,32 @@
type="org.linphone.ui.main.chat.model.FileModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_wrapBefore="@{model.flexboxLayoutWrapBefore}"
android:padding="1dp">
app:layout_wrapBefore="@{model.flexboxLayoutWrapBefore}">
<androidx.constraintlayout.widget.Group
android:id="@+id/file_group"
<include
android:id="@+id/media_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{model.isImage || model.isVideoPreview ? View.GONE : View.VISIBLE}"
app:constraint_referenced_ids="file_name, file_background" />
<androidx.constraintlayout.widget.Group
android:id="@+id/download_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{model.transferProgress == -1 || model.transferProgress >= 100 ? View.GONE : View.VISIBLE}"
app:constraint_referenced_ids="transfer_progress, transfer_progress_label" />
<ImageView
android:id="@+id/broken_media_icon"
android:onClick="@{() -> model.onClick()}"
android:layout_width="@dimen/chat_bubble_grid_image_size"
android:layout_height="@dimen/chat_bubble_grid_image_size"
android:adjustViewBounds="true"
android:padding="18dp"
android:background="@drawable/shape_squircle_file_background_5dp"
android:visibility="@{model.mediaPreviewAvailable ? View.GONE : View.VISIBLE, default=gone}"
android:contentDescription="@{model.isVideoPreview ? @string/content_description_chat_bubble_video : @string/content_description_chat_bubble_image}"
android:src="@{model.isVideoPreview ? @drawable/file_video : @drawable/file_image, default=@drawable/file_video}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:tint="?attr/color_main2_600" />
<ImageView
android:id="@+id/image"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:layout_width="@dimen/chat_bubble_grid_image_size"
android:layout_height="@dimen/chat_bubble_grid_image_size"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:contentDescription="@{model.isVideoPreview ? @string/content_description_chat_bubble_video : @string/content_description_chat_bubble_image}"
layout="@layout/chat_bubble_media_grid_cell"
android:visibility="@{model.isImage || model.isVideoPreview ? View.VISIBLE : View.GONE}"
coilBubbleGrid="@{model.mediaPreview}"
coilBubbleFallback="@{brokenMediaIcon}"
bind:model="@{model}"
bind:onLongClickListener="@{onLongClickListener}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_600"
android:id="@+id/video_duration"
<include
android:id="@+id/file_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@{model.audioVideoDuration, default=`00:42`}"
android:textColor="@{model.isVideoPreview ? @color/bc_white : @color/main2_600}"
android:textSize="12sp"
android:visibility="@{model.isVideoPreview &amp;&amp; model.audioVideoDuration.length() > 0 ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<ImageView
android:id="@+id/video_preview"
android:onClick="@{() -> model.onClick()}"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:src="@drawable/play_fill"
android:contentDescription="@null"
android:visibility="@{model.isVideoPreview &amp;&amp; model.mediaPreviewAvailable ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toTopOf="@id/image"
app:layout_constraintBottom_toBottomOf="@id/image"
app:layout_constraintStart_toStartOf="@id/image"
app:layout_constraintEnd_toEndOf="@id/image"
app:tint="@color/bc_white" />
<View
android:id="@+id/left_background"
android:layout_width="@dimen/chat_bubble_grid_image_size"
android:layout_height="@dimen/chat_bubble_grid_image_size"
android:background="@drawable/shape_squircle_file_background_left"
android:visibility="@{model.isImage || model.isVideoPreview ? View.INVISIBLE : View.VISIBLE}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/file_icon"
android:layout_width="0dp"
android:layout_height="0dp"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:adjustViewBounds="true"
android:padding="18dp"
android:contentDescription="@string/content_description_chat_bubble_file"
android:src="@{model.isWaitingToBeDownloaded ? @drawable/download_simple : model.isPdf ? @drawable/file_pdf : model.isAudio ? @drawable/file_audio : @drawable/file, default=@drawable/file_pdf}"
android:visibility="@{model.isImage || model.isVideoPreview || (model.transferProgress >= 0 &amp;&amp; model.transferProgress &lt; 100) ? View.GONE : View.VISIBLE}"
app:layout_constraintTop_toTopOf="@id/left_background"
app:layout_constraintBottom_toBottomOf="@id/left_background"
app:layout_constraintStart_toStartOf="@id/left_background"
app:layout_constraintEnd_toEndOf="@id/left_background"
app:tint="?attr/color_main2_600" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_600"
android:id="@+id/audio_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@{model.audioVideoDuration, default=`00:42`}"
android:textColor="?attr/color_main2_600"
android:textSize="12sp"
android:visibility="@{model.isAudio &amp;&amp; model.audioVideoDuration.length() > 0 ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toBottomOf="@id/left_background"
app:layout_constraintStart_toStartOf="@id/left_background"/>
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/transfer_progress"
android:layout_width="@dimen/chat_bubble_grid_image_size"
android:layout_height="@dimen/chat_bubble_grid_image_size"
android:indeterminate="false"
android:progress="@{model.transferProgress}"
android:max="100"
app:indicatorSize="50dp"
app:trackColor="?attr/color_main1_100"
app:indicatorColor="?attr/color_main1_500"
app:layout_constraintTop_toTopOf="@id/left_background"
app:layout_constraintBottom_toBottomOf="@id/left_background"
app:layout_constraintStart_toStartOf="@id/left_background"
app:layout_constraintEnd_toEndOf="@id/left_background"
tools:progress="40" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/main_page_title_style"
android:id="@+id/transfer_progress_label"
android:layout_width="0dp"
android:layout_height="0dp"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:text="@{model.transferProgressLabel, default=`40%`}"
android:textSize="16sp"
android:textAlignment="center"
android:textColor="?attr/color_main1_500"
app:layout_constraintTop_toTopOf="@id/transfer_progress"
app:layout_constraintBottom_toBottomOf="@id/transfer_progress"
app:layout_constraintStart_toStartOf="@id/transfer_progress"
app:layout_constraintEnd_toEndOf="@id/transfer_progress" />
<View
android:id="@+id/file_background"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:layout_width="@dimen/chat_bubble_grid_file_width"
android:layout_height="0dp"
android:background="@drawable/shape_squircle_white_right"
app:layout_constraintTop_toTopOf="@id/left_background"
app:layout_constraintBottom_toBottomOf="@id/left_background"
app:layout_constraintStart_toEndOf="@id/left_background"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_700"
android:id="@+id/file_name"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="@{model.fileName, default=`Lorem_ipsum.pdf`}"
android:textColor="?attr/color_main2_600"
android:textSize="13sp"
android:maxLines="1"
android:ellipsize="middle"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/left_background"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/file_background"
app:layout_constraintBottom_toTopOf="@id/file_size"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_300"
android:id="@+id/file_size"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@{model.formattedFileSize, default=`42 kb`}"
android:textColor="?attr/color_main2_600"
android:textSize="12sp"
android:maxLines="1"
android:ellipsize="end"
android:visibility="@{model.isImage || model.isVideoPreview || model.fileSize == 0 ? View.GONE : View.VISIBLE, default=gone}"
app:layout_constraintStart_toStartOf="@id/file_name"
app:layout_constraintEnd_toEndOf="@id/file_name"
app:layout_constraintTop_toBottomOf="@id/file_name"
app:layout_constraintBottom_toBottomOf="@id/file_background"/>
layout="@layout/chat_bubble_file_grid_cell"
android:visibility="@{!model.isImage &amp;&amp; !model.isVideoPreview ? View.VISIBLE : View.GONE}"
bind:model="@{model}"
bind:onLongClickListener="@{onLongClickListener}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,155 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.view.View" />
<variable
name="onLongClickListener"
type="View.OnLongClickListener" />
<variable
name="model"
type="org.linphone.ui.main.chat.model.FileModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="1dp">
<View
android:id="@+id/background"
android:layout_width="@dimen/chat_bubble_grid_file_size"
android:layout_height="@dimen/chat_bubble_grid_file_size"
android:background="@drawable/shape_squircle_file_bubble_background"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<androidx.constraintlayout.widget.Group
android:id="@+id/download_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{model.transferProgress == -1 || model.transferProgress >= 100 ? View.GONE : View.VISIBLE, default=gone}"
app:constraint_referenced_ids="transfer_progress, transfer_progress_label" />
<View
android:id="@+id/top_background"
android:layout_width="0dp"
android:layout_height="24dp"
android:background="@drawable/shape_squircle_file_background_top"
app:layout_constraintStart_toStartOf="@id/background"
app:layout_constraintEnd_toEndOf="@id/background"
app:layout_constraintTop_toTopOf="@id/background"
app:layout_constraintBottom_toBottomOf="@id/file_icon"/>
<ImageView
android:id="@+id/file_icon"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:adjustViewBounds="true"
android:padding="2dp"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:contentDescription="@string/content_description_chat_bubble_file"
android:src="@{model.isWaitingToBeDownloaded ? @drawable/download_simple : model.isPdf ? @drawable/file_pdf : model.isAudio ? @drawable/file_audio : @drawable/file, default=@drawable/file_pdf}"
app:layout_constraintStart_toStartOf="@id/background"
app:layout_constraintEnd_toEndOf="@id/background"
app:layout_constraintTop_toTopOf="@id/top_background"
app:layout_constraintBottom_toBottomOf="@id/top_background"
app:tint="?attr/color_main2_600" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_700"
android:id="@+id/file_name"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:text="@{model.fileName, default=`Lorem_ipsum.pdf`}"
android:textColor="?attr/color_main2_600"
android:textAlignment="center"
android:textSize="12sp"
android:maxLines="2"
android:ellipsize="end"
android:visibility="@{model.transferProgress >= 0 &amp;&amp; model.transferProgress &lt; 100 ? View.INVISIBLE : View.VISIBLE}"
app:layout_constraintStart_toStartOf="@id/background"
app:layout_constraintEnd_toEndOf="@id/background"
app:layout_constraintTop_toBottomOf="@id/file_icon"
app:layout_constraintBottom_toTopOf="@id/bottom_barrier"/>
<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="file_size, audio_duration" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_300"
android:id="@+id/file_size"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:text="@{model.formattedFileSize, default=`42 kb`}"
android:textColor="?attr/color_main2_600"
android:textSize="12sp"
android:maxLines="1"
android:ellipsize="end"
android:visibility="@{(model.transferProgress >= 0 &amp;&amp; model.transferProgress &lt; 100) || model.fileSize == 0 ? View.GONE : View.VISIBLE, default=gone}"
app:layout_constraintEnd_toEndOf="@id/background"
app:layout_constraintBottom_toBottomOf="@id/background"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_600"
android:id="@+id/audio_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@{model.audioVideoDuration, default=`00:42`}"
android:textColor="?attr/color_main2_600"
android:textSize="11sp"
android:visibility="@{model.isAudio &amp;&amp; model.audioVideoDuration.length() > 0 ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toBottomOf="@id/background"
app:layout_constraintStart_toStartOf="parent"/>
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/transfer_progress"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:indeterminate="false"
android:progress="@{model.transferProgress}"
android:max="100"
app:indicatorSize="50dp"
app:trackColor="?attr/color_main1_100"
app:indicatorColor="?attr/color_main1_500"
app:layout_constraintTop_toBottomOf="@id/top_background"
app:layout_constraintBottom_toBottomOf="@id/background"
app:layout_constraintStart_toStartOf="@id/background"
app:layout_constraintEnd_toEndOf="@id/background"
tools:progress="40" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/main_page_title_style"
android:id="@+id/transfer_progress_label"
android:layout_width="0dp"
android:layout_height="0dp"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:text="@{model.transferProgressLabel, default=`40%`}"
android:textSize="16sp"
android:textAlignment="center"
android:textColor="?attr/color_main1_500"
app:layout_constraintTop_toTopOf="@id/transfer_progress"
app:layout_constraintBottom_toBottomOf="@id/transfer_progress"
app:layout_constraintStart_toStartOf="@id/transfer_progress"
app:layout_constraintEnd_toEndOf="@id/transfer_progress" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -175,7 +175,7 @@
android:onLongClick="@{onLongClickListener}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{model.filesList.size() >= 2 || (model.filesList.size() == 1 &amp;&amp; !model.firstFileModel.isMedia) ? View.VISIBLE : View.GONE, default=gone}"
android:visibility="@{model.filesList.size() >= 2 ? View.VISIBLE : View.GONE, default=gone}"
app:alignItems="center"
app:flexWrap="wrap"
app:justifyContent="@{model.outgoing ? JustifyContent.FLEX_END : JustifyContent.FLEX_START}"
@ -193,6 +193,16 @@
bind:model="@{model.firstFileModel}"
bind:onLongClickListener="@{onLongClickListener}"/>
<ViewStub
android:id="@+id/single_file"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout="@layout/chat_bubble_single_file_content"
android:visibility="@{model.filesList.size() == 1 &amp;&amp; !model.firstFileModel.isMedia ? View.VISIBLE : View.GONE, default=gone}"
bind:inflatedVisibility="@{model.filesList.size() == 1 &amp;&amp; !model.firstFileModel.isMedia? View.VISIBLE : View.GONE}"
bind:model="@{model.firstFileModel}"
bind:onLongClickListener="@{onLongClickListener}"/>
<ViewStub
android:id="@+id/meeting_info"
android:layout_width="@dimen/chat_bubble_meeting_invite_width"

View file

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View" />
<variable
name="onLongClickListener"
type="View.OnLongClickListener" />
<variable
name="model"
type="org.linphone.ui.main.chat.model.FileModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="1dp">
<ImageView
android:id="@+id/broken_media_icon"
android:onClick="@{() -> model.onClick()}"
android:layout_width="@dimen/chat_bubble_grid_file_size"
android:layout_height="@dimen/chat_bubble_grid_file_size"
android:adjustViewBounds="true"
android:padding="18dp"
android:background="@drawable/shape_squircle_file_background_5dp"
android:visibility="@{!model.isMedia || model.mediaPreviewAvailable ? View.GONE : View.VISIBLE, default=gone}"
android:contentDescription="@{model.isVideoPreview ? @string/content_description_chat_bubble_video : @string/content_description_chat_bubble_image}"
android:src="@{model.isVideoPreview ? @drawable/file_video : @drawable/file_image, default=@drawable/file_video}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:tint="?attr/color_main2_600" />
<ImageView
android:id="@+id/image"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:layout_width="@dimen/chat_bubble_grid_file_size"
android:layout_height="@dimen/chat_bubble_grid_file_size"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:contentDescription="@{model.isVideoPreview ? @string/content_description_chat_bubble_video : @string/content_description_chat_bubble_image}"
android:visibility="@{model.isImage || model.isVideoPreview ? View.VISIBLE : View.GONE}"
coilBubbleGrid="@{model.mediaPreview}"
coilBubbleFallback="@{brokenMediaIcon}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_600"
android:id="@+id/video_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@{model.audioVideoDuration, default=`00:42`}"
android:textColor="@{model.isVideoPreview ? @color/bc_white : @color/main2_600}"
android:textSize="12sp"
android:visibility="@{model.isVideoPreview &amp;&amp; model.audioVideoDuration.length() > 0 ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<ImageView
android:id="@+id/video_preview"
android:onClick="@{() -> model.onClick()}"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:src="@drawable/play_fill"
android:contentDescription="@null"
android:visibility="@{model.isVideoPreview &amp;&amp; model.mediaPreviewAvailable ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toTopOf="@id/image"
app:layout_constraintBottom_toBottomOf="@id/image"
app:layout_constraintStart_toStartOf="@id/image"
app:layout_constraintEnd_toEndOf="@id/image"
app:tint="@color/bc_white" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -147,7 +147,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onLongClick="@{onLongClickListener}"
android:visibility="@{model.filesList.size() >= 2 || (model.filesList.size() == 1 &amp;&amp; !model.firstFileModel.isMedia) ? View.VISIBLE : View.GONE, default=gone}"
android:visibility="@{model.filesList.size() >= 2 ? View.VISIBLE : View.GONE, default=gone}"
app:alignItems="center"
app:flexWrap="wrap"
app:justifyContent="@{model.outgoing ? JustifyContent.FLEX_END : JustifyContent.FLEX_START}"
@ -165,6 +165,16 @@
bind:model="@{model.firstFileModel}"
bind:onLongClickListener="@{onLongClickListener}"/>
<ViewStub
android:id="@+id/single_file"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout="@layout/chat_bubble_single_file_content"
android:visibility="@{model.filesList.size() == 1 &amp;&amp; !model.firstFileModel.isMedia ? View.VISIBLE : View.GONE, default=gone}"
bind:inflatedVisibility="@{model.filesList.size() == 1 &amp;&amp; !model.firstFileModel.isMedia? View.VISIBLE : View.GONE}"
bind:model="@{model.firstFileModel}"
bind:onLongClickListener="@{onLongClickListener}"/>
<ViewStub
android:id="@+id/meeting_info"
android:layout_width="@dimen/chat_bubble_meeting_invite_width"

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="android.view.View" />
<variable
name="onLongClickListener"
type="View.OnLongClickListener" />
<variable
name="model"
type="org.linphone.ui.main.chat.model.FileModel" />
<variable
name="inflatedVisibility"
type="Integer" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="1dp"
android:visibility="@{inflatedVisibility == View.VISIBLE ? View.VISIBLE : View.GONE}"
inflatedLifecycleOwner="@{true}">
<androidx.constraintlayout.widget.Group
android:id="@+id/file_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{model.isImage || model.isVideoPreview ? View.GONE : View.VISIBLE}"
app:constraint_referenced_ids="file_name, file_background" />
<androidx.constraintlayout.widget.Group
android:id="@+id/download_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{model.transferProgress == -1 || model.transferProgress >= 100 ? View.GONE : View.VISIBLE, default=gone}"
app:constraint_referenced_ids="transfer_progress, transfer_progress_label" />
<View
android:id="@+id/left_background"
android:layout_width="@dimen/chat_bubble_grid_file_size"
android:layout_height="@dimen/chat_bubble_grid_file_size"
android:background="@drawable/shape_squircle_file_background_left"
android:visibility="@{model.isImage || model.isVideoPreview ? View.INVISIBLE : View.VISIBLE}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/file_icon"
android:layout_width="0dp"
android:layout_height="0dp"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:adjustViewBounds="true"
android:padding="18dp"
android:contentDescription="@string/content_description_chat_bubble_file"
android:src="@{model.isWaitingToBeDownloaded ? @drawable/download_simple : model.isPdf ? @drawable/file_pdf : model.isAudio ? @drawable/file_audio : @drawable/file, default=@drawable/file_pdf}"
android:visibility="@{model.isImage || model.isVideoPreview || (model.transferProgress >= 0 &amp;&amp; model.transferProgress &lt; 100) ? View.GONE : View.VISIBLE}"
app:layout_constraintTop_toTopOf="@id/left_background"
app:layout_constraintBottom_toBottomOf="@id/left_background"
app:layout_constraintStart_toStartOf="@id/left_background"
app:layout_constraintEnd_toEndOf="@id/left_background"
app:tint="?attr/color_main2_600" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_600"
android:id="@+id/audio_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@{model.audioVideoDuration, default=`00:42`}"
android:textColor="?attr/color_main2_600"
android:textSize="12sp"
android:visibility="@{model.isAudio &amp;&amp; model.audioVideoDuration.length() > 0 ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toBottomOf="@id/left_background"
app:layout_constraintStart_toStartOf="@id/left_background"/>
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/transfer_progress"
android:layout_width="@dimen/chat_bubble_grid_file_size"
android:layout_height="@dimen/chat_bubble_grid_file_size"
android:indeterminate="false"
android:progress="@{model.transferProgress}"
android:max="100"
app:indicatorSize="50dp"
app:trackColor="?attr/color_main1_100"
app:indicatorColor="?attr/color_main1_500"
app:layout_constraintTop_toTopOf="@id/left_background"
app:layout_constraintBottom_toBottomOf="@id/left_background"
app:layout_constraintStart_toStartOf="@id/left_background"
app:layout_constraintEnd_toEndOf="@id/left_background"
tools:progress="40" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/main_page_title_style"
android:id="@+id/transfer_progress_label"
android:layout_width="0dp"
android:layout_height="0dp"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:text="@{model.transferProgressLabel, default=`40%`}"
android:textSize="16sp"
android:textAlignment="center"
android:textColor="?attr/color_main1_500"
app:layout_constraintTop_toTopOf="@id/transfer_progress"
app:layout_constraintBottom_toBottomOf="@id/transfer_progress"
app:layout_constraintStart_toStartOf="@id/transfer_progress"
app:layout_constraintEnd_toEndOf="@id/transfer_progress" />
<View
android:id="@+id/file_background"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@drawable/shape_squircle_file_bubble_right_background"
app:layout_constraintWidth_min="@dimen/chat_bubble_single_file_width"
app:layout_constraintTop_toTopOf="@id/left_background"
app:layout_constraintBottom_toBottomOf="@id/left_background"
app:layout_constraintStart_toEndOf="@id/left_background"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_700"
android:id="@+id/file_name"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="@{model.fileName, default=`Lorem_ipsum.pdf`}"
android:textColor="?attr/color_main2_600"
android:textSize="13sp"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/left_background"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/file_background"
app:layout_constraintBottom_toTopOf="@id/file_size"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_300"
android:id="@+id/file_size"
android:onClick="@{() -> model.onClick()}"
android:onLongClick="@{onLongClickListener}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@{model.formattedFileSize, default=`42 kb`}"
android:textColor="?attr/color_main2_600"
android:textSize="12sp"
android:maxLines="1"
android:ellipsize="end"
android:visibility="@{model.isImage || model.isVideoPreview || model.fileSize == 0 ? View.GONE : View.VISIBLE, default=gone}"
app:layout_constraintStart_toStartOf="@id/file_name"
app:layout_constraintEnd_toEndOf="@id/file_name"
app:layout_constraintTop_toBottomOf="@id/file_name"
app:layout_constraintBottom_toBottomOf="@id/file_background"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -25,8 +25,8 @@
<ImageView
android:id="@+id/broken_media_icon"
android:onClick="@{() -> model.onClick()}"
android:layout_width="@dimen/chat_bubble_grid_image_size"
android:layout_height="@dimen/chat_bubble_grid_image_size"
android:layout_width="@dimen/chat_bubble_grid_file_size"
android:layout_height="@dimen/chat_bubble_grid_file_size"
android:adjustViewBounds="true"
android:padding="18dp"
android:background="@drawable/shape_squircle_file_background_5dp"
@ -85,8 +85,8 @@
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/transfer_progress"
android:layout_width="@dimen/chat_bubble_grid_image_size"
android:layout_height="@dimen/chat_bubble_grid_image_size"
android:layout_width="@dimen/chat_bubble_grid_file_size"
android:layout_height="@dimen/chat_bubble_grid_file_size"
android:indeterminate="false"
android:progress="@{model.transferProgress}"
android:max="100"

View file

@ -1,110 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/tools">
<data>
<import type="android.view.View" />
<variable
name="onLongClickListener"
type="View.OnLongClickListener" />
<variable
name="model"
type="org.linphone.ui.main.chat.model.FileModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="1dp">
android:layout_height="wrap_content">
<ImageView
android:id="@+id/broken_media_icon"
android:onClick="@{() -> model.onClick()}"
android:layout_width="@dimen/chat_bubble_grid_image_size"
android:layout_height="@dimen/chat_bubble_grid_image_size"
android:adjustViewBounds="true"
android:padding="18dp"
android:background="@drawable/shape_squircle_file_background_5dp"
android:visibility="@{model.mediaPreviewAvailable ? View.GONE : View.VISIBLE, default=gone}"
android:contentDescription="@{model.isVideoPreview ? @string/content_description_chat_bubble_video : @string/content_description_chat_bubble_image}"
android:src="@{model.isVideoPreview ? @drawable/file_video : @drawable/file_image, default=@drawable/file_video}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:tint="?attr/color_main2_600" />
<ImageView
android:id="@+id/image"
android:layout_width="@dimen/chat_bubble_grid_image_size"
android:layout_height="@dimen/chat_bubble_grid_image_size"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:contentDescription="@{model.isVideoPreview ? @string/content_description_chat_bubble_video : @string/content_description_chat_bubble_image}"
<include
android:id="@+id/media_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
layout="@layout/chat_bubble_media_grid_cell"
android:visibility="@{model.isImage || model.isVideoPreview ? View.VISIBLE : View.GONE}"
coilBubbleGrid="@{model.mediaPreview}"
coilBubbleFallback="@{brokenMediaIcon}"
bind:model="@{model}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_600"
android:id="@+id/video_duration"
<include
android:id="@+id/file_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@{model.audioVideoDuration, default=`00:42`}"
android:textColor="@{model.isVideoPreview ? @color/bc_white : @color/main2_600}"
android:textSize="12sp"
android:visibility="@{model.isVideoPreview &amp;&amp; model.audioVideoDuration.length() > 0 ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<ImageView
android:id="@+id/video_preview"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:src="@drawable/play_fill"
android:contentDescription="@null"
android:visibility="@{model.isVideoPreview &amp;&amp; model.mediaPreviewAvailable ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toTopOf="@id/image"
app:layout_constraintBottom_toBottomOf="@id/image"
app:layout_constraintStart_toStartOf="@id/image"
app:layout_constraintEnd_toEndOf="@id/image"
app:tint="@color/bc_white" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_700"
android:id="@+id/file_name"
android:layout_width="@dimen/chat_bubble_grid_image_size"
android:layout_height="@dimen/chat_bubble_grid_image_size"
android:paddingStart="3dp"
android:paddingEnd="3dp"
android:text="@{model.fileName, default=`Lorem_ipsum.pdf`}"
android:textColor="?attr/color_main2_600"
android:textSize="13sp"
android:maxLines="2"
android:ellipsize="middle"
android:gravity="center"
android:background="@drawable/shape_squircle_main2_200"
android:visibility="@{model.isImage || model.isVideoPreview ? View.GONE : View.VISIBLE}"
app:layout_constraintVertical_chainStyle="packed"
layout="@layout/chat_bubble_file_grid_cell"
android:visibility="@{!model.isImage &amp;&amp; !model.isVideoPreview ? View.VISIBLE : View.GONE}"
bind:model="@{model}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_600"
android:id="@+id/audio_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@{model.audioVideoDuration, default=`00:42`}"
android:textColor="?attr/color_main2_600"
android:textSize="12sp"
android:visibility="@{model.isAudio &amp;&amp; model.audioVideoDuration.length() > 0 ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toBottomOf="@id/file_name"
app:layout_constraintStart_toStartOf="@id/file_name"/>
app:layout_constraintTop_toTopOf="parent"/>
<ImageView
android:id="@+id/remove_attachment"

View file

@ -1,67 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View" />
<variable
name="model"
type="org.linphone.ui.main.chat.model.FileModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:paddingTop="10dp"
android:paddingBottom="10dp">
<ImageView
android:id="@+id/file_icon"
android:layout_width="@dimen/chat_bubble_grid_image_size"
android:layout_height="@dimen/chat_bubble_grid_image_size"
android:onClick="@{() -> model.onClick()}"
android:adjustViewBounds="true"
android:padding="18dp"
android:contentDescription="@string/content_description_chat_bubble_file"
android:src="@{model.isWaitingToBeDownloaded ? @drawable/download_simple : model.isPdf ? @drawable/file_pdf : model.isAudio ? @drawable/file_audio : @drawable/file, default=@drawable/file_pdf}"
android:background="@drawable/shape_squircle_file_background_left"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:tint="?attr/color_main2_600" />
<View
android:id="@+id/file_background"
android:onClick="@{() -> model.onClick()}"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@drawable/shape_squircle_white_right"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/file_icon"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_700"
android:id="@+id/file_name"
android:onClick="@{() -> model.onClick()}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="@{model.fileName, default=`Lorem_ipsum.pdf`}"
android:textColor="?attr/color_main2_600"
android:textSize="13sp"
android:maxLines="1"
android:ellipsize="middle"
app:layout_constraintStart_toEndOf="@id/file_icon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/tools">
<data>
<import type="android.view.View" />
@ -47,37 +48,6 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<ImageView
android:id="@+id/audio_file"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="1dp"
android:adjustViewBounds="true"
android:padding="18dp"
android:background="@drawable/shape_squircle_main2_200"
android:src="@drawable/file_audio"
android:contentDescription="@null"
android:visibility="@{model.isAudio ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:tint="?attr/color_main2_600" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_600"
android:id="@+id/audio_video_duration"
android:onClick="@{() -> model.onClick()}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@{model.audioVideoDuration, default=`00:42`}"
android:textColor="@{model.isVideoPreview ? @color/bc_white : @color/main2_600}"
android:textSize="12sp"
android:visibility="@{(model.isVideoPreview || model.isAudio) &amp;&amp; model.audioVideoDuration.length() > 0 ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<ImageView
android:id="@+id/video_preview"
android:onClick="@{() -> model.onClick()}"
@ -92,6 +62,86 @@
app:layout_constraintEnd_toEndOf="@id/image"
app:tint="@color/bc_white" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/audio_file"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="1dp"
android:visibility="@{model.isAudio ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<View
android:id="@+id/background"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@drawable/shape_squircle_file_bubble_background"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<View
android:id="@+id/top_background"
android:layout_width="0dp"
android:layout_height="24dp"
android:background="@drawable/shape_squircle_file_background_top"
app:layout_constraintStart_toStartOf="@id/background"
app:layout_constraintEnd_toEndOf="@id/background"
app:layout_constraintTop_toTopOf="@id/background"
app:layout_constraintBottom_toBottomOf="@id/file_icon"/>
<ImageView
android:id="@+id/file_icon"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:adjustViewBounds="true"
android:padding="2dp"
android:onClick="@{() -> model.onClick()}"
android:contentDescription="@string/content_description_chat_bubble_file"
android:src="@drawable/file_audio"
app:layout_constraintStart_toStartOf="@id/background"
app:layout_constraintEnd_toEndOf="@id/background"
app:layout_constraintTop_toTopOf="@id/top_background"
app:layout_constraintBottom_toBottomOf="@id/top_background"
app:tint="?attr/color_main2_600" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_700"
android:id="@+id/file_name"
android:onClick="@{() -> model.onClick()}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginEnd="2dp"
android:text="@{model.fileName, default=`Lorem_ipsum.pdf`}"
android:textColor="?attr/color_main2_600"
android:textAlignment="center"
android:textSize="12sp"
android:maxLines="2"
android:ellipsize="end"
android:visibility="@{model.transferProgress >= 0 &amp;&amp; model.transferProgress &lt; 100 ? View.INVISIBLE : View.VISIBLE}"
app:layout_constraintStart_toStartOf="@id/background"
app:layout_constraintEnd_toEndOf="@id/background"
app:layout_constraintTop_toBottomOf="@id/file_icon"
app:layout_constraintBottom_toTopOf="@id/audio_duration"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_600"
android:id="@+id/audio_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@{model.audioVideoDuration, default=`00:42`}"
android:textColor="?attr/color_main2_600"
android:textSize="11sp"
android:visibility="@{model.audioVideoDuration.length() > 0 ? View.VISIBLE : View.GONE}"
app:layout_constraintBottom_toBottomOf="@id/background"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -83,9 +83,9 @@
<dimen name="chat_bubble_grouped_bottom_margin">2dp</dimen>
<dimen name="chat_bubble_bottom_margin">8dp</dimen>
<dimen name="chat_bubble_long_press_emoji_reaction_size">25dp</dimen>
<dimen name="chat_bubble_grid_image_size">87dp</dimen>
<dimen name="chat_bubble_big_image_max_size">175dp</dimen>
<dimen name="chat_bubble_grid_file_width">178dp</dimen> <!-- (87 + 2) * 2 -->
<dimen name="chat_bubble_grid_file_size">87dp</dimen>
<dimen name="chat_bubble_single_file_width">178dp</dimen> <!-- (87 + 2) * 2 -->
<dimen name="chat_bubble_meeting_invite_width">271dp</dimen>
<dimen name="chat_bubble_voice_record_width">230dp</dimen>
<dimen name="chat_bubble_max_reply_width">271dp</dimen>
@ -94,6 +94,8 @@
<dimen name="chat_conversation_header_avatar_start_margin_if_back_button">5dp</dimen>
<dimen name="chat_conversation_header_avatar_start_margin_if_no_back_button">15dp</dimen>
<dimen name="unread_count_indicator_size">24dp</dimen>
<dimen name="chat_documents_list_padding_top_bottom">5dp</dimen>
<dimen name="chat_documents_list_padding_start_end">10dp</dimen>
<dimen name="chat_room_emoji_picker_height">300dp</dimen>
<dimen name="chat_room_participants_list_max_height">300dp</dimen>