Reworked & improved export when usig VFS feature
This commit is contained in:
parent
a2ac7e9f37
commit
0404777c32
4 changed files with 151 additions and 20 deletions
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue