Also use Coil to extract image from video + fixed contact matching issue in chat room creation
This commit is contained in:
parent
5ce69e63cd
commit
a1f9b95432
11 changed files with 17 additions and 43 deletions
|
@ -235,6 +235,7 @@ dependencies {
|
||||||
implementation("io.coil-kt:coil:$coil_version")
|
implementation("io.coil-kt:coil:$coil_version")
|
||||||
implementation("io.coil-kt:coil-gif:$coil_version")
|
implementation("io.coil-kt:coil-gif:$coil_version")
|
||||||
implementation("io.coil-kt:coil-svg:$coil_version")
|
implementation("io.coil-kt:coil-svg:$coil_version")
|
||||||
|
implementation("io.coil-kt:coil-video:$coil_version")
|
||||||
|
|
||||||
// https://github.com/Baseflow/PhotoView/blob/master/LICENSE Apache v2.0
|
// https://github.com/Baseflow/PhotoView/blob/master/LICENSE Apache v2.0
|
||||||
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
|
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
|
||||||
|
|
|
@ -27,6 +27,7 @@ import coil.ImageLoaderFactory
|
||||||
import coil.decode.GifDecoder
|
import coil.decode.GifDecoder
|
||||||
import coil.decode.ImageDecoderDecoder
|
import coil.decode.ImageDecoderDecoder
|
||||||
import coil.decode.SvgDecoder
|
import coil.decode.SvgDecoder
|
||||||
|
import coil.decode.VideoFrameDecoder
|
||||||
import coil.disk.DiskCache
|
import coil.disk.DiskCache
|
||||||
import coil.memory.MemoryCache
|
import coil.memory.MemoryCache
|
||||||
import org.linphone.core.*
|
import org.linphone.core.*
|
||||||
|
@ -86,6 +87,7 @@ class LinphoneApplication : Application(), ImageLoaderFactory {
|
||||||
override fun newImageLoader(): ImageLoader {
|
override fun newImageLoader(): ImageLoader {
|
||||||
return ImageLoader.Builder(this)
|
return ImageLoader.Builder(this)
|
||||||
.components {
|
.components {
|
||||||
|
add(VideoFrameDecoder.Factory())
|
||||||
add(SvgDecoder.Factory())
|
add(SvgDecoder.Factory())
|
||||||
if (Version.sdkAboveOrEqual(Version.API28_PIE_90)) {
|
if (Version.sdkAboveOrEqual(Version.API28_PIE_90)) {
|
||||||
add(ImageDecoderDecoder.Factory())
|
add(ImageDecoderDecoder.Factory())
|
||||||
|
|
|
@ -19,11 +19,7 @@
|
||||||
*/
|
*/
|
||||||
package org.linphone.activities.main.chat.data
|
package org.linphone.activities.main.chat.data
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import org.linphone.utils.FileUtils
|
import org.linphone.utils.FileUtils
|
||||||
import org.linphone.utils.ImageUtils
|
|
||||||
|
|
||||||
class ChatMessageAttachmentData(
|
class ChatMessageAttachmentData(
|
||||||
val path: String,
|
val path: String,
|
||||||
|
@ -34,23 +30,6 @@ class ChatMessageAttachmentData(
|
||||||
val isVideo: Boolean = FileUtils.isExtensionVideo(path)
|
val isVideo: Boolean = FileUtils.isExtensionVideo(path)
|
||||||
val isAudio: Boolean = FileUtils.isExtensionAudio(path)
|
val isAudio: Boolean = FileUtils.isExtensionAudio(path)
|
||||||
val isPdf: Boolean = FileUtils.isExtensionPdf(path)
|
val isPdf: Boolean = FileUtils.isExtensionPdf(path)
|
||||||
val videoPreview = MutableLiveData<Bitmap>()
|
|
||||||
|
|
||||||
private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (isVideo) {
|
|
||||||
scope.launch {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
videoPreview.postValue(ImageUtils.getVideoPreview(path))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun destroy() {
|
|
||||||
scope.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun delete() {
|
fun delete() {
|
||||||
deleteCallback(this)
|
deleteCallback(this)
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
*/
|
*/
|
||||||
package org.linphone.activities.main.chat.data
|
package org.linphone.activities.main.chat.data
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
import android.text.Spanned
|
import android.text.Spanned
|
||||||
|
@ -43,7 +42,6 @@ import org.linphone.core.*
|
||||||
import org.linphone.core.tools.Log
|
import org.linphone.core.tools.Log
|
||||||
import org.linphone.utils.AppUtils
|
import org.linphone.utils.AppUtils
|
||||||
import org.linphone.utils.FileUtils
|
import org.linphone.utils.FileUtils
|
||||||
import org.linphone.utils.ImageUtils
|
|
||||||
import org.linphone.utils.TimestampUtils
|
import org.linphone.utils.TimestampUtils
|
||||||
|
|
||||||
class ChatMessageContentData(
|
class ChatMessageContentData(
|
||||||
|
@ -57,7 +55,6 @@ class ChatMessageContentData(
|
||||||
val isImage = MutableLiveData<Boolean>()
|
val isImage = MutableLiveData<Boolean>()
|
||||||
val isVideo = MutableLiveData<Boolean>()
|
val isVideo = MutableLiveData<Boolean>()
|
||||||
val isAudio = MutableLiveData<Boolean>()
|
val isAudio = MutableLiveData<Boolean>()
|
||||||
val videoPreview = MutableLiveData<Bitmap>()
|
|
||||||
val isPdf = MutableLiveData<Boolean>()
|
val isPdf = MutableLiveData<Boolean>()
|
||||||
val isGenericFile = MutableLiveData<Boolean>()
|
val isGenericFile = MutableLiveData<Boolean>()
|
||||||
val isVoiceRecording = MutableLiveData<Boolean>()
|
val isVoiceRecording = MutableLiveData<Boolean>()
|
||||||
|
@ -264,12 +261,6 @@ class ChatMessageContentData(
|
||||||
} else if (isConferenceIcs) {
|
} else if (isConferenceIcs) {
|
||||||
parseConferenceInvite(content)
|
parseConferenceInvite(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isVideo.value == true) {
|
|
||||||
scope.launch {
|
|
||||||
videoPreview.postValue(ImageUtils.getVideoPreview(path))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (isConferenceIcs) {
|
} else if (isConferenceIcs) {
|
||||||
Log.i("[Content] Found content with icalendar file")
|
Log.i("[Content] Found content with icalendar file")
|
||||||
parseConferenceInvite(content)
|
parseConferenceInvite(content)
|
||||||
|
|
|
@ -140,7 +140,6 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
attachments.value.orEmpty().forEach(ChatMessageAttachmentData::destroy)
|
|
||||||
pendingChatMessageToReplyTo.value?.destroy()
|
pendingChatMessageToReplyTo.value?.destroy()
|
||||||
|
|
||||||
if (recorder.state != RecorderState.Closed) {
|
if (recorder.state != RecorderState.Closed) {
|
||||||
|
@ -191,7 +190,6 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
||||||
val list = arrayListOf<ChatMessageAttachmentData>()
|
val list = arrayListOf<ChatMessageAttachmentData>()
|
||||||
list.addAll(attachments.value.orEmpty())
|
list.addAll(attachments.value.orEmpty())
|
||||||
list.remove(attachment)
|
list.remove(attachment)
|
||||||
attachment.destroy()
|
|
||||||
attachments.value = list
|
attachments.value = list
|
||||||
|
|
||||||
sendMessageEnabled.value = textToSend.value.orEmpty().isNotEmpty() || list.isNotEmpty() || isPendingVoiceRecord.value == true
|
sendMessageEnabled.value = textToSend.value.orEmpty().isNotEmpty() || list.isNotEmpty() || isPendingVoiceRecord.value == true
|
||||||
|
@ -266,7 +264,6 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel()
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelReply()
|
cancelReply()
|
||||||
attachments.value.orEmpty().forEach(ChatMessageAttachmentData::destroy)
|
|
||||||
attachments.value = arrayListOf()
|
attachments.value = arrayListOf()
|
||||||
textToSend.value = ""
|
textToSend.value = ""
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ import androidx.constraintlayout.widget.Guideline
|
||||||
import androidx.databinding.*
|
import androidx.databinding.*
|
||||||
import coil.load
|
import coil.load
|
||||||
import coil.request.CachePolicy
|
import coil.request.CachePolicy
|
||||||
|
import coil.request.videoFrameMillis
|
||||||
import coil.transform.CircleCropTransformation
|
import coil.transform.CircleCropTransformation
|
||||||
import com.google.android.material.switchmaterial.SwitchMaterial
|
import com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
import org.linphone.BR
|
import org.linphone.BR
|
||||||
|
@ -356,6 +357,15 @@ fun loadAvatarWithGlide(imageView: ImageView, path: String?) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@BindingAdapter("coilVideoPreview")
|
||||||
|
fun loadVideoPreview(imageView: ImageView, path: String?) {
|
||||||
|
if (path != null && path.isNotEmpty() && FileUtils.isExtensionVideo(path)) {
|
||||||
|
imageView.load(path) {
|
||||||
|
videoFrameMillis(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@BindingAdapter("showSecurityLevel")
|
@BindingAdapter("showSecurityLevel")
|
||||||
fun ContactAvatarView.setShowAvatarSecurityLevel(visible: Boolean) {
|
fun ContactAvatarView.setShowAvatarSecurityLevel(visible: Boolean) {
|
||||||
this.binding.securityBadgeVisibility = visible
|
this.binding.securityBadgeVisibility = visible
|
||||||
|
|
|
@ -21,9 +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 org.linphone.compatibility.Compatibility
|
import org.linphone.compatibility.Compatibility
|
||||||
import org.linphone.core.tools.Log
|
import org.linphone.core.tools.Log
|
||||||
|
|
||||||
|
@ -63,10 +61,6 @@ 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)
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
android:src="@{data.videoPreview}"/>
|
app:coilVideoPreview="@{data.path}"/>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:visibility="@{data.video ? View.VISIBLE : View.GONE, default=gone}"
|
android:visibility="@{data.video ? View.VISIBLE : View.GONE, default=gone}"
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:maxHeight="@dimen/chat_message_bubble_image_height_big"
|
android:maxHeight="@dimen/chat_message_bubble_image_height_big"
|
||||||
android:layout_size="@{data.alone ? 0f : @dimen/chat_message_bubble_file_size}"
|
android:layout_size="@{data.alone ? 0f : @dimen/chat_message_bubble_file_size}"
|
||||||
android:src="@{data.videoPreview}"
|
app:coilVideoPreview="@{data.filePath}"
|
||||||
android:visibility="@{!data.downloadable && data.video ? View.VISIBLE : View.GONE}"
|
android:visibility="@{!data.downloadable && data.video ? View.VISIBLE : View.GONE}"
|
||||||
android:scaleType="@{data.alone ? ScaleType.FIT_CENTER : ScaleType.CENTER_CROP}"
|
android:scaleType="@{data.alone ? ScaleType.FIT_CENTER : ScaleType.CENTER_CROP}"
|
||||||
android:adjustViewBounds="true" />
|
android:adjustViewBounds="true" />
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:maxHeight="@dimen/chat_message_bubble_image_height_small"
|
android:maxHeight="@dimen/chat_message_bubble_image_height_small"
|
||||||
android:layout_size="@{data.alone ? 0f : @dimen/chat_message_small_bubble_file_size}"
|
android:layout_size="@{data.alone ? 0f : @dimen/chat_message_small_bubble_file_size}"
|
||||||
android:src="@{data.videoPreview}"
|
app:coilVideoPreview="@{data.filePath}"
|
||||||
android:visibility="@{data.video ? View.VISIBLE : View.GONE}"
|
android:visibility="@{data.video ? View.VISIBLE : View.GONE}"
|
||||||
android:scaleType="@{ScaleType.CENTER_CROP}"
|
android:scaleType="@{ScaleType.CENTER_CROP}"
|
||||||
android:adjustViewBounds="true" />
|
android:adjustViewBounds="true" />
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
android:layout_width="@dimen/chat_message_small_bubble_file_size"
|
android:layout_width="@dimen/chat_message_small_bubble_file_size"
|
||||||
android:layout_height="@dimen/chat_message_small_bubble_file_size"
|
android:layout_height="@dimen/chat_message_small_bubble_file_size"
|
||||||
android:layout_margin="5dp"
|
android:layout_margin="5dp"
|
||||||
android:src="@{data.videoPreview}"
|
app:coilVideoPreview="@{data.filePath}"
|
||||||
android:visibility="@{data.video ? View.VISIBLE : View.GONE}"
|
android:visibility="@{data.video ? View.VISIBLE : View.GONE}"
|
||||||
android:scaleType="@{ScaleType.CENTER_CROP}"
|
android:scaleType="@{ScaleType.CENTER_CROP}"
|
||||||
android:adjustViewBounds="true" />
|
android:adjustViewBounds="true" />
|
||||||
|
|
Loading…
Reference in a new issue