Reworked & improved export when usig VFS feature

This commit is contained in:
Sylvain Berfini 2022-01-03 17:00:49 +01:00
parent a2ac7e9f37
commit 0404777c32
4 changed files with 151 additions and 20 deletions

View file

@ -21,13 +21,21 @@ package org.linphone.activities.main.files.fragments
import android.os.Bundle
import android.view.View
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.linphone.R
import org.linphone.activities.GenericFragment
import org.linphone.activities.SnackBarActivity
import org.linphone.compatibility.Compatibility
import org.linphone.core.Content
import org.linphone.core.tools.Log
import org.linphone.databinding.FileViewerTopBarFragmentBinding
import org.linphone.mediastream.Version
import org.linphone.utils.FileUtils
import org.linphone.utils.PermissionHelper
class TopBarFragment : GenericFragment<FileViewerTopBarFragmentBinding>() {
private var content: Content? = null
@ -46,20 +54,9 @@ class TopBarFragment : GenericFragment<FileViewerTopBarFragmentBinding>() {
}
binding.setExportClickListener {
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: $plainFilePath")
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 = ""
}
}
val contentToExport = content
if (contentToExport != null) {
exportContent(contentToExport)
} else {
Log.e("[File Viewer] No Content set!")
}
@ -89,4 +86,77 @@ class TopBarFragment : GenericFragment<FileViewerTopBarFragmentBinding>() {
content = c
binding.fileName.text = c.name
}
private fun exportContent(content: Content) {
lifecycleScope.launch {
var mediaStoreFilePath = ""
if (Version.sdkAboveOrEqual(Version.API29_ANDROID_10) || PermissionHelper.get().hasWriteExternalStoragePermission()) {
Log.i("[File Viewer] Exporting image through Media Store API")
when (content.type) {
"image" -> {
val export = lifecycleScope.async {
Compatibility.addImageToMediaStore(requireContext(), content)
}
if (export.await()) {
Log.i("[File Viewer] Adding image ${content.name} to Media Store terminated: ${content.userData}")
mediaStoreFilePath = content.userData.toString()
} else {
Log.e("[File Viewer] Something went wrong while copying file to Media Store...")
}
}
"video" -> {
val export = lifecycleScope.async {
Compatibility.addVideoToMediaStore(requireContext(), content)
}
if (export.await()) {
Log.i("[File Viewer] Adding video ${content.name} to Media Store terminated: ${content.userData}")
mediaStoreFilePath = content.userData.toString()
} else {
Log.e("[File Viewer] Something went wrong while copying file to Media Store...")
}
}
"audio" -> {
val export = lifecycleScope.async {
Compatibility.addAudioToMediaStore(requireContext(), content)
}
if (export.await()) {
Log.i("[File Viewer] Adding audio ${content.name} to Media Store terminated: ${content.userData}")
mediaStoreFilePath = content.userData.toString()
} else {
Log.e("[File Viewer] Something went wrong while copying file to Media Store...")
}
}
else -> {
Log.w("[File Viewer] File ${content.name} isn't either an image, an audio file or a video, can't add it to the Media Store")
}
}
} else {
Log.w("[File Viewer] Can't export image through Media Store API (requires Android 10 or WRITE_EXTERNAL permission, using fallback method...")
}
withContext(Dispatchers.Main) {
if (mediaStoreFilePath.isEmpty()) {
Log.w("[File Viewer] Media store file path is empty, media store export failed?")
val filePath = content.plainFilePath.orEmpty()
plainFilePath = if (filePath.isEmpty()) content.filePath.orEmpty() else filePath
Log.i("[File Viewer] Plain file path is: $plainFilePath")
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 {
plainFilePath = ""
Log.i("[File Viewer] Media store file path is: $mediaStoreFilePath")
FileUtils.openMediaStoreFile(requireActivity(), mediaStoreFilePath)
}
}
}
}
}

View file

