Merge pull request #3081 from Dominaezzz/suspend_functions_11

Convert FileService to suspend functions
This commit is contained in:
Benoit Marty 2021-03-29 14:00:12 +02:00 committed by GitHub
commit 0e71dfa8e1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 232 additions and 292 deletions

View file

@ -17,11 +17,9 @@
package org.matrix.android.sdk.api.session.file
import android.net.Uri
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
import org.matrix.android.sdk.api.session.room.model.message.getFileName
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt
import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt
import java.io.File
@ -41,20 +39,17 @@ interface FileService {
* Download a file.
* Result will be a decrypted file, stored in the cache folder. url parameter will be used to create unique filename to avoid name collision.
*/
fun downloadFile(fileName: String,
suspend fun downloadFile(fileName: String,
mimeType: String?,
url: String?,
elementToDecrypt: ElementToDecrypt?,
callback: MatrixCallback<File>): Cancelable
elementToDecrypt: ElementToDecrypt?): File
fun downloadFile(messageContent: MessageWithAttachmentContent,
callback: MatrixCallback<File>): Cancelable =
suspend fun downloadFile(messageContent: MessageWithAttachmentContent): File =
downloadFile(
fileName = messageContent.getFileName(),
mimeType = messageContent.mimeType,
url = messageContent.getFileUrl(),
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt(),
callback = callback
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt()
)
fun isFileInCache(mxcUrl: String?,

View file

@ -20,26 +20,20 @@ import android.content.Context
import android.net.Uri
import android.webkit.MimeTypeMap
import androidx.core.content.FileProvider
import arrow.core.Try
import kotlinx.coroutines.launch
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.completeWith
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.content.ContentUrlResolver
import org.matrix.android.sdk.api.session.file.FileService
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.NoOpCancellable
import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt
import org.matrix.android.sdk.internal.crypto.attachments.MXEncryptedAttachments
import org.matrix.android.sdk.internal.di.SessionDownloadsDirectory
import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificateWithProgress
import org.matrix.android.sdk.internal.session.download.DownloadProgressInterceptor.Companion.DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
import org.matrix.android.sdk.internal.util.md5
import org.matrix.android.sdk.internal.util.toCancelable
import org.matrix.android.sdk.internal.util.writeToFile
import timber.log.Timber
import java.io.File
@ -53,14 +47,15 @@ internal class DefaultFileService @Inject constructor(
private val contentUrlResolver: ContentUrlResolver,
@UnauthenticatedWithCertificateWithProgress
private val okHttpClient: OkHttpClient,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val taskExecutor: TaskExecutor
private val coroutineDispatchers: MatrixCoroutineDispatchers
) : FileService {
// Legacy folder, will be deleted
private val legacyFolder = File(sessionCacheDirectory, "MF")
// Folder to store downloaded files (not decrypted)
private val downloadFolder = File(sessionCacheDirectory, "F")
// Folder to store decrypted files
private val decryptedFolder = File(downloadFolder, "D")
@ -73,134 +68,113 @@ internal class DefaultFileService @Inject constructor(
* Retain ongoing downloads to avoid re-downloading and already downloading file
* map of mxCurl to callbacks
*/
private val ongoing = mutableMapOf<String, ArrayList<MatrixCallback<File>>>()
private val ongoing = mutableMapOf<String, CompletableDeferred<File>>()
/**
* Download file in the cache folder, and eventually decrypt it
* TODO looks like files are copied 3 times
*/
override fun downloadFile(fileName: String,
mimeType: String?,
url: String?,
elementToDecrypt: ElementToDecrypt?,
callback: MatrixCallback<File>): Cancelable {
url ?: return NoOpCancellable.also {
callback.onFailure(IllegalArgumentException("url is null"))
}
override suspend fun downloadFile(fileName: String,
mimeType: String?,
url: String?,
elementToDecrypt: ElementToDecrypt?): File {
url ?: throw IllegalArgumentException("url is null")
Timber.v("## FileService downloadFile $url")
synchronized(ongoing) {
// TODO: Remove use of `synchronized` in suspend function.
val existingDownload = synchronized(ongoing) {
val existing = ongoing[url]
if (existing != null) {
Timber.v("## FileService downloadFile is already downloading.. ")
existing.add(callback)
return NoOpCancellable
existing
} else {
// mark as tracked
ongoing[url] = ArrayList()
ongoing[url] = CompletableDeferred()
// and proceed to download
null
}
}
return taskExecutor.executorScope.launch(coroutineDispatchers.main) {
withContext(coroutineDispatchers.io) {
Try {
if (!decryptedFolder.exists()) {
decryptedFolder.mkdirs()
}
// ensure we use unique file name by using URL (mapped to suitable file name)
// Also we need to add extension for the FileProvider, if not it lot's of app that it's
// shared with will not function well (even if mime type is passed in the intent)
getFiles(url, fileName, mimeType, elementToDecrypt != null)
}.flatMap { cachedFiles ->
if (!cachedFiles.file.exists()) {
val resolvedUrl = contentUrlResolver.resolveFullSize(url) ?: return@flatMap Try.Failure(IllegalArgumentException("url is null"))
if (existingDownload != null) {
// FIXME If the first downloader cancels then we'll unfortunately be cancelled too.
return existingDownload.await()
}
val request = Request.Builder()
.url(resolvedUrl)
.header(DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER, url)
.build()
val response = try {
okHttpClient.newCall(request).execute()
} catch (e: Throwable) {
return@flatMap Try.Failure(e)
}
if (!response.isSuccessful) {
return@flatMap Try.Failure(IOException())
}
val source = response.body?.source()
?: return@flatMap Try.Failure(IOException())
Timber.v("Response size ${response.body?.contentLength()} - Stream available: ${!source.exhausted()}")
// Write the file to cache (encrypted version if the file is encrypted)
writeToFile(source.inputStream(), cachedFiles.file)
response.close()
} else {
Timber.v("## FileService: cache hit for $url")
}
Try.just(cachedFiles)
val result = runCatching {
val cachedFiles = withContext(coroutineDispatchers.io) {
if (!decryptedFolder.exists()) {
decryptedFolder.mkdirs()
}
}.flatMap { cachedFiles ->
// Decrypt if necessary
if (cachedFiles.decryptedFile != null) {
if (!cachedFiles.decryptedFile.exists()) {
Timber.v("## FileService: decrypt file")
// Ensure the parent folder exists
cachedFiles.decryptedFile.parentFile?.mkdirs()
val decryptSuccess = cachedFiles.file.inputStream().use { inputStream ->
cachedFiles.decryptedFile.outputStream().buffered().use { outputStream ->
MXEncryptedAttachments.decryptAttachment(
inputStream,
elementToDecrypt,
outputStream
)
}
}
if (!decryptSuccess) {
return@flatMap Try.Failure(IllegalStateException("Decryption error"))
}
} else {
Timber.v("## FileService: cache hit for decrypted file")
// ensure we use unique file name by using URL (mapped to suitable file name)
// Also we need to add extension for the FileProvider, if not it lot's of app that it's
// shared with will not function well (even if mime type is passed in the intent)
val cachedFiles = getFiles(url, fileName, mimeType, elementToDecrypt != null)
if (!cachedFiles.file.exists()) {
val resolvedUrl = contentUrlResolver.resolveFullSize(url) ?: throw IllegalArgumentException("url is null")
val request = Request.Builder()
.url(resolvedUrl)
.header(DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER, url)
.build()
val response = okHttpClient.newCall(request).execute()
if (!response.isSuccessful) {
throw IOException()
}
Try.just(cachedFiles.decryptedFile)
val source = response.body?.source() ?: throw IOException()
Timber.v("Response size ${response.body?.contentLength()} - Stream available: ${!source.exhausted()}")
// Write the file to cache (encrypted version if the file is encrypted)
writeToFile(source.inputStream(), cachedFiles.file)
response.close()
} else {
// Clear file
Try.just(cachedFiles.file)
Timber.v("## FileService: cache hit for $url")
}
}.fold(
{ throwable ->
callback.onFailure(throwable)
// notify concurrent requests
val toNotify = synchronized(ongoing) {
ongoing[url]?.also {
ongoing.remove(url)
}
}
toNotify?.forEach { otherCallbacks ->
tryOrNull { otherCallbacks.onFailure(throwable) }
}
},
{ file ->
callback.onSuccess(file)
// notify concurrent requests
val toNotify = synchronized(ongoing) {
ongoing[url]?.also {
ongoing.remove(url)
}
}
Timber.v("## FileService additional to notify ${toNotify?.size ?: 0} ")
toNotify?.forEach { otherCallbacks ->
tryOrNull { otherCallbacks.onSuccess(file) }
cachedFiles
}
// Decrypt if necessary
if (cachedFiles.decryptedFile != null) {
if (!cachedFiles.decryptedFile.exists()) {
Timber.v("## FileService: decrypt file")
// Ensure the parent folder exists
cachedFiles.decryptedFile.parentFile?.mkdirs()
val decryptSuccess = cachedFiles.file.inputStream().use { inputStream ->
cachedFiles.decryptedFile.outputStream().buffered().use { outputStream ->
MXEncryptedAttachments.decryptAttachment(
inputStream,
elementToDecrypt,
outputStream
)
}
}
)
}.toCancelable()
if (!decryptSuccess) {
throw IllegalStateException("Decryption error")
}
} else {
Timber.v("## FileService: cache hit for decrypted file")
}
cachedFiles.decryptedFile
} else {
// Clear file
cachedFiles.file
}
}
// notify concurrent requests
val toNotify = synchronized(ongoing) { ongoing.remove(url) }
result.onSuccess {
Timber.v("## FileService additional to notify is > 0 ")
}
toNotify?.completeWith(result)
return result.getOrThrow()
}
fun storeDataFor(mxcUrl: String,
@ -325,6 +299,7 @@ internal class DefaultFileService @Inject constructor(
companion object {
private const val ENCRYPTED_FILENAME = "encrypted.bin"
// The extension would be added from the mimetype
private const val DEFAULT_FILENAME = "file"
}

View file

@ -28,10 +28,10 @@ import com.bumptech.glide.signature.ObjectKey
import im.vector.app.core.extensions.vectorComponent
import im.vector.app.core.files.LocalFilesHelper
import im.vector.app.features.media.ImageContentRenderer
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import org.matrix.android.sdk.api.MatrixCallback
import timber.log.Timber
import java.io.File
import java.io.IOException
import java.io.InputStream
@ -113,21 +113,19 @@ class VectorGlideDataFetcher(context: Context,
callback.onLoadFailed(IllegalArgumentException("No File service"))
}
// Use the file vector service, will avoid flickering and redownload after upload
fileService.downloadFile(
fileName = data.filename,
mimeType = data.mimeType,
url = data.url,
elementToDecrypt = data.elementToDecrypt,
callback = object : MatrixCallback<File> {
override fun onSuccess(data: File) {
callback.onDataReady(data.inputStream())
}
override fun onFailure(failure: Throwable) {
callback.onLoadFailed(failure as? Exception ?: IOException(failure.localizedMessage))
}
}
)
GlobalScope.launch {
val result = runCatching {
fileService.downloadFile(
fileName = data.filename,
mimeType = data.mimeType,
url = data.url,
elementToDecrypt = data.elementToDecrypt)
}
result.fold(
{ callback.onDataReady(it.inputStream()) },
{ callback.onLoadFailed(it as? Exception ?: IOException(it.localizedMessage)) }
)
}
// val url = contentUrlResolver.resolveFullSize(data.url)
// ?: return
//

View file

@ -170,12 +170,12 @@ import im.vector.app.features.widgets.WidgetKind
import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
import nl.dionsegijn.konfetti.models.Shape
import nl.dionsegijn.konfetti.models.Size
import org.billcarsonfr.jsonviewer.JSonViewerDialog
import org.commonmark.parser.Parser
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import org.matrix.android.sdk.api.session.events.model.Event
@ -201,7 +201,6 @@ import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
import timber.log.Timber
import java.io.File
import java.net.URL
import java.util.UUID
import java.util.concurrent.TimeUnit
@ -1676,16 +1675,12 @@ class RoomDetailFragment @Inject constructor(
if (action.messageContent is MessageTextContent) {
shareText(requireContext(), action.messageContent.body)
} else if (action.messageContent is MessageWithAttachmentContent) {
session.fileService().downloadFile(
messageContent = action.messageContent,
callback = object : MatrixCallback<File> {
override fun onSuccess(data: File) {
if (isAdded) {
shareMedia(requireContext(), data, getMimeTypeFromUri(requireContext(), data.toUri()))
}
}
}
)
lifecycleScope.launch {
val data = session.fileService().downloadFile(messageContent = action.messageContent)
if (isAdded) {
shareMedia(requireContext(), data, getMimeTypeFromUri(requireContext(), data.toUri()))
}
}
}
}
@ -1706,22 +1701,18 @@ class RoomDetailFragment @Inject constructor(
sharedActionViewModel.pendingAction = action
return
}
session.fileService().downloadFile(
messageContent = action.messageContent,
callback = object : MatrixCallback<File> {
override fun onSuccess(data: File) {
if (isAdded) {
saveMedia(
context = requireContext(),
file = data,
title = action.messageContent.body,
mediaMimeType = action.messageContent.mimeType ?: getMimeTypeFromUri(requireContext(), data.toUri()),
notificationUtils = notificationUtils
)
}
}
}
)
lifecycleScope.launch {
val data = session.fileService().downloadFile(messageContent = action.messageContent)
if (isAdded) {
saveMedia(
context = requireContext(),
file = data,
title = action.messageContent.body,
mediaMimeType = action.messageContent.mimeType ?: getMimeTypeFromUri(requireContext(), data.toUri()),
notificationUtils = notificationUtils
)
}
}
}
private fun handleActions(action: EventSharedAction) {

View file

@ -106,7 +106,6 @@ import org.matrix.android.sdk.internal.util.awaitCallback
import org.matrix.android.sdk.rx.rx
import org.matrix.android.sdk.rx.unwrap
import timber.log.Timber
import java.io.File
import java.util.UUID
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
@ -1140,25 +1139,17 @@ class RoomDetailViewModel @AssistedInject constructor(
))
}
} else {
session.fileService().downloadFile(
messageContent = action.messageFileContent,
callback = object : MatrixCallback<File> {
override fun onSuccess(data: File) {
_viewEvents.post(RoomDetailViewEvents.DownloadFileState(
action.messageFileContent.mimeType,
data,
null
))
}
viewModelScope.launch {
val result = runCatching {
session.fileService().downloadFile(messageContent = action.messageFileContent)
}
override fun onFailure(failure: Throwable) {
_viewEvents.post(RoomDetailViewEvents.DownloadFileState(
action.messageFileContent.mimeType,
null,
failure
))
}
})
_viewEvents.post(RoomDetailViewEvents.DownloadFileState(
action.messageFileContent.mimeType,
result.getOrNull(),
result.exceptionOrNull()
))
}
}
}

View file

@ -31,7 +31,8 @@ import im.vector.lib.attachmentviewer.AttachmentInfo
import im.vector.lib.attachmentviewer.AttachmentSourceProvider
import im.vector.lib.attachmentviewer.ImageLoaderTarget
import im.vector.lib.attachmentviewer.VideoLoaderTarget
import org.matrix.android.sdk.api.MatrixCallback
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.events.model.isVideoMessage
import org.matrix.android.sdk.api.session.file.FileService
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
@ -152,21 +153,20 @@ abstract class BaseAttachmentProvider<Type>(
target.onVideoURLReady(info.uid, data.url)
} else {
target.onVideoFileLoading(info.uid)
fileService.downloadFile(
fileName = data.filename,
mimeType = data.mimeType,
url = data.url,
elementToDecrypt = data.elementToDecrypt,
callback = object : MatrixCallback<File> {
override fun onSuccess(data: File) {
target.onVideoFileReady(info.uid, data)
}
override fun onFailure(failure: Throwable) {
target.onVideoFileLoadFailed(info.uid)
}
}
)
GlobalScope.launch {
val result = runCatching {
fileService.downloadFile(
fileName = data.filename,
mimeType = data.mimeType,
url = data.url,
elementToDecrypt = data.elementToDecrypt
)
}
result.fold(
{ target.onVideoFileReady(info.uid, it) },
{ target.onVideoFileLoadFailed(info.uid) }
)
}
}
}

View file

@ -19,7 +19,8 @@ package im.vector.app.features.media
import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.resources.StringProvider
import im.vector.lib.attachmentviewer.AttachmentInfo
import org.matrix.android.sdk.api.MatrixCallback
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.file.FileService
import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
@ -77,20 +78,16 @@ class DataAttachmentRoomProvider(
override fun getFileForSharing(position: Int, callback: (File?) -> Unit) {
val item = getItem(position)
fileService.downloadFile(
fileName = item.filename,
mimeType = item.mimeType,
url = item.url,
elementToDecrypt = item.elementToDecrypt,
callback = object : MatrixCallback<File> {
override fun onSuccess(data: File) {
callback(data)
}
override fun onFailure(failure: Throwable) {
callback(null)
}
}
)
GlobalScope.launch {
val result = runCatching {
fileService.downloadFile(
fileName = item.filename,
mimeType = item.mimeType,
url = item.url,
elementToDecrypt = item.elementToDecrypt
)
}
callback(result.getOrNull())
}
}
}

View file

@ -19,7 +19,8 @@ package im.vector.app.features.media
import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.resources.StringProvider
import im.vector.lib.attachmentviewer.AttachmentInfo
import org.matrix.android.sdk.api.MatrixCallback
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.file.FileService
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
@ -125,21 +126,16 @@ class RoomEventsAttachmentProvider(
val messageContent = timelineEvent.root.getClearContent().toModel<MessageContent>()
as? MessageWithAttachmentContent
?: return@let
fileService.downloadFile(
fileName = messageContent.body,
mimeType = messageContent.mimeType,
url = messageContent.getFileUrl(),
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt(),
callback = object : MatrixCallback<File> {
override fun onSuccess(data: File) {
callback(data)
}
override fun onFailure(failure: Throwable) {
callback(null)
}
}
)
GlobalScope.launch {
val result = runCatching {
fileService.downloadFile(
fileName = messageContent.body,
mimeType = messageContent.mimeType,
url = messageContent.getFileUrl(),
elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt())
}
callback(result.getOrNull())
}
}
}
}

View file

@ -25,11 +25,11 @@ import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.files.LocalFilesHelper
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt
import timber.log.Timber
import java.io.File
import java.net.URLEncoder
import javax.inject.Inject
@ -74,28 +74,31 @@ class VideoContentRenderer @Inject constructor(private val localFilesHelper: Loc
thumbnailView.isVisible = true
loadingView.isVisible = true
activeSessionHolder.getActiveSession().fileService()
.downloadFile(
fileName = data.filename,
mimeType = data.mimeType,
url = data.url,
elementToDecrypt = data.elementToDecrypt,
callback = object : MatrixCallback<File> {
override fun onSuccess(data: File) {
thumbnailView.isVisible = false
loadingView.isVisible = false
videoView.isVisible = true
GlobalScope.launch {
val result = runCatching {
activeSessionHolder.getActiveSession().fileService()
.downloadFile(
fileName = data.filename,
mimeType = data.mimeType,
url = data.url,
elementToDecrypt = data.elementToDecrypt)
}
result.fold(
{ data ->
thumbnailView.isVisible = false
loadingView.isVisible = false
videoView.isVisible = true
videoView.setVideoPath(data.path)
videoView.start()
}
override fun onFailure(failure: Throwable) {
loadingView.isVisible = false
errorView.isVisible = true
errorView.text = errorFormatter.toHumanReadable(failure)
}
})
videoView.setVideoPath(data.path)
videoView.start()
},
{
loadingView.isVisible = false
errorView.isVisible = true
errorView.text = errorFormatter.toHumanReadable(it)
}
)
}
}
} else {
val resolvedUrl = contentUrlResolver.resolveFullSize(data.url)
@ -112,28 +115,31 @@ class VideoContentRenderer @Inject constructor(private val localFilesHelper: Loc
thumbnailView.isVisible = true
loadingView.isVisible = true
activeSessionHolder.getActiveSession().fileService()
.downloadFile(
fileName = data.filename,
mimeType = data.mimeType,
url = data.url,
elementToDecrypt = null,
callback = object : MatrixCallback<File> {
override fun onSuccess(data: File) {
thumbnailView.isVisible = false
loadingView.isVisible = false
videoView.isVisible = true
GlobalScope.launch {
val result = runCatching {
activeSessionHolder.getActiveSession().fileService()
.downloadFile(
fileName = data.filename,
mimeType = data.mimeType,
url = data.url,
elementToDecrypt = null)
}
result.fold(
{ data ->
thumbnailView.isVisible = false
loadingView.isVisible = false
videoView.isVisible = true
videoView.setVideoPath(data.path)
videoView.start()
}
override fun onFailure(failure: Throwable) {
loadingView.isVisible = false
errorView.isVisible = true
errorView.text = errorFormatter.toHumanReadable(failure)
}
})
videoView.setVideoPath(data.path)
videoView.start()
},
{
loadingView.isVisible = false
errorView.isVisible = true
errorView.text = errorFormatter.toHumanReadable(it)
}
)
}
}
}
}

View file

@ -32,10 +32,8 @@ import im.vector.app.core.platform.VectorViewModel
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.model.message.MessageType
import org.matrix.android.sdk.internal.util.awaitCallback
import org.matrix.android.sdk.rx.rx
import org.matrix.android.sdk.rx.unwrap
import java.io.File
class RoomUploadsViewModel @AssistedInject constructor(
@Assisted initialState: RoomUploadsViewState,
@ -130,12 +128,8 @@ class RoomUploadsViewModel @AssistedInject constructor(
private fun handleShare(action: RoomUploadsAction.Share) {
viewModelScope.launch {
try {
val file = awaitCallback<File> {
session.fileService().downloadFile(
messageContent = action.uploadEvent.contentWithAttachmentContent,
callback = it
)
}
val file = session.fileService().downloadFile(
messageContent = action.uploadEvent.contentWithAttachmentContent)
_viewEvents.post(RoomUploadsViewEvents.FileReadyForSharing(file))
} catch (failure: Throwable) {
_viewEvents.post(RoomUploadsViewEvents.Failure(failure))
@ -146,11 +140,8 @@ class RoomUploadsViewModel @AssistedInject constructor(
private fun handleDownload(action: RoomUploadsAction.Download) {
viewModelScope.launch {
try {
val file = awaitCallback<File> {
session.fileService().downloadFile(
messageContent = action.uploadEvent.contentWithAttachmentContent,
callback = it)
}
val file = session.fileService().downloadFile(
messageContent = action.uploadEvent.contentWithAttachmentContent)
_viewEvents.post(RoomUploadsViewEvents.FileReadyForSaving(file, action.uploadEvent.contentWithAttachmentContent.body))
} catch (failure: Throwable) {
_viewEvents.post(RoomUploadsViewEvents.Failure(failure))