Improved videos shared by chat preview
This commit is contained in:
parent
f6a6459b29
commit
7ca36938df
9 changed files with 111 additions and 24 deletions
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue