diff --git a/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageAttachmentData.kt b/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageAttachmentData.kt index 0dd0211e4..caa0a4f5c 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageAttachmentData.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageAttachmentData.kt @@ -19,6 +19,7 @@ */ package org.linphone.activities.main.chat.data +import android.webkit.MimeTypeMap import org.linphone.utils.FileUtils class ChatMessageAttachmentData( @@ -26,10 +27,19 @@ class ChatMessageAttachmentData( private val deleteCallback: (attachment: ChatMessageAttachmentData) -> Unit ) { val fileName: String = FileUtils.getNameFromFilePath(path) - val isImage: Boolean = FileUtils.isExtensionImage(path) - val isVideo: Boolean = FileUtils.isExtensionVideo(path) - val isAudio: Boolean = FileUtils.isExtensionAudio(path) - val isPdf: Boolean = FileUtils.isExtensionPdf(path) + val isImage: Boolean + val isVideo: Boolean + val isAudio: Boolean + val isPdf: Boolean + + init { + val extension = FileUtils.getExtensionFromFileName(path) + val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + isImage = FileUtils.isMimeImage(mime) + isVideo = FileUtils.isMimeVideo(mime) + isAudio = FileUtils.isMimeAudio(mime) + isPdf = FileUtils.isMimePdf(mime) + } fun delete() { deleteCallback(this) diff --git a/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageContentData.kt b/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageContentData.kt index 20ec64c49..a1d243b75 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageContentData.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageContentData.kt @@ -23,6 +23,7 @@ import android.text.Spannable import android.text.SpannableString import android.text.Spanned import android.text.style.UnderlineSpan +import android.webkit.MimeTypeMap import android.widget.Toast import androidx.lifecycle.MutableLiveData import androidx.media.AudioFocusRequestCompat @@ -259,12 +260,23 @@ class ChatMessageContentData( isConferenceSchedule.value = isConferenceIcs if (path.isNotEmpty()) { - Log.i("[Content] Found displayable content: $path") filePath.value = path - isImage.value = FileUtils.isExtensionImage(path) - isVideo.value = FileUtils.isExtensionVideo(path) && !isVoiceRecord - isAudio.value = FileUtils.isExtensionAudio(path) && !isVoiceRecord - isPdf.value = FileUtils.isExtensionPdf(path) + val extension = FileUtils.getExtensionFromFileName(path) + val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + isImage.value = FileUtils.isMimeImage(mime) + isVideo.value = FileUtils.isMimeVideo(mime) && !isVoiceRecord + isAudio.value = FileUtils.isMimeAudio(mime) && !isVoiceRecord + isPdf.value = FileUtils.isMimePdf(mime) + val type = when { + isImage.value == true -> "image" + isVideo.value == true -> "video" + isAudio.value == true -> "audio" + isPdf.value == true -> "pdf" + isVoiceRecord -> "voice recording" + isConferenceIcs -> "conference invitation" + else -> "unknown" + } + Log.i("[Content] Extension for file [$path] is [$extension], deduced type from MIME is [$type]") if (isVoiceRecord) { val duration = content.fileDuration // duration is in ms @@ -288,10 +300,12 @@ class ChatMessageContentData( } } else if (content.isFileTransfer) { downloadable.value = true - isImage.value = FileUtils.isExtensionImage(fileName.value!!) - isVideo.value = FileUtils.isExtensionVideo(fileName.value!!) - isAudio.value = FileUtils.isExtensionAudio(fileName.value!!) - isPdf.value = FileUtils.isExtensionPdf(fileName.value!!) + val extension = FileUtils.getExtensionFromFileName(fileName.value!!) + val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + isImage.value = FileUtils.isMimeImage(mime) + isVideo.value = FileUtils.isMimeVideo(mime) + isAudio.value = FileUtils.isMimeAudio(mime) + isPdf.value = FileUtils.isMimePdf(mime) isVoiceRecording.value = false isConferenceSchedule.value = false } else if (content.isIcalendar) { diff --git a/app/src/main/java/org/linphone/activities/main/chat/fragments/DetailChatRoomFragment.kt b/app/src/main/java/org/linphone/activities/main/chat/fragments/DetailChatRoomFragment.kt index c63242d62..e01f5d1be 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/fragments/DetailChatRoomFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/fragments/DetailChatRoomFragment.kt @@ -29,6 +29,7 @@ import android.os.Parcelable import android.provider.MediaStore import android.view.* import android.view.ViewTreeObserver.OnGlobalLayoutListener +import android.webkit.MimeTypeMap import android.widget.PopupWindow import androidx.core.content.ContextCompat import androidx.core.content.FileProvider @@ -457,20 +458,23 @@ class DetailChatRoomFragment : MasterFragment navigateToImageFileViewer( + FileUtils.isMimeImage(mime) -> navigateToImageFileViewer( preventScreenshots ) - FileUtils.isExtensionVideo(path) -> navigateToVideoFileViewer( + FileUtils.isMimeVideo(mime) -> navigateToVideoFileViewer( preventScreenshots ) - FileUtils.isExtensionAudio(path) -> navigateToAudioFileViewer( + FileUtils.isMimeAudio(mime) -> navigateToAudioFileViewer( preventScreenshots ) - FileUtils.isExtensionPdf(path) -> navigateToPdfFileViewer( + FileUtils.isMimePdf(mime) -> navigateToPdfFileViewer( preventScreenshots ) - FileUtils.isPlainTextFile(path) -> navigateToTextFileViewer( + FileUtils.isMimePlainText(mime) -> navigateToTextFileViewer( preventScreenshots ) else -> { diff --git a/app/src/main/java/org/linphone/activities/main/files/fragments/TopBarFragment.kt b/app/src/main/java/org/linphone/activities/main/files/fragments/TopBarFragment.kt index 9134163dc..868f7b946 100644 --- a/app/src/main/java/org/linphone/activities/main/files/fragments/TopBarFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/files/fragments/TopBarFragment.kt @@ -21,6 +21,7 @@ package org.linphone.activities.main.files.fragments import android.os.Bundle import android.view.View +import android.webkit.MimeTypeMap import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async @@ -89,8 +90,10 @@ class TopBarFragment : GenericFragment() { val filePath = content.filePath.orEmpty() Log.i("[File Viewer] Trying to export file [$filePath] through Media Store API") + val extension = FileUtils.getExtensionFromFileName(filePath) + val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) when { - FileUtils.isExtensionImage(filePath) -> { + FileUtils.isMimeImage(mime) -> { val export = lifecycleScope.async { Compatibility.addImageToMediaStore(requireContext(), content) } @@ -101,7 +104,7 @@ class TopBarFragment : GenericFragment() { Log.e("[File Viewer] Something went wrong while copying file to Media Store...") } } - FileUtils.isExtensionVideo(filePath) -> { + FileUtils.isMimeVideo(mime) -> { val export = lifecycleScope.async { Compatibility.addVideoToMediaStore(requireContext(), content) } @@ -112,7 +115,7 @@ class TopBarFragment : GenericFragment() { Log.e("[File Viewer] Something went wrong while copying file to Media Store...") } } - FileUtils.isExtensionAudio(filePath) -> { + FileUtils.isMimeAudio(mime) -> { val export = lifecycleScope.async { Compatibility.addAudioToMediaStore(requireContext(), content) } diff --git a/app/src/main/java/org/linphone/core/CoreContext.kt b/app/src/main/java/org/linphone/core/CoreContext.kt index 12deefea3..2729ecb14 100644 --- a/app/src/main/java/org/linphone/core/CoreContext.kt +++ b/app/src/main/java/org/linphone/core/CoreContext.kt @@ -31,6 +31,7 @@ import android.telephony.TelephonyManager import android.util.Base64 import android.util.Pair import android.view.* +import android.webkit.MimeTypeMap import androidx.emoji.bundled.BundledEmojiCompatConfig import androidx.emoji.text.EmojiCompat import androidx.lifecycle.* @@ -894,22 +895,24 @@ class CoreContext( val filePath = content.filePath.orEmpty() Log.i("[Context] Trying to export file [$filePath] through Media Store API") + val extension = FileUtils.getExtensionFromFileName(filePath) + val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) when { - FileUtils.isExtensionImage(filePath) -> { + FileUtils.isMimeImage(mime) -> { if (Compatibility.addImageToMediaStore(context, content)) { Log.i("[Context] Successfully exported image [${content.name}] to Media Store") } else { Log.e("[Context] Something went wrong while copying file to Media Store...") } } - FileUtils.isExtensionVideo(filePath) -> { + FileUtils.isMimeVideo(mime) -> { if (Compatibility.addVideoToMediaStore(context, content)) { Log.i("[Context] Successfully exported video [${content.name}] to Media Store") } else { Log.e("[Context] Something went wrong while copying file to Media Store...") } } - FileUtils.isExtensionAudio(filePath) -> { + FileUtils.isMimeAudio(mime) -> { if (Compatibility.addAudioToMediaStore(context, content)) { Log.i("[Context] Successfully exported audio [${content.name}] to Media Store") } else { diff --git a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt index 3e85dcf25..93df621d9 100644 --- a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt +++ b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt @@ -326,11 +326,22 @@ fun loadImageWithCoil(imageView: ImageView, path: String?) { if (corePreferences.vfsEnabled && path.endsWith(FileUtils.VFS_PLAIN_FILE_EXTENSION)) { imageView.load(path) { diskCachePolicy(CachePolicy.DISABLED) + listener( + onError = { _, result -> + Log.e("[Data Binding] [Coil] Error loading [$path]: ${result.throwable}") + } + ) } } else { - imageView.load(path) + imageView.load(path) { + listener( + onError = { _, result -> + Log.e("[Data Binding] [Coil] Error loading [$path]: ${result.throwable}") + } + ) + } } - } else { + } else if (path != null) { Log.w("[Data Binding] [Coil] Can't load $path") } } @@ -466,8 +477,8 @@ fun loadAvatarWithCoil(imageView: ImageView, path: String?) { imageView.load(path) { transformations(CircleCropTransformation()) listener( - onError = { _, _ -> - Log.w("[Data Binding] [Coil] Can't load $path") + onError = { _, result -> + Log.e("[Data Binding] [Coil] Error loading [$path]: ${result.throwable}") imageView.visibility = View.GONE }, onSuccess = { _, _ -> diff --git a/app/src/main/java/org/linphone/utils/FileUtils.kt b/app/src/main/java/org/linphone/utils/FileUtils.kt index 665827373..576d61e6c 100644 --- a/app/src/main/java/org/linphone/utils/FileUtils.kt +++ b/app/src/main/java/org/linphone/utils/FileUtils.kt @@ -66,39 +66,41 @@ class FileUtils { } } - return extension + return extension.lowercase(Locale.getDefault()) } - fun isPlainTextFile(path: String): Boolean { - val extension = getExtensionFromFileName(path).lowercase(Locale.getDefault()) - val type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + fun isMimePlainText(type: String?): Boolean { return type?.startsWith("text/plain") ?: false } - fun isExtensionPdf(path: String): Boolean { - val extension = getExtensionFromFileName(path).lowercase(Locale.getDefault()) - val type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + fun isMimePdf(type: String?): Boolean { return type?.startsWith("application/pdf") ?: false } - fun isExtensionImage(path: String): Boolean { - val extension = getExtensionFromFileName(path).lowercase(Locale.getDefault()) - val type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + fun isMimeImage(type: String?): Boolean { return type?.startsWith("image/") ?: false } - fun isExtensionVideo(path: String): Boolean { - val extension = getExtensionFromFileName(path).lowercase(Locale.getDefault()) - val type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + fun isMimeVideo(type: String?): Boolean { return type?.startsWith("video/") ?: false } - fun isExtensionAudio(path: String): Boolean { - val extension = getExtensionFromFileName(path).lowercase(Locale.getDefault()) - val type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + fun isMimeAudio(type: String?): Boolean { return type?.startsWith("audio/") ?: false } + fun isExtensionImage(path: String): Boolean { + val extension = getExtensionFromFileName(path) + val type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + return isMimeImage(type) + } + + fun isExtensionVideo(path: String): Boolean { + val extension = getExtensionFromFileName(path) + val type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) + return isMimeVideo(type) + } + fun clearExistingPlainFiles() { for (file in coreContext.context.filesDir.listFiles().orEmpty()) { if (file.path.endsWith(VFS_PLAIN_FILE_EXTENSION)) {