@ -77,7 +77,9 @@ class Api21Compatibility {
return false
}
val filePath = content.filePath
val plainFilePath = content.plainFilePath.orEmpty()
val isVfsEncrypted = plainFilePath.isNotEmpty()
val filePath = if (isVfsEncrypted) plainFilePath else content.filePath
if (filePath == null) {
Log.e("[Media Store] Content doesn't have a file path!")
return false
@ -95,6 +97,10 @@ class Api21Compatibility {
}
val collection = MediaStore.Images.Media.getContentUri("external")
val mediaStoreFilePath = addContentValuesToCollection(context, filePath, collection, values)
if (isVfsEncrypted) {
Log.w("[Media Store] Content was encrypted, delete plain version: $plainFilePath")
FileUtils.deleteFile(plainFilePath)
}
if (mediaStoreFilePath.isNotEmpty()) {
content.userData = mediaStoreFilePath
return true
@ -108,7 +114,9 @@ class Api21Compatibility {
return false
}
val filePath = content.filePath
val plainFilePath = content.plainFilePath.orEmpty()
val isVfsEncrypted = plainFilePath.isNotEmpty()
val filePath = if (isVfsEncrypted) plainFilePath else content.filePath
if (filePath == null) {
Log.e("[Media Store] Content doesn't have a file path!")
return false
@ -127,6 +135,10 @@ class Api21Compatibility {
}
val collection = MediaStore.Video.Media.getContentUri("external")
val mediaStoreFilePath = addContentValuesToCollection(context, filePath, collection, values)
if (isVfsEncrypted) {
Log.w("[Media Store] Content was encrypted, delete plain version: $plainFilePath")
FileUtils.deleteFile(plainFilePath)
}
if (mediaStoreFilePath.isNotEmpty()) {
content.userData = mediaStoreFilePath
return true
@ -140,7 +152,9 @@ class Api21Compatibility {
return false
}
val filePath = content.filePath
val plainFilePath = content.plainFilePath.orEmpty()
val isVfsEncrypted = plainFilePath.isNotEmpty()
val filePath = if (isVfsEncrypted) plainFilePath else content.filePath
if (filePath == null) {
Log.e("[Media Store] Content doesn't have a file path!")
return false
@ -160,6 +174,10 @@ class Api21Compatibility {
val collection = MediaStore.Audio.Media.getContentUri("external")
val mediaStoreFilePath = addContentValuesToCollection(context, filePath, collection, values)
if (isVfsEncrypted) {
Log.w("[Media Store] Content was encrypted, delete plain version: $plainFilePath")
FileUtils.deleteFile(plainFilePath)
}
if (mediaStoreFilePath.isNotEmpty()) {
content.userData = mediaStoreFilePath
return true

View file

@ -99,7 +99,9 @@ class Api29Compatibility {
}
suspend fun addImageToMediaStore(context: Context, content: Content): Boolean {
val filePath = content.filePath
val plainFilePath = content.plainFilePath.orEmpty()
val isVfsEncrypted = plainFilePath.isNotEmpty()
val filePath = if (isVfsEncrypted) plainFilePath else content.filePath
if (filePath == null) {
Log.e("[Media Store] Content doesn't have a file path!")
return false
@ -119,6 +121,11 @@ class Api29Compatibility {
}
val collection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val mediaStoreFilePath = addContentValuesToCollection(context, filePath, collection, values, MediaStore.Images.Media.IS_PENDING)
if (isVfsEncrypted) {
Log.w("[Media Store] Content was encrypted, delete plain version: $plainFilePath")
FileUtils.deleteFile(plainFilePath)
}
if (mediaStoreFilePath.isNotEmpty()) {
content.userData = mediaStoreFilePath
return true
@ -127,7 +134,9 @@ class Api29Compatibility {
}
suspend fun addVideoToMediaStore(context: Context, content: Content): Boolean {
val filePath = content.filePath
val plainFilePath = content.plainFilePath.orEmpty()
val isVfsEncrypted = plainFilePath.isNotEmpty()
val filePath = if (isVfsEncrypted) plainFilePath else content.filePath
if (filePath == null) {
Log.e("[Media Store] Content doesn't have a file path!")
return false
@ -148,6 +157,11 @@ class Api29Compatibility {
}
val collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val mediaStoreFilePath = addContentValuesToCollection(context, filePath, collection, values, MediaStore.Video.Media.IS_PENDING)
if (isVfsEncrypted) {
Log.w("[Media Store] Content was encrypted, delete plain version: $plainFilePath")
FileUtils.deleteFile(plainFilePath)
}
if (mediaStoreFilePath.isNotEmpty()) {
content.userData = mediaStoreFilePath
return true
@ -156,7 +170,9 @@ class Api29Compatibility {
}
suspend fun addAudioToMediaStore(context: Context, content: Content): Boolean {
val filePath = content.filePath
val plainFilePath = content.plainFilePath.orEmpty()
val isVfsEncrypted = plainFilePath.isNotEmpty()
val filePath = if (isVfsEncrypted) plainFilePath else content.filePath
if (filePath == null) {
Log.e("[Media Store] Content doesn't have a file path!")
return false
@ -178,6 +194,11 @@ class Api29Compatibility {
val collection = MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val mediaStoreFilePath = addContentValuesToCollection(context, filePath, collection, values, MediaStore.Audio.Media.IS_PENDING)
if (isVfsEncrypted) {
Log.w("[Media Store] Content was encrypted, delete plain version: $plainFilePath")
FileUtils.deleteFile(plainFilePath)
}
if (mediaStoreFilePath.isNotEmpty()) {
content.userData = mediaStoreFilePath
return true

View file

@ -470,5 +470,27 @@ class FileUtils {
}
return false
}
fun openMediaStoreFile(
activity: Activity,
contentFilePath: String,
newTask: Boolean = false
): Boolean {
val intent = Intent(Intent.ACTION_VIEW)
val contentUri: Uri = Uri.parse(contentFilePath)
intent.data = contentUri
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
if (newTask) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
try {
activity.startActivity(intent)
return true
} catch (anfe: ActivityNotFoundException) {
Log.e("[File Viewer] Can't open media store export in third party app: $anfe")
}
return false
}
}
}