Improved file copy code by using coroutines
This commit is contained in:
parent
de389858ce
commit
e1dc8ad8c2
8 changed files with 158 additions and 119 deletions
|
@ -30,12 +30,14 @@ import android.view.inputmethod.InputMethodManager
|
|||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDestination
|
||||
import androidx.navigation.findNavController
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import java.io.UnsupportedEncodingException
|
||||
import java.net.URLDecoder
|
||||
import kotlinx.coroutines.*
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||
import org.linphone.R
|
||||
|
@ -147,11 +149,15 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
|
|||
private fun handleIntentParams(intent: Intent) {
|
||||
when (intent.action) {
|
||||
Intent.ACTION_SEND -> {
|
||||
lifecycleScope.launch {
|
||||
handleSendImage(intent)
|
||||
}
|
||||
}
|
||||
Intent.ACTION_SEND_MULTIPLE -> {
|
||||
lifecycleScope.launch {
|
||||
handleSendMultipleImages(intent)
|
||||
}
|
||||
}
|
||||
Intent.ACTION_VIEW -> {
|
||||
if (intent.type == AppUtils.getString(R.string.linphone_address_mime_type)) {
|
||||
val contactUri = intent.data
|
||||
|
@ -218,14 +224,19 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleSendImage(intent: Intent) {
|
||||
private suspend fun handleSendImage(intent: Intent) {
|
||||
(intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri)?.let {
|
||||
val list = arrayListOf<String>()
|
||||
val path = FileUtils.getFilePath(this, it)
|
||||
coroutineScope {
|
||||
val deferred = async {
|
||||
FileUtils.getFilePath(this@MainActivity, it)
|
||||
}
|
||||
val path = deferred.await()
|
||||
if (path != null) {
|
||||
list.add(path)
|
||||
Log.i("[Main Activity] Found single file to share: $path")
|
||||
}
|
||||
}
|
||||
sharedViewModel.filesToShare.value = list
|
||||
|
||||
val deepLink = "linphone-android://chat/"
|
||||
|
@ -234,15 +245,21 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleSendMultipleImages(intent: Intent) {
|
||||
private suspend fun handleSendMultipleImages(intent: Intent) {
|
||||
intent.getParcelableArrayListExtra<Parcelable>(Intent.EXTRA_STREAM)?.let {
|
||||
val list = arrayListOf<String>()
|
||||
coroutineScope {
|
||||
val deferred = arrayListOf<Deferred<String?>>()
|
||||
for (parcelable in it) {
|
||||
val uri = parcelable as Uri
|
||||
val path = FileUtils.getFilePath(this, uri)
|
||||
deferred.add(async { FileUtils.getFilePath(this@MainActivity, uri) })
|
||||
}
|
||||
val paths = deferred.awaitAll()
|
||||
for (path in paths) {
|
||||
Log.i("[Main Activity] Found file to share: $path")
|
||||
if (path != null) list.add(path)
|
||||
}
|
||||
}
|
||||
sharedViewModel.filesToShare.value = list
|
||||
|
||||
val deepLink = "linphone-android://chat/"
|
||||
|
|
|
@ -35,12 +35,14 @@ import androidx.appcompat.view.menu.MenuPopupHelper
|
|||
import androidx.core.content.FileProvider
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.Navigation
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import java.io.File
|
||||
import kotlinx.android.synthetic.main.tabs_fragment.*
|
||||
import kotlinx.coroutines.launch
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||
import org.linphone.R
|
||||
|
@ -299,8 +301,8 @@ class DetailChatRoomFragment : MasterFragment() {
|
|||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
lifecycleScope.launch {
|
||||
var fileToUploadPath: String? = null
|
||||
|
||||
val temporaryFileUploadPath = chatSendingViewModel.temporaryFileUploadPath
|
||||
if (temporaryFileUploadPath != null) {
|
||||
if (data != null) {
|
||||
|
@ -336,6 +338,7 @@ class DetailChatRoomFragment : MasterFragment() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun enterEditionMode() {
|
||||
listSelectionViewModel.isEditionEnabled.value = true
|
||||
|
|
|
@ -22,6 +22,8 @@ package org.linphone.activities.main.chat.viewmodels
|
|||
import android.os.CountDownTimer
|
||||
import android.text.Spanned
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||
import org.linphone.R
|
||||
|
@ -205,6 +207,7 @@ class ChatMessageViewModel(
|
|||
}
|
||||
|
||||
private fun addContentToMediaStore(content: Content) {
|
||||
viewModelScope.launch {
|
||||
when (content.type) {
|
||||
"image" -> {
|
||||
if (Compatibility.addImageToMediaStore(coreContext.context, content)) {
|
||||
|
@ -233,3 +236,4 @@ class ChatMessageViewModel(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,8 +31,10 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import java.io.File
|
||||
import kotlinx.coroutines.launch
|
||||
import org.linphone.R
|
||||
import org.linphone.activities.main.MainActivity
|
||||
import org.linphone.activities.main.contact.viewmodels.*
|
||||
|
@ -130,6 +132,7 @@ class ContactEditorFragment : Fragment() {
|
|||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
lifecycleScope.launch {
|
||||
var fileToUploadPath: String? = null
|
||||
|
||||
val temporaryFileUploadPath = temporaryPicturePath
|
||||
|
@ -167,6 +170,7 @@ class ContactEditorFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun pickFile() {
|
||||
val cameraIntents = ArrayList<Intent>()
|
||||
|
|
|
@ -57,7 +57,7 @@ class Api21Compatibility {
|
|||
vibrator.vibrate(pattern, 1)
|
||||
}
|
||||
|
||||
fun addImageToMediaStore(context: Context, content: Content): Boolean {
|
||||
suspend fun addImageToMediaStore(context: Context, content: Content): Boolean {
|
||||
val filePath = content.filePath
|
||||
val appName = AppUtils.getString(R.string.app_name)
|
||||
val relativePath = "${Environment.DIRECTORY_PICTURES}/$appName"
|
||||
|
@ -88,7 +88,7 @@ class Api21Compatibility {
|
|||
return copyOk
|
||||
}
|
||||
|
||||
fun addVideoToMediaStore(context: Context, content: Content): Boolean {
|
||||
suspend fun addVideoToMediaStore(context: Context, content: Content): Boolean {
|
||||
val filePath = content.filePath
|
||||
val appName = AppUtils.getString(R.string.app_name)
|
||||
val relativePath = "${Environment.DIRECTORY_MOVIES}/$appName"
|
||||
|
@ -120,7 +120,7 @@ class Api21Compatibility {
|
|||
return copyOk
|
||||
}
|
||||
|
||||
fun addAudioToMediaStore(context: Context, content: Content): Boolean {
|
||||
suspend fun addAudioToMediaStore(context: Context, content: Content): Boolean {
|
||||
val filePath = content.filePath
|
||||
val appName = AppUtils.getString(R.string.app_name)
|
||||
val relativePath = "${Environment.DIRECTORY_MUSIC}/$appName"
|
||||
|
|
|
@ -33,7 +33,7 @@ import org.linphone.utils.FileUtils
|
|||
@TargetApi(29)
|
||||
class Api29Compatibility {
|
||||
companion object {
|
||||
fun addImageToMediaStore(context: Context, content: Content): Boolean {
|
||||
suspend fun addImageToMediaStore(context: Context, content: Content): Boolean {
|
||||
val filePath = content.filePath
|
||||
val appName = AppUtils.getString(R.string.app_name)
|
||||
val relativePath = "${Environment.DIRECTORY_PICTURES}/$appName"
|
||||
|
@ -70,7 +70,7 @@ class Api29Compatibility {
|
|||
return copyOk
|
||||
}
|
||||
|
||||
fun addVideoToMediaStore(context: Context, content: Content): Boolean {
|
||||
suspend fun addVideoToMediaStore(context: Context, content: Content): Boolean {
|
||||
val filePath = content.filePath
|
||||
val appName = AppUtils.getString(R.string.app_name)
|
||||
val relativePath = "${Environment.DIRECTORY_MOVIES}/$appName"
|
||||
|
@ -108,7 +108,7 @@ class Api29Compatibility {
|
|||
return copyOk
|
||||
}
|
||||
|
||||
fun addAudioToMediaStore(context: Context, content: Content): Boolean {
|
||||
suspend fun addAudioToMediaStore(context: Context, content: Content): Boolean {
|
||||
val filePath = content.filePath
|
||||
val appName = AppUtils.getString(R.string.app_name)
|
||||
val relativePath = "${Environment.DIRECTORY_MUSIC}/$appName"
|
||||
|
|
|
@ -115,21 +115,21 @@ class Compatibility {
|
|||
// TODO Use removeLongLivedShortcuts() starting Android R (API 30)
|
||||
}
|
||||
|
||||
fun addImageToMediaStore(context: Context, content: Content): Boolean {
|
||||
suspend fun addImageToMediaStore(context: Context, content: Content): Boolean {
|
||||
if (Version.sdkAboveOrEqual(Version.API29_ANDROID_10)) {
|
||||
return Api29Compatibility.addImageToMediaStore(context, content)
|
||||
}
|
||||
return Api21Compatibility.addImageToMediaStore(context, content)
|
||||
}
|
||||
|
||||
fun addVideoToMediaStore(context: Context, content: Content): Boolean {
|
||||
suspend fun addVideoToMediaStore(context: Context, content: Content): Boolean {
|
||||
if (Version.sdkAboveOrEqual(Version.API29_ANDROID_10)) {
|
||||
return Api29Compatibility.addVideoToMediaStore(context, content)
|
||||
}
|
||||
return Api21Compatibility.addVideoToMediaStore(context, content)
|
||||
}
|
||||
|
||||
fun addAudioToMediaStore(context: Context, content: Content): Boolean {
|
||||
suspend fun addAudioToMediaStore(context: Context, content: Content): Boolean {
|
||||
if (Version.sdkAboveOrEqual(Version.API29_ANDROID_10)) {
|
||||
return Api29Compatibility.addAudioToMediaStore(context, content)
|
||||
}
|
||||
|
|
|
@ -26,6 +26,10 @@ import android.provider.OpenableColumns
|
|||
import java.io.*
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.core.tools.Log
|
||||
|
||||
|
@ -109,7 +113,7 @@ class FileUtils {
|
|||
}
|
||||
}
|
||||
|
||||
fun getFilePath(context: Context, uri: Uri): String? {
|
||||
suspend fun getFilePath(context: Context, uri: Uri): String? {
|
||||
var result: String? = null
|
||||
val name: String = getNameFromUri(uri, context)
|
||||
|
||||
|
@ -123,13 +127,16 @@ class FileUtils {
|
|||
" to local file " +
|
||||
localFile.absolutePath
|
||||
)
|
||||
if (copyToFile(remoteFile, localFile)) {
|
||||
coroutineScope {
|
||||
val deferred = async { copyToFile(remoteFile, localFile) }
|
||||
if (deferred.await()) {
|
||||
Log.i("[File Utils] Copy successful")
|
||||
result = localFile.absolutePath
|
||||
} else {
|
||||
Log.e("[File Utils] Copy failed")
|
||||
}
|
||||
remoteFile?.close()
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.e("[File Utils] getFilePath exception: ", e)
|
||||
}
|
||||
|
@ -158,7 +165,7 @@ class FileUtils {
|
|||
return name
|
||||
}
|
||||
|
||||
fun copyFileTo(filePath: String, outputStream: OutputStream?): Boolean {
|
||||
suspend fun copyFileTo(filePath: String, outputStream: OutputStream?): Boolean {
|
||||
if (outputStream == null) {
|
||||
Log.e("[File Utils] Can't copy file $filePath to given null output stream")
|
||||
return false
|
||||
|
@ -171,12 +178,14 @@ class FileUtils {
|
|||
}
|
||||
|
||||
try {
|
||||
withContext(Dispatchers.IO) {
|
||||
val inputStream = FileInputStream(file)
|
||||
val buffer = ByteArray(4096)
|
||||
var bytesRead: Int
|
||||
while (inputStream.read(buffer).also { bytesRead = it } >= 0) {
|
||||
outputStream.write(buffer, 0, bytesRead)
|
||||
}
|
||||
}
|
||||
return true
|
||||
} catch (e: IOException) {
|
||||
Log.e("[File Utils] copyFileTo exception: $e")
|
||||
|
@ -184,9 +193,10 @@ class FileUtils {
|
|||
return false
|
||||
}
|
||||
|
||||
private fun copyToFile(inputStream: InputStream?, destFile: File?): Boolean {
|
||||
private suspend fun copyToFile(inputStream: InputStream?, destFile: File?): Boolean {
|
||||
if (inputStream == null || destFile == null) return false
|
||||
try {
|
||||
withContext(Dispatchers.IO) {
|
||||
FileOutputStream(destFile).use { out ->
|
||||
val buffer = ByteArray(4096)
|
||||
var bytesRead: Int
|
||||
|
@ -194,6 +204,7 @@ class FileUtils {
|
|||
out.write(buffer, 0, bytesRead)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
} catch (e: IOException) {
|
||||
Log.e("[File Utils] copyToFile exception: $e")
|
||||
|
|
Loading…
Reference in a new issue