Improved videos shared by chat preview

This commit is contained in:
Sylvain Berfini 2020-06-11 14:54:21 +02:00
parent f6a6459b29
commit 7ca36938df
9 changed files with 111 additions and 24 deletions

View file

@ -39,7 +39,6 @@ import androidx.navigation.Navigation
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.tabs_fragment.*
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.LinphoneApplication.Companion.corePreferences
@ -518,8 +517,7 @@ class DetailChatRoomFragment : MasterFragment() {
private fun openFile(contentFilePath: String) { private fun openFile(contentFilePath: String) {
val intent = Intent(Intent.ACTION_VIEW) val intent = Intent(Intent.ACTION_VIEW)
val path = contentFilePath val contentUri: Uri = FileUtils.getPublicFilePath(requireContext(), contentFilePath)
val contentUri: Uri = FileUtils.getPublicFilePath(requireContext(), path)
val filePath: String = contentUri.toString() val filePath: String = contentUri.toString()
Log.i("[Chat Message] Trying to open file: $filePath") Log.i("[Chat Message] Trying to open file: $filePath")
var type: String? = null var type: String? = null
@ -535,7 +533,7 @@ class DetailChatRoomFragment : MasterFragment() {
if (type != null) { if (type != null) {
Log.i("[Chat Message] Found matching MIME type $type") Log.i("[Chat Message] Found matching MIME type $type")
} else { } else {
type = FileUtils.getMimeFromFile(filePath) type = "file/$extension"
Log.e("[Chat Message] Can't get MIME type from extension: $extension, will use $type") Log.e("[Chat Message] Can't get MIME type from extension: $extension, will use $type")
} }

View file

@ -19,15 +19,34 @@
*/ */
package org.linphone.activities.main.chat.viewmodels package org.linphone.activities.main.chat.viewmodels
import android.graphics.Bitmap
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.linphone.utils.FileUtils import org.linphone.utils.FileUtils
import org.linphone.utils.ImageUtils
class ChatMessageAttachmentViewModel( class ChatMessageAttachmentViewModel(
val path: String, val path: String,
val isImage: Boolean,
private val deleteCallback: (attachment: ChatMessageAttachmentViewModel) -> Unit private val deleteCallback: (attachment: ChatMessageAttachmentViewModel) -> Unit
) : ViewModel() { ) : ViewModel() {
val fileName: String = FileUtils.getNameFromFilePath(path) val fileName: String = FileUtils.getNameFromFilePath(path)
val isImage: Boolean = FileUtils.isExtensionImage(path)
val isVideo: Boolean = FileUtils.isExtensionVideo(path)
val videoPreview = MutableLiveData<Bitmap>()
init {
if (isVideo) {
viewModelScope.launch {
withContext(Dispatchers.IO) {
videoPreview.postValue(ImageUtils.getVideoPreview(path))
}
}
}
}
fun delete() { fun delete() {
deleteCallback(this) deleteCallback(this)

View file

@ -19,12 +19,18 @@
*/ */
package org.linphone.activities.main.chat.viewmodels package org.linphone.activities.main.chat.viewmodels
import android.graphics.Bitmap
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.linphone.core.ChatMessage import org.linphone.core.ChatMessage
import org.linphone.core.Content import org.linphone.core.Content
import org.linphone.core.tools.Log import org.linphone.core.tools.Log
import org.linphone.utils.FileUtils import org.linphone.utils.FileUtils
import org.linphone.utils.ImageUtils
class ChatMessageContentViewModel( class ChatMessageContentViewModel(
val content: Content, val content: Content,
@ -32,6 +38,8 @@ class ChatMessageContentViewModel(
private val listener: OnContentClickedListener? private val listener: OnContentClickedListener?
) : ViewModel() { ) : ViewModel() {
val isImage = MutableLiveData<Boolean>() val isImage = MutableLiveData<Boolean>()
val isVideo = MutableLiveData<Boolean>()
val videoPreview = MutableLiveData<Bitmap>()
val downloadable = MutableLiveData<Boolean>() val downloadable = MutableLiveData<Boolean>()
@ -55,14 +63,25 @@ class ChatMessageContentViewModel(
if (content.filePath.isNotEmpty()) { if (content.filePath.isNotEmpty()) {
Log.i("[Content] Found displayable content: ${content.filePath}") Log.i("[Content] Found displayable content: ${content.filePath}")
isImage.value = FileUtils.isExtensionImage(content.filePath) isImage.value = FileUtils.isExtensionImage(content.filePath)
isVideo.value = FileUtils.isExtensionVideo(content.filePath)
if (isVideo.value == true) {
viewModelScope.launch {
withContext(Dispatchers.IO) {
videoPreview.postValue(ImageUtils.getVideoPreview(content.filePath))
}
}
}
} else { } else {
Log.w("[Content] Found content with empty path...") Log.w("[Content] Found content with empty path...")
isImage.value = false isImage.value = false
isVideo.value = false
} }
} else { } else {
Log.i("[Content] Found downloadable content: ${content.name}") Log.i("[Content] Found downloadable content: ${content.name}")
downloadable.value = true downloadable.value = true
isImage.value = false isImage.value = false
isVideo.value = false
} }
downloadEnabled.value = downloadable.value downloadEnabled.value = downloadable.value

View file

@ -74,7 +74,7 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
fun addAttachment(path: String) { fun addAttachment(path: String) {
val list = arrayListOf<ChatMessageAttachmentViewModel>() val list = arrayListOf<ChatMessageAttachmentViewModel>()
list.addAll(attachments.value.orEmpty()) list.addAll(attachments.value.orEmpty())
list.add(ChatMessageAttachmentViewModel(path, FileUtils.isExtensionImage(path)) { list.add(ChatMessageAttachmentViewModel(path) {
removeAttachment(it) removeAttachment(it)
}) })
attachments.value = list attachments.value = list

View file

@ -20,6 +20,7 @@
package org.linphone.utils package org.linphone.utils
import android.content.Context import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.net.Uri import android.net.Uri
import android.text.Editable import android.text.Editable
@ -54,6 +55,11 @@ fun ImageView.setSourceImageResource(resource: Int) {
this.setImageResource(resource) this.setImageResource(resource)
} }
@BindingAdapter("android:src")
fun ImageView.setSourceImageBitmap(bitmap: Bitmap?) {
if (bitmap != null) this.setImageBitmap(bitmap)
}
@BindingAdapter("android:contentDescription") @BindingAdapter("android:contentDescription")
fun ImageView.setContentDescription(resource: Int) { fun ImageView.setContentDescription(resource: Int) {
if (resource == 0) { if (resource == 0) {

View file

@ -23,6 +23,7 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Environment import android.os.Environment
import android.provider.OpenableColumns import android.provider.OpenableColumns
import android.webkit.MimeTypeMap
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import java.io.* import java.io.*
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -37,13 +38,6 @@ import org.linphone.core.tools.Log
class FileUtils { class FileUtils {
companion object { companion object {
fun getMimeFromFile(path: String?): String? {
val filePath = path ?: ""
return if (isExtensionImage(filePath)) {
"image/" + getExtensionFromFileName(filePath)
} else "file/" + getExtensionFromFileName(filePath)
}
fun getNameFromFilePath(filePath: String): String { fun getNameFromFilePath(filePath: String): String {
var name = filePath var name = filePath
val i = filePath.lastIndexOf('/') val i = filePath.lastIndexOf('/')
@ -54,17 +48,19 @@ class FileUtils {
} }
fun getExtensionFromFileName(fileName: String): String { fun getExtensionFromFileName(fileName: String): String {
var extension = "" return MimeTypeMap.getFileExtensionFromUrl(fileName)
val i = fileName.lastIndexOf('.')
if (i > 0) {
extension = fileName.substring(i + 1)
}
return extension
} }
fun isExtensionImage(path: String): Boolean { fun isExtensionImage(path: String): Boolean {
val extension = getExtensionFromFileName(path).toLowerCase(Locale.getDefault()) val extension = getExtensionFromFileName(path).toLowerCase(Locale.getDefault())
return extension.matches(Regex("(png|jpg|jpeg|bmp|gif)")) val type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
return type?.startsWith("image/") ?: false
}
fun isExtensionVideo(path: String): Boolean {
val extension = getExtensionFromFileName(path).toLowerCase(Locale.getDefault())
val type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
return type?.startsWith("video/") ?: false
} }
fun getFileStorageDir(isPicture: Boolean = false): File { fun getFileStorageDir(isPicture: Boolean = false): File {

View file

@ -21,6 +21,7 @@ package org.linphone.utils
import android.content.Context import android.content.Context
import android.graphics.* import android.graphics.*
import android.media.ThumbnailUtils
import android.net.Uri import android.net.Uri
import android.provider.MediaStore import android.provider.MediaStore
@ -62,6 +63,10 @@ class ImageUtils {
return rotatedBitmap return rotatedBitmap
} }
fun getVideoPreview(path: String): Bitmap? {
return ThumbnailUtils.createVideoThumbnail(path, MediaStore.Images.Thumbnails.MINI_KIND)
}
private fun getRoundBitmap(bitmap: Bitmap): Bitmap? { private fun getRoundBitmap(bitmap: Bitmap): Bitmap? {
val output = val output =
Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888) Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)

View file

@ -34,9 +34,35 @@
android:layout_alignRight="@id/pendingImageForUpload" android:layout_alignRight="@id/pendingImageForUpload"
android:src="@drawable/clean_field" /> android:src="@drawable/clean_field" />
<ImageView
android:id="@+id/pendingVideoForUpload"
android:visibility="@{data.video ? View.VISIBLE : View.GONE, default=gone}"
android:contentDescription="@string/content_description_pending_file_transfer"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:adjustViewBounds="true"
android:src="@{data.videoPreview}"/>
<ImageView
android:visibility="@{data.video ? View.VISIBLE : View.GONE, default=gone}"
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/recording_play_pause"
android:layout_centerInParent="true"/>
<ImageView
android:visibility="@{data.video ? View.VISIBLE : View.GONE, default=gone}"
android:onClick="@{() -> data.delete()}"
android:contentDescription="@string/content_description_remove_pending_file_transfer"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignTop="@id/pendingVideoForUpload"
android:layout_alignRight="@id/pendingVideoForUpload"
android:src="@drawable/clean_field" />
<TextView <TextView
android:id="@+id/pendingFileForUpload" android:id="@+id/pendingFileForUpload"
android:visibility="@{data.image ? View.GONE : View.VISIBLE}" android:visibility="@{data.image || data.video ? View.GONE : View.VISIBLE, default=gone}"
android:text="@{data.fileName}" android:text="@{data.fileName}"
android:textColor="?attr/secondaryTextColor" android:textColor="?attr/secondaryTextColor"
android:textSize="21sp" android:textSize="21sp"
@ -47,7 +73,7 @@
android:textAlignment="center" /> android:textAlignment="center" />
<ImageView <ImageView
android:visibility="@{data.image ? View.GONE : View.VISIBLE}" android:visibility="@{data.image || data.video ? View.GONE : View.VISIBLE, default=gone}"
android:onClick="@{() -> data.delete()}" android:onClick="@{() -> data.delete()}"
android:contentDescription="@string/content_description_remove_pending_file_transfer" android:contentDescription="@string/content_description_remove_pending_file_transfer"
android:layout_width="20dp" android:layout_width="20dp"

View file

@ -24,9 +24,27 @@
android:layout_height="@{data.alone ? @dimen/chat_message_bubble_image_height_big : @dimen/chat_message_bubble_image_height_small, default=wrap_content}" android:layout_height="@{data.alone ? @dimen/chat_message_bubble_image_height_big : @dimen/chat_message_bubble_image_height_small, default=wrap_content}"
android:layout_margin="5dp" android:layout_margin="5dp"
app:glidePath="@{data.content.filePath}" app:glidePath="@{data.content.filePath}"
android:visibility="@{data.isImage() ? View.VISIBLE : View.GONE}" android:visibility="@{data.image ? View.VISIBLE : View.GONE}"
android:adjustViewBounds="true" /> android:adjustViewBounds="true" />
<ImageView
android:onClick="@{() -> data.openFile()}"
android:onLongClick="@{longClickListener}"
android:contentDescription="@string/content_description_downloaded_file_transfer"
android:layout_width="wrap_content"
android:layout_height="@{data.alone ? @dimen/chat_message_bubble_image_height_big : @dimen/chat_message_bubble_image_height_small, default=wrap_content}"
android:layout_margin="5dp"
android:src="@{data.videoPreview}"
android:visibility="@{data.video ? View.VISIBLE : View.GONE, default=gone}"
android:adjustViewBounds="true" />
<ImageView
android:visibility="@{data.video ? View.VISIBLE : View.GONE, default=gone}"
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/recording_play_pause"
android:layout_centerInParent="true"/>
<TextView <TextView
android:onClick="@{() -> data.openFile()}" android:onClick="@{() -> data.openFile()}"
android:onLongClick="@{longClickListener}" android:onLongClick="@{longClickListener}"
@ -35,7 +53,7 @@
android:fontFamily="sans-serif" android:fontFamily="sans-serif"
android:textStyle="normal" android:textStyle="normal"
android:text="@{data.content.name}" android:text="@{data.content.name}"
android:visibility="@{data.downloadable || data.isImage() ? View.GONE : View.VISIBLE}" android:visibility="@{data.downloadable || data.image || data.video ? View.GONE : View.VISIBLE, default=gone}"
android:layout_width="150dp" android:layout_width="150dp"
android:layout_height="100dp" android:layout_height="100dp"
android:layout_margin="5dp" android:layout_margin="5dp"