mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-16 20:10:04 +03:00
Merge pull request #3081 from Dominaezzz/suspend_functions_11
Convert FileService to suspend functions
This commit is contained in:
commit
0e71dfa8e1
10 changed files with 232 additions and 292 deletions
|
@ -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?,
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Add table
Reference in a new issue