mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-18 03:48:08 +00:00
Added file content layout in bubble
This commit is contained in:
parent
efdfc809bc
commit
6fec1958b8
12 changed files with 166 additions and 21 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
9
app/src/main/res/drawable/file.xml
Normal file
9
app/src/main/res/drawable/file.xml
Normal 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>
|
||||
9
app/src/main/res/drawable/file_pdf.xml
Normal file
9
app/src/main/res/drawable/file_pdf.xml
Normal 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>
|
||||
|
|
@ -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>
|
||||
5
app/src/main/res/drawable/shape_squircle_white_right.xml
Normal file
5
app/src/main/res/drawable/shape_squircle_white_right.xml
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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 && 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue