Changes to prepare for VFS
This commit is contained in:
parent
5411648e2e
commit
d165b04307
14 changed files with 110 additions and 85 deletions
|
@ -19,11 +19,9 @@
|
|||
*/
|
||||
package org.linphone.activities.chat_bubble
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.webkit.MimeTypeMap
|
||||
import android.widget.Toast
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
@ -31,7 +29,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.linphone.LinphoneApplication
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.R
|
||||
import org.linphone.activities.GenericActivity
|
||||
|
@ -113,8 +110,12 @@ class ChatBubbleActivity : GenericActivity() {
|
|||
adapter.disableContextMenu()
|
||||
|
||||
adapter.openContentEvent.observe(this, {
|
||||
it.consume { path ->
|
||||
openFile(path)
|
||||
it.consume { content ->
|
||||
if (content.isFileEncrypted) {
|
||||
Toast.makeText(this, R.string.chat_bubble_cant_open_enrypted_file, Toast.LENGTH_LONG).show()
|
||||
} else {
|
||||
FileUtils.openFileInThirdPartyApp(this, content.filePath.orEmpty(), true)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -174,41 +175,4 @@ class ChatBubbleActivity : GenericActivity() {
|
|||
binding.chatMessagesList.scrollToPosition(adapter.itemCount - 1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openFile(contentFilePath: String) {
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
val contentUri: Uri = FileUtils.getPublicFilePath(this, contentFilePath)
|
||||
val filePath: String = contentUri.toString()
|
||||
Log.i("[Chat Bubble] Trying to open file: $filePath")
|
||||
var type: String? = null
|
||||
val extension = FileUtils.getExtensionFromFileName(filePath)
|
||||
|
||||
if (extension.isNotEmpty()) {
|
||||
Log.i("[Chat Bubble] Found extension $extension")
|
||||
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
|
||||
} else {
|
||||
Log.e("[Chat Bubble] Couldn't find extension")
|
||||
}
|
||||
|
||||
if (type != null) {
|
||||
Log.i("[Chat Bubble] Found matching MIME type $type")
|
||||
} else {
|
||||
type = "file/$extension"
|
||||
Log.e("[Chat Bubble] Can't get MIME type from extension: $extension, will use $type")
|
||||
}
|
||||
|
||||
intent.setDataAndType(contentUri, type)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
|
||||
try {
|
||||
startActivity(intent)
|
||||
|
||||
if (LinphoneApplication.corePreferences.enableAnimations) {
|
||||
overridePendingTransition(R.anim.enter_right, R.anim.exit_left)
|
||||
}
|
||||
} catch (anfe: ActivityNotFoundException) {
|
||||
Log.e("[Chat Bubble] Couldn't find an activity to handle MIME type: $type")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.linphone.activities.main.chat.viewmodels.OnContentClickedListener
|
|||
import org.linphone.activities.main.viewmodels.ListTopBarViewModel
|
||||
import org.linphone.core.ChatMessage
|
||||
import org.linphone.core.ChatRoomCapabilities
|
||||
import org.linphone.core.Content
|
||||
import org.linphone.core.EventLog
|
||||
import org.linphone.databinding.ChatEventListCellBinding
|
||||
import org.linphone.databinding.ChatMessageListCellBinding
|
||||
|
@ -73,13 +74,13 @@ class ChatMessagesListAdapter(
|
|||
MutableLiveData<Event<String>>()
|
||||
}
|
||||
|
||||
val openContentEvent: MutableLiveData<Event<String>> by lazy {
|
||||
MutableLiveData<Event<String>>()
|
||||
val openContentEvent: MutableLiveData<Event<Content>> by lazy {
|
||||
MutableLiveData<Event<Content>>()
|
||||
}
|
||||
|
||||
private val contentClickedListener = object : OnContentClickedListener {
|
||||
override fun onContentClicked(path: String) {
|
||||
openContentEvent.value = Event(path)
|
||||
override fun onContentClicked(content: Content) {
|
||||
openContentEvent.value = Event(content)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -222,25 +222,48 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
|
|||
})
|
||||
|
||||
adapter.openContentEvent.observe(viewLifecycleOwner, {
|
||||
it.consume { path ->
|
||||
it.consume { content ->
|
||||
val path = content.filePath.orEmpty()
|
||||
|
||||
if (!File(path).exists()) {
|
||||
(requireActivity() as MainActivity).showSnackBar(R.string.chat_room_file_not_found)
|
||||
} else {
|
||||
Log.i("[Chat Message] Opening file: $path")
|
||||
sharedViewModel.fileToOpen.value = path
|
||||
val preventScreenshots = viewModel.chatRoom.currentParams.encryptionEnabled()
|
||||
sharedViewModel.contentToOpen.value = content
|
||||
|
||||
if (corePreferences.useInAppFileViewerForNonEncryptedFiles || content.isFileEncrypted) {
|
||||
val preventScreenshots =
|
||||
viewModel.chatRoom.currentParams.encryptionEnabled()
|
||||
when {
|
||||
FileUtils.isExtensionImage(path) -> navigateToImageFileViewer(preventScreenshots)
|
||||
FileUtils.isExtensionVideo(path) -> navigateToVideoFileViewer(preventScreenshots)
|
||||
FileUtils.isExtensionAudio(path) -> navigateToAudioFileViewer(preventScreenshots)
|
||||
FileUtils.isExtensionPdf(path) -> navigateToPdfFileViewer(preventScreenshots)
|
||||
FileUtils.isPlainTextFile(path) -> navigateToTextFileViewer(preventScreenshots)
|
||||
FileUtils.isExtensionImage(path) -> navigateToImageFileViewer(
|
||||
preventScreenshots
|
||||
)
|
||||
FileUtils.isExtensionVideo(path) -> navigateToVideoFileViewer(
|
||||
preventScreenshots
|
||||
)
|
||||
FileUtils.isExtensionAudio(path) -> navigateToAudioFileViewer(
|
||||
preventScreenshots
|
||||
)
|
||||
FileUtils.isExtensionPdf(path) -> navigateToPdfFileViewer(
|
||||
preventScreenshots
|
||||
)
|
||||
FileUtils.isPlainTextFile(path) -> navigateToTextFileViewer(
|
||||
preventScreenshots
|
||||
)
|
||||
else -> {
|
||||
if (!FileUtils.openFileInThirdPartyApp(requireActivity(), path)) {
|
||||
if (content.isFileEncrypted) {
|
||||
Log.w("[Chat Message] File is encrypted and can't be opened in one of our viewers...")
|
||||
// TODO: show dialog to ask user for consent before trying to export the file first
|
||||
} else if (!FileUtils.openFileInThirdPartyApp(requireActivity(), path)) {
|
||||
showDialogToSuggestOpeningFileAsText()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!FileUtils.openFileInThirdPartyApp(requireActivity(), path)) {
|
||||
showDialogToSuggestOpeningFileAsText()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -146,10 +146,10 @@ class ChatMessageContentViewModel(
|
|||
}
|
||||
|
||||
fun openFile() {
|
||||
listener?.onContentClicked(content.filePath.orEmpty())
|
||||
listener?.onContentClicked(content)
|
||||
}
|
||||
}
|
||||
|
||||
interface OnContentClickedListener {
|
||||
fun onContentClicked(path: String)
|
||||
fun onContentClicked(content: Content)
|
||||
}
|
||||
|
|
|
@ -45,11 +45,13 @@ class ImageViewerFragment : SecureFragment<ImageViewerFragmentBinding>() {
|
|||
ViewModelProvider(this).get(SharedMainViewModel::class.java)
|
||||
}
|
||||
|
||||
val filePath = sharedViewModel.fileToOpen.value
|
||||
val content = sharedViewModel.contentToOpen.value
|
||||
content ?: return
|
||||
val filePath = content.filePath
|
||||
filePath ?: return
|
||||
|
||||
(childFragmentManager.findFragmentById(R.id.top_bar_fragment) as? TopBarFragment)
|
||||
?.setFilePath(filePath)
|
||||
?.setContent(content)
|
||||
|
||||
viewModel = ViewModelProvider(
|
||||
this,
|
||||
|
|
|
@ -45,11 +45,13 @@ class PdfViewerFragment : SecureFragment<PdfViewerFragmentBinding>() {
|
|||
ViewModelProvider(this).get(SharedMainViewModel::class.java)
|
||||
}
|
||||
|
||||
val filePath = sharedViewModel.fileToOpen.value
|
||||
val content = sharedViewModel.contentToOpen.value
|
||||
content ?: return
|
||||
val filePath = content.filePath
|
||||
filePath ?: return
|
||||
|
||||
(childFragmentManager.findFragmentById(R.id.top_bar_fragment) as? TopBarFragment)
|
||||
?.setFilePath(filePath)
|
||||
?.setContent(content)
|
||||
|
||||
viewModel = ViewModelProvider(
|
||||
this,
|
||||
|
|
|
@ -43,11 +43,13 @@ class TextViewerFragment : SecureFragment<TextViewerFragmentBinding>() {
|
|||
ViewModelProvider(this).get(SharedMainViewModel::class.java)
|
||||
}
|
||||
|
||||
val filePath = sharedViewModel.fileToOpen.value
|
||||
val content = sharedViewModel.contentToOpen.value
|
||||
content ?: return
|
||||
val filePath = content.filePath
|
||||
filePath ?: return
|
||||
|
||||
(childFragmentManager.findFragmentById(R.id.top_bar_fragment) as? TopBarFragment)
|
||||
?.setFilePath(filePath)
|
||||
?.setContent(content)
|
||||
|
||||
viewModel = ViewModelProvider(
|
||||
this,
|
||||
|
|
|
@ -24,12 +24,14 @@ import androidx.navigation.fragment.findNavController
|
|||
import org.linphone.R
|
||||
import org.linphone.activities.GenericFragment
|
||||
import org.linphone.activities.SnackBarActivity
|
||||
import org.linphone.core.Content
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.databinding.FileViewerTopBarFragmentBinding
|
||||
import org.linphone.utils.FileUtils
|
||||
|
||||
class TopBarFragment : GenericFragment<FileViewerTopBarFragmentBinding>() {
|
||||
private var filePath: String = ""
|
||||
private var content: Content? = null
|
||||
private var plainFilePath: String = ""
|
||||
|
||||
override fun getLayoutId(): Int = R.layout.file_viewer_top_bar_fragment
|
||||
|
||||
|
@ -43,24 +45,36 @@ class TopBarFragment : GenericFragment<FileViewerTopBarFragmentBinding>() {
|
|||
}
|
||||
|
||||
binding.setExportClickListener {
|
||||
if (!FileUtils.openFileInThirdPartyApp(requireActivity(), filePath)) {
|
||||
if (content != null) {
|
||||
val filePath = content?.plainFilePath.orEmpty()
|
||||
plainFilePath = if (filePath.isEmpty()) content?.filePath.orEmpty() else filePath
|
||||
Log.i("[File Viewer] Plain file path is: $filePath")
|
||||
if (plainFilePath.isNotEmpty()) {
|
||||
if (!FileUtils.openFileInThirdPartyApp(requireActivity(), plainFilePath)) {
|
||||
(requireActivity() as SnackBarActivity).showSnackBar(R.string.chat_message_no_app_found_to_handle_file_mime_type)
|
||||
if (plainFilePath != content?.filePath.orEmpty()) {
|
||||
Log.i("[File Viewer] No app to open plain file path: $plainFilePath, destroying it")
|
||||
FileUtils.deleteFile(plainFilePath)
|
||||
}
|
||||
plainFilePath = ""
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.e("[File Viewer] No Content set!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setFilePath(newFilePath: String) {
|
||||
Log.i("[File Viewer] File path is: $newFilePath")
|
||||
filePath = newFilePath
|
||||
override fun onDestroyView() {
|
||||
if (plainFilePath.isNotEmpty() && plainFilePath != content?.filePath.orEmpty()) {
|
||||
Log.i("[File Viewer] Destroying plain file path: $plainFilePath")
|
||||
FileUtils.deleteFile(plainFilePath)
|
||||
}
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
outState.putString("FilePath", filePath)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun onViewStateRestored(savedInstanceState: Bundle?) {
|
||||
super.onViewStateRestored(savedInstanceState)
|
||||
filePath = savedInstanceState?.getString("FilePath") ?: filePath
|
||||
fun setContent(c: Content) {
|
||||
Log.i("[File Viewer] Content file path is: ${c.filePath}")
|
||||
content = c
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,11 +47,13 @@ class VideoViewerFragment : SecureFragment<VideoViewerFragmentBinding>() {
|
|||
ViewModelProvider(this).get(SharedMainViewModel::class.java)
|
||||
}
|
||||
|
||||
val filePath = sharedViewModel.fileToOpen.value
|
||||
val content = sharedViewModel.contentToOpen.value
|
||||
content ?: return
|
||||
val filePath = content.filePath
|
||||
filePath ?: return
|
||||
|
||||
(childFragmentManager.findFragmentById(R.id.top_bar_fragment) as? TopBarFragment)
|
||||
?.setFilePath(filePath)
|
||||
?.setContent(content)
|
||||
|
||||
viewModel = ViewModelProvider(
|
||||
this,
|
||||
|
|
|
@ -48,7 +48,7 @@ class SharedMainViewModel : ViewModel() {
|
|||
MutableLiveData<Event<ChatMessage>>()
|
||||
}
|
||||
|
||||
val fileToOpen = MutableLiveData<String>()
|
||||
val contentToOpen = MutableLiveData<Content>()
|
||||
|
||||
var createEncryptedChatRoom: Boolean = false
|
||||
|
||||
|
|
|
@ -128,6 +128,12 @@ class CorePreferences constructor(private val context: Context) {
|
|||
config.setBool("app", "make_downloaded_images_public_in_gallery", value)
|
||||
}
|
||||
|
||||
var useInAppFileViewerForNonEncryptedFiles: Boolean
|
||||
get() = config.getBool("app", "use_in_app_file_viewer_for_non_encrypted_files", true)
|
||||
set(value) {
|
||||
config.setBool("app", "use_in_app_file_viewer_for_non_encrypted_files", value)
|
||||
}
|
||||
|
||||
var hideChatMessageContentInNotification: Boolean
|
||||
get() = config.getBool("app", "hide_chat_message_content_in_notification", false)
|
||||
set(value) {
|
||||
|
|
|
@ -297,7 +297,11 @@ class FileUtils {
|
|||
return contentUri
|
||||
}
|
||||
|
||||
fun openFileInThirdPartyApp(activity: Activity, contentFilePath: String): Boolean {
|
||||
fun openFileInThirdPartyApp(
|
||||
activity: Activity,
|
||||
contentFilePath: String,
|
||||
newTask: Boolean = false
|
||||
): Boolean {
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
val contentUri: Uri = getPublicFilePath(activity, contentFilePath)
|
||||
val filePath: String = contentUri.toString()
|
||||
|
@ -321,6 +325,9 @@ class FileUtils {
|
|||
|
||||
intent.setDataAndType(contentUri, type)
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
if (newTask) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
|
||||
try {
|
||||
activity.startActivity(intent)
|
||||
|
|
|
@ -568,4 +568,5 @@
|
|||
<string name="content_description_open_app">Ouvrir les conversations dans l\'application au lieu de la bulle</string>
|
||||
<string name="content_description_export">Ouvrir le fichier dans une application tierce</string>
|
||||
<string name="chat_message_no_app_found_to_handle_file_mime_type">Aucune application n\'est disponible pour ouvrir ce type de fichier</string>
|
||||
<string name="chat_bubble_cant_open_enrypted_file">Impossible d\'ouvrir un fichier chiffré dans la bulle de conversation</string>
|
||||
</resources>
|
|
@ -201,6 +201,7 @@
|
|||
</plurals>
|
||||
<string name="chat_message_notification_hidden_content"><Redacted></string>
|
||||
<string name="chat_message_no_app_found_to_handle_file_mime_type">No app available for this kind of file</string>
|
||||
<string name="chat_bubble_cant_open_enrypted_file">Can\'t open encrypted file in chat bubble</string>
|
||||
|
||||
<!-- Recordings -->
|
||||
<string name="recordings_empty_list">No recordings</string>
|
||||
|
|
Loading…
Reference in a new issue