Downloaded (large?) files are truncated

This commit is contained in:
Valere 2020-06-23 15:21:07 +02:00 committed by Benoit Marty
parent 19f16c9e56
commit 80e8cd4191
5 changed files with 90 additions and 34 deletions

View File

@ -58,6 +58,7 @@ internal class DefaultFileService @Inject constructor(
/**
* Download file in the cache folder, and eventually decrypt it
* TODO implement clear file, to delete "MF"
* TODO looks like files are copied 3 times
*/
override fun downloadFile(downloadMode: FileService.DownloadMode,
id: String,
@ -87,20 +88,30 @@ internal class DefaultFileService @Inject constructor(
return@flatMap Try.Failure(e)
}
var inputStream = response.body?.byteStream()
Timber.v("Response size ${response.body?.contentLength()} - Stream available: ${inputStream?.available()}")
if (!response.isSuccessful || inputStream == null) {
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()}")
if (elementToDecrypt != null) {
Timber.v("## decrypt file")
inputStream = MXEncryptedAttachments.decryptAttachment(inputStream, elementToDecrypt)
?: return@flatMap Try.Failure(IllegalStateException("Decryption error"))
val decryptedStream = MXEncryptedAttachments.decryptAttachment(source.inputStream(), elementToDecrypt)
response.close()
if (decryptedStream == null) {
return@flatMap Try.Failure(IllegalStateException("Decryption error"))
} else {
decryptedStream.use {
writeToFile(decryptedStream, destFile)
}
}
} else {
writeToFile(source.inputStream(), destFile)
response.close()
}
writeToFile(inputStream, destFile)
}
Try.just(copyFile(destFile, downloadMode))

View File

@ -17,10 +17,8 @@
package im.vector.matrix.android.internal.util
import androidx.annotation.WorkerThread
import okio.buffer
import okio.sink
import okio.source
import java.io.File
import java.io.FileOutputStream
import java.io.InputStream
/**
@ -28,9 +26,7 @@ import java.io.InputStream
*/
@WorkerThread
fun writeToFile(inputStream: InputStream, outputFile: File) {
inputStream.source().buffer().use { input ->
outputFile.sink().buffer().use { output ->
output.writeAll(input)
}
FileOutputStream(outputFile).use {
inputStream.copyTo(it)
}
}

View File

@ -19,12 +19,14 @@ package im.vector.riotx.core.files
import android.app.DownloadManager
import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import androidx.annotation.WorkerThread
import arrow.core.Try
import okio.buffer
import okio.sink
import okio.source
import timber.log.Timber
import java.io.File
@ -56,7 +58,7 @@ fun addEntryToDownloadManager(context: Context,
file: File,
mimeType: String,
title: String = file.name,
description: String = file.name) {
description: String = file.name) : Uri? {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val contentValues = ContentValues().apply {
@ -65,17 +67,31 @@ fun addEntryToDownloadManager(context: Context,
put(MediaStore.Downloads.MIME_TYPE, mimeType)
put(MediaStore.Downloads.SIZE, file.length())
}
context.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)?.let { uri ->
context.contentResolver.openOutputStream(uri)?.use { outputStream ->
outputStream.sink().buffer().write(file.inputStream().use { it.readBytes() })
return context.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
?.let { uri ->
Timber.v("## addEntryToDownloadManager(): $uri")
val source = file.inputStream().source().buffer()
context.contentResolver.openOutputStream(uri)?.sink()?.buffer()?.let { sink ->
source.use { input ->
sink.use { output ->
output.writeAll(input)
}
}
}
uri
} ?: run {
Timber.v("## addEntryToDownloadManager(): context.contentResolver.insert failed")
null
}
} else {
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager?
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as? DownloadManager
@Suppress("DEPRECATION")
downloadManager?.addCompletedDownload(title, description, true, mimeType, file.absolutePath, file.length(), true)
return null
}
} catch (e: Exception) {
Timber.e(e, "## addEntryToDownloadManager(): Exception")
}
return null
}

View File

@ -167,6 +167,7 @@ import im.vector.riotx.features.invite.VectorInviteView
import im.vector.riotx.features.media.ImageContentRenderer
import im.vector.riotx.features.media.VideoContentRenderer
import im.vector.riotx.features.notifications.NotificationDrawerManager
import im.vector.riotx.features.notifications.NotificationUtils
import im.vector.riotx.features.permalink.NavigationInterceptor
import im.vector.riotx.features.permalink.PermalinkHandler
import im.vector.riotx.features.reactions.EmojiReactionPickerActivity
@ -209,6 +210,7 @@ class RoomDetailFragment @Inject constructor(
private val eventHtmlRenderer: EventHtmlRenderer,
private val vectorPreferences: VectorPreferences,
private val colorProvider: ColorProvider,
private val notificationUtils: NotificationUtils,
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager) :
VectorBaseFragment(),
TimelineEventController.Callback,
@ -486,8 +488,19 @@ class RoomDetailFragment @Inject constructor(
if (action.throwable != null) {
activity.toast(errorFormatter.toHumanReadable(action.throwable))
} else if (action.file != null) {
activity.toast(getString(R.string.downloaded_file, action.file.path))
addEntryToDownloadManager(activity, action.file, action.mimeType)
addEntryToDownloadManager(activity, action.file, action.mimeType)?.let {
// This is a temporary solution to help users find downloaded files
// there is a better way to do that
// On android Q+ this method returns the file URI, on older
// it returns null, and the download manager handles the notification
notificationUtils.buildDownloadFileNotification(
it,
action.file.name ?: "file",
action.mimeType
).let { notification ->
notificationUtils.showNotificationMessage("DL", action.file.absolutePath.hashCode(), notification)
}
}
}
}

View File

@ -480,6 +480,26 @@ class NotificationUtils @Inject constructor(private val context: Context,
.build()
}
fun buildDownloadFileNotification(uri: Uri, fileName: String, mimeType: String): Notification {
return NotificationCompat.Builder(context, SILENT_NOTIFICATION_CHANNEL_ID)
.setGroup(stringProvider.getString(R.string.app_name))
.setSmallIcon(R.drawable.ic_download)
.setContentText(stringProvider.getString(R.string.downloaded_file, fileName))
.setAutoCancel(true)
.apply {
val intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(uri, mimeType)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
PendingIntent.getActivity(
context, System.currentTimeMillis().toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT
).let {
setContentIntent(it)
}
}
.build()
}
/**
* Build a notification for a Room
*/