Added file content layout in bubble

This commit is contained in:
Sylvain Berfini 2023-11-15 00:09:47 +01:00
parent efdfc809bc
commit 6fec1958b8
12 changed files with 166 additions and 21 deletions

View file

@ -53,6 +53,7 @@ import org.linphone.ui.main.contacts.model.ContactAvatarModel
import org.linphone.utils.AppUtils
import org.linphone.utils.AudioRouteUtils
import org.linphone.utils.Event
import org.linphone.utils.FileUtils
import org.linphone.utils.LinphoneUtils
import org.linphone.utils.PatternClickableSpan
import org.linphone.utils.SpannableClickedListener
@ -95,7 +96,7 @@ class ChatMessageModel @WorkerThread constructor(
val reactions = MutableLiveData<String>()
val imagesList = MutableLiveData<ArrayList<FileModel>>()
val filesList = MutableLiveData<ArrayList<FileModel>>()
val firstImage = MutableLiveData<FileModel>()
@ -178,7 +179,7 @@ class ChatMessageModel @WorkerThread constructor(
var displayableContentFound = false
var filesContentCount = 0
val imagesPath = arrayListOf<FileModel>()
val filesPath = arrayListOf<FileModel>()
val contents = chatMessage.contents
for (content in contents) {
@ -198,10 +199,10 @@ class ChatMessageModel @WorkerThread constructor(
)
when (content.type) {
"image", "video" -> {
val fileModel = FileModel(path) { file ->
val fileModel = FileModel(path, "") { file ->
onContentClicked?.invoke(file)
}
imagesPath.add(fileModel)
filesPath.add(fileModel)
if (filesContentCount == 1) {
firstImage.postValue(fileModel)
@ -222,6 +223,15 @@ class ChatMessageModel @WorkerThread constructor(
displayableContentFound = true
}
else -> {
val size = FileUtils.bytesToDisplayableSize(
content.fileSize.toLong()
)
val fileModel = FileModel(path, size) { file ->
onContentClicked?.invoke(file)
}
filesPath.add(fileModel)
displayableContentFound = true
}
}
} else {
@ -234,7 +244,7 @@ class ChatMessageModel @WorkerThread constructor(
}
}
imagesList.postValue(imagesPath)
filesList.postValue(filesPath)
if (!displayableContentFound) { // Temporary workaround to prevent empty bubbles
val describe = LinphoneUtils.getTextDescribingMessage(chatMessage)

View file

@ -9,6 +9,7 @@ import org.linphone.utils.FileUtils
class FileModel @AnyThread constructor(
val file: String,
val fileSize: String,
private val onClicked: ((file: String) -> Unit)? = null
) {
companion object {
@ -21,12 +22,22 @@ class FileModel @AnyThread constructor(
val mimeType: FileUtils.MimeType
val isImage: Boolean
val isVideoPreview: Boolean
val isPdf: Boolean
init {
path.postValue(file)
val extension = FileUtils.getExtensionFromFileName(file)
isPdf = extension == "pdf"
val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
mimeType = FileUtils.getMimeType(mime)
isImage = mimeType == FileUtils.MimeType.Image
isVideoPreview = mimeType == FileUtils.MimeType.Video
}
@UiThread

View file

@ -300,7 +300,7 @@ class SendMessageInConversationViewModel @UiThread constructor() : ViewModel() {
fun addAttachment(file: String) {
val list = arrayListOf<FileModel>()
list.addAll(attachments.value.orEmpty())
val model = FileModel(file) { file ->
val model = FileModel(file, "") { file ->
removeAttachment(file)
}
list.add(model)

View file

@ -27,6 +27,7 @@ import android.os.ParcelFileDescriptor
import android.os.Process
import android.provider.OpenableColumns
import android.system.Os
import android.text.format.Formatter
import android.webkit.MimeTypeMap
import androidx.annotation.AnyThread
import java.io.File
@ -52,6 +53,11 @@ class FileUtils {
companion object {
private const val TAG = "[File Utils]"
@AnyThread
fun bytesToDisplayableSize(bytes: Long): String {
return Formatter.formatShortFileSize(coreContext.context, bytes)
}
@AnyThread
fun isExtensionVideo(path: String): Boolean {
val extension = getExtensionFromFileName(path)

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="256"
android:viewportHeight="256">
<path
android:pathData="M213.66,82.34l-56,-56A8,8 0,0 0,152 24H56A16,16 0,0 0,40 40V216a16,16 0,0 0,16 16H200a16,16 0,0 0,16 -16V88A8,8 0,0 0,213.66 82.34ZM160,51.31 L188.69,80H160ZM200,216H56V40h88V88a8,8 0,0 0,8 8h48V216Z"
android:fillColor="#4e6074"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="256"
android:viewportHeight="256">
<path
android:pathData="M224,152a8,8 0,0 1,-8 8L192,160v16h16a8,8 0,0 1,0 16L192,192v16a8,8 0,0 1,-16 0L176,152a8,8 0,0 1,8 -8h32A8,8 0,0 1,224 152ZM92,172a28,28 0,0 1,-28 28L56,200v8a8,8 0,0 1,-16 0L40,152a8,8 0,0 1,8 -8L64,144A28,28 0,0 1,92 172ZM76,172a12,12 0,0 0,-12 -12L56,160v24h8A12,12 0,0 0,76 172ZM164,180a36,36 0,0 1,-36 36L112,216a8,8 0,0 1,-8 -8L104,152a8,8 0,0 1,8 -8h16A36,36 0,0 1,164 180ZM148,180a20,20 0,0 0,-20 -20h-8v40h8A20,20 0,0 0,148 180ZM40,112L40,40A16,16 0,0 1,56 24h96a8,8 0,0 1,5.66 2.34l56,56A8,8 0,0 1,216 88v24a8,8 0,0 1,-16 0L200,96L152,96a8,8 0,0 1,-8 -8L144,40L56,40v72a8,8 0,0 1,-16 0ZM160,80h28.69L160,51.31Z"
android:fillColor="#4e6074"/>
</vector>

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="10dp" android:bottomLeftRadius="10dp" />
<solid android:color="@color/gray_main2_200" />
</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:topRightRadius="10dp" android:bottomRightRadius="10dp" />
<solid android:color="@color/white" />
</shape>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<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" />
@ -8,13 +9,101 @@
type="org.linphone.ui.main.chat.model.FileModel" />
</data>
<ImageView
<androidx.constraintlayout.widget.ConstraintLayout
android:onClick="@{() -> model.onClick()}"
android:layout_width="@dimen/chat_bubble_grid_image_size"
android:layout_height="@dimen/chat_bubble_grid_image_size"
android:layout_margin="1dp"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
coilBubbleGrid="@{model.path}"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<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, default=gone}"
app:constraint_referenced_ids="file_name, file_size, file_background, file_icon" />
<ImageView
android:id="@+id/image"
android:layout_width="@dimen/chat_bubble_grid_image_size"
android:layout_height="@dimen/chat_bubble_grid_image_size"
android:layout_margin="1dp"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:visibility="@{model.isImage || model.isVideoPreview ? View.VISIBLE : View.GONE}"
coilBubbleGrid="@{model.path}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="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:visibility="@{model.isVideoPreview ? 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/orange_main_500" />
<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:adjustViewBounds="true"
android:padding="18dp"
android:src="@{model.isPdf ? @drawable/file_pdf : @drawable/file, default=@drawable/file_pdf}"
android:background="@drawable/shape_squircle_main2_200_left"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<View
android:id="@+id/file_background"
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:layout_width="@dimen/chat_bubble_grid_file_name_max_width"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="16dp"
android:text="@{model.fileName, default=`Lorem_ipsum.pdf`}"
android:textColor="@color/gray_main2_600"
android:textSize="13sp"
android:maxLines="1"
android:ellipsize="middle"
android:background="@drawable/shape_squircle_white_r10_background"
app:layout_constraintStart_toEndOf="@id/file_icon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/file_size"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_300"
android:id="@+id/file_size"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="@{model.fileSize, default=`42 kb`}"
android:textColor="@color/gray_main2_600"
android:textSize="12sp"
android:maxLines="1"
android:ellipsize="end"
android:background="@drawable/shape_squircle_white_r10_background"
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="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -145,15 +145,15 @@
app:layout_constraintStart_toEndOf="@id/avatar">
<com.google.android.flexbox.FlexboxLayout
android:id="@+id/images_grid"
android:id="@+id/files_grid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onLongClick="@{onLongClickListener}"
android:visibility="@{model.imagesList.size() >= 2 ? View.VISIBLE : View.GONE, default=gone}"
android:visibility="@{model.filesList.size() >= 2 || (model.filesList.size() >= 1 &amp;&amp; model.firstImage.path.length() == 0) ? View.VISIBLE : View.GONE, default=gone}"
app:alignItems="center"
app:flexWrap="wrap"
app:justifyContent="@{model.outgoing ? JustifyContent.FLEX_END : JustifyContent.FLEX_START}"
entries="@{model.imagesList}"
entries="@{model.filesList}"
layout="@{@layout/chat_bubble_content_grid_cell}"/>
<ImageView
@ -165,7 +165,7 @@
android:maxHeight="@dimen/chat_bubble_big_image_max_size"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:visibility="@{model.imagesList.size() > 1 || model.firstImage.path.length() == 0 ? View.GONE : View.VISIBLE}"
android:visibility="@{model.filesList.size() > 1 || model.firstImage.path.length() == 0 ? View.GONE : View.VISIBLE}"
coilBubble="@{model.firstImage.path}"/>
<ViewStub

View file

@ -110,11 +110,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onLongClick="@{onLongClickListener}"
android:visibility="@{model.imagesList.size() >= 2 ? 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}"
entries="@{model.imagesList}"
entries="@{model.filesList}"
layout="@{@layout/chat_bubble_content_grid_cell}"/>
<ImageView
@ -126,7 +126,7 @@
android:maxHeight="@dimen/chat_bubble_big_image_max_size"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:visibility="@{model.imagesList.size() > 1 || model.firstImage.path.length() == 0 ? View.GONE : View.VISIBLE}"
android:visibility="@{model.filesList.size() > 1 || model.firstImage.path.length() == 0 ? View.GONE : View.VISIBLE}"
coilBubble="@{model.firstImage.path}"/>
<ViewStub

View file

@ -69,6 +69,7 @@
<dimen name="chat_bubble_long_press_emoji_reaction_size">30sp</dimen>
<dimen name="chat_bubble_grid_image_size">88dp</dimen>
<dimen name="chat_bubble_big_image_max_size">150dp</dimen>
<dimen name="chat_bubble_grid_file_name_max_width">151dp</dimen> <!-- 271 - 88 - 32 -->
<dimen name="chat_bubble_meeting_invite_width">271dp</dimen>
<dimen name="chat_bubble_voice_record_width">271dp</dimen>
<dimen name="chat_bubble_max_reply_width">271dp</dimen>