From f3bc39a0c57c7f28d71313f6fa26a8e24164de49 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 8 Dec 2020 11:14:55 +0100
Subject: [PATCH 01/20] Cleanup

---
 .../android/sdk/internal/session/sync/job/SyncThread.kt     | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt
index 74cba5e796..424c24663c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt
@@ -25,7 +25,6 @@ import org.matrix.android.sdk.api.failure.isTokenError
 import org.matrix.android.sdk.api.session.sync.SyncState
 import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker
 import org.matrix.android.sdk.internal.session.sync.SyncTask
-import org.matrix.android.sdk.internal.session.typing.DefaultTypingUsersTracker
 import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
 import org.matrix.android.sdk.internal.util.Debouncer
 import org.matrix.android.sdk.internal.util.createUIHandler
@@ -50,14 +49,13 @@ private const val RETRY_WAIT_TIME_MS = 10_000L
 private const val DEFAULT_LONG_POOL_TIMEOUT = 30_000L
 
 internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
-                                              private val typingUsersTracker: DefaultTypingUsersTracker,
                                               private val networkConnectivityChecker: NetworkConnectivityChecker,
                                               private val backgroundDetectionObserver: BackgroundDetectionObserver,
                                               private val activeCallHandler: ActiveCallHandler
 ) : Thread("SyncThread"), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
 
     private var state: SyncState = SyncState.Idle
-    private var liveState = MutableLiveData<SyncState>(state)
+    private var liveState = MutableLiveData(state)
     private val lock = Object()
     private val syncScope = CoroutineScope(SupervisorJob())
     private val debouncer = Debouncer(createUIHandler())
@@ -231,7 +229,7 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
             return
         }
         state = newState
-        debouncer.debounce("post_state", Runnable {
+        debouncer.debounce("post_state", {
             liveState.value = newState
         }, 150)
     }

From 28bfea6af0f611ab40981b0b2b8181da21108556 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 8 Dec 2020 11:31:16 +0100
Subject: [PATCH 02/20] This code is for debug build (see the path), so no need
 to check again

---
 .../interceptors/FormattedJsonHttpLogger.kt   | 41 +++++++++----------
 1 file changed, 19 insertions(+), 22 deletions(-)

diff --git a/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt
index 630f6f1e29..849a464867 100644
--- a/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt
+++ b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt
@@ -38,31 +38,28 @@ class FormattedJsonHttpLogger : HttpLoggingInterceptor.Logger {
      */
     @Synchronized
     override fun log(@NonNull message: String) {
-        // In RELEASE there is no log, but for sure, test again BuildConfig.DEBUG
-        if (BuildConfig.DEBUG) {
-            Timber.v(message)
+        Timber.v(message)
 
-            if (message.startsWith("{")) {
-                // JSON Detected
-                try {
-                    val o = JSONObject(message)
-                    logJson(o.toString(INDENT_SPACE))
-                } catch (e: JSONException) {
-                    // Finally this is not a JSON string...
-                    Timber.e(e)
-                }
-            } else if (message.startsWith("[")) {
-                // JSON Array detected
-                try {
-                    val o = JSONArray(message)
-                    logJson(o.toString(INDENT_SPACE))
-                } catch (e: JSONException) {
-                    // Finally not JSON...
-                    Timber.e(e)
-                }
+        if (message.startsWith("{")) {
+            // JSON Detected
+            try {
+                val o = JSONObject(message)
+                logJson(o.toString(INDENT_SPACE))
+            } catch (e: JSONException) {
+                // Finally this is not a JSON string...
+                Timber.e(e)
+            }
+        } else if (message.startsWith("[")) {
+            // JSON Array detected
+            try {
+                val o = JSONArray(message)
+                logJson(o.toString(INDENT_SPACE))
+            } catch (e: JSONException) {
+                // Finally not JSON...
+                Timber.e(e)
             }
-            // Else not a json string to log
         }
+        // Else not a json string to log
     }
 
     private fun logJson(formattedJson: String) {

From a0c8a8e97ce5e7739f9881b9e7bfadcd7663dc5b Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 8 Dec 2020 11:36:12 +0100
Subject: [PATCH 03/20] Log HTTP requests and responses in production (level
 BASIC, i.e. without any private data)

---
 CHANGES.md                      | 1 +
 gradle.properties               | 2 +-
 matrix-sdk-android/build.gradle | 2 +-
 3 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 1005040328..12cfd8ef83 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -29,6 +29,7 @@ Test:
 
 Other changes:
  - Remove "Status.im" theme #2424
+ - Log HTTP requests and responses in production (level BASIC, i.e. without any private data)
 
 Changes in Element 1.0.11 (2020-11-27)
 ===================================================
diff --git a/gradle.properties b/gradle.properties
index b3f11e08a3..200866be25 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -18,7 +18,7 @@ org.gradle.jvmargs=-Xmx2048m
 org.gradle.vfs.watch=true
 
 vector.debugPrivateData=false
-vector.httpLogLevel=NONE
+vector.httpLogLevel=BASIC
 
 # Note: to debug, you can put and uncomment the following lines in the file ~/.gradle/gradle.properties to override the value above
 #vector.debugPrivateData=true
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index d961560c17..7f0d5c1bbf 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -63,7 +63,7 @@ android {
 
         release {
             buildConfigField "boolean", "LOG_PRIVATE_DATA", "false"
-            buildConfigField "okhttp3.logging.HttpLoggingInterceptor.Level", "OKHTTP_LOGGING_LEVEL", "okhttp3.logging.HttpLoggingInterceptor.Level.NONE"
+            buildConfigField "okhttp3.logging.HttpLoggingInterceptor.Level", "OKHTTP_LOGGING_LEVEL", "okhttp3.logging.HttpLoggingInterceptor.Level.BASIC"
         }
     }
 

From b43f3b3b6aec9413b0f0c165dad79d0ee330023a Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 8 Dec 2020 11:57:18 +0100
Subject: [PATCH 04/20] Log some details about the request which has failed

---
 .../org/matrix/android/sdk/internal/network/Request.kt | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt
index e6cec7f7ac..2535a5347a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt
@@ -16,14 +16,15 @@
 
 package org.matrix.android.sdk.internal.network
 
-import org.matrix.android.sdk.api.failure.Failure
-import org.matrix.android.sdk.api.failure.shouldBeRetried
-import org.matrix.android.sdk.internal.network.ssl.CertUtil
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.delay
 import org.greenrobot.eventbus.EventBus
+import org.matrix.android.sdk.api.failure.Failure
+import org.matrix.android.sdk.api.failure.shouldBeRetried
+import org.matrix.android.sdk.internal.network.ssl.CertUtil
 import retrofit2.Call
 import retrofit2.awaitResponse
+import timber.log.Timber
 import java.io.IOException
 
 internal suspend inline fun <DATA : Any> executeRequest(eventBus: EventBus?,
@@ -49,6 +50,9 @@ internal class Request<DATA : Any>(private val eventBus: EventBus?) {
                 throw response.toFailure(eventBus)
             }
         } catch (exception: Throwable) {
+            // Log some details about the request which has failed
+            Timber.e("Exception when executing request ${apiCall.request().method} ${apiCall.request().url.toString().substringBefore("?")}")
+
             // Check if this is a certificateException
             CertUtil.getCertificateException(exception)
                     // TODO Support certificate error once logged

From dda2685bd8693d5317866c69d9c21e7c76c85b35 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 8 Dec 2020 13:33:01 +0100
Subject: [PATCH 05/20] Upgrade Realm dependency to 10.1.2

---
 CHANGES.md                      | 1 +
 matrix-sdk-android/build.gradle | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGES.md b/CHANGES.md
index 12cfd8ef83..b0be0a1145 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -23,6 +23,7 @@ SDK API changes ⚠️:
 Build 🧱:
  - Upgrade some dependencies and Kotlin version
  - Use fragment-ktx and preference-ktx dependencies (fix lint issue KtxExtensionAvailable)
+ - Upgrade Realm dependency to 10.1.2
 
 Test:
  -
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index 7f0d5c1bbf..519b8439c9 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -9,7 +9,7 @@ buildscript {
         jcenter()
     }
     dependencies {
-        classpath "io.realm:realm-gradle-plugin:10.0.0"
+        classpath "io.realm:realm-gradle-plugin:10.1.2"
     }
 }
 

From 7152dead1da6272836601e18986b941fb985cf02 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 8 Dec 2020 16:47:29 +0100
Subject: [PATCH 06/20] Rename method

---
 .../org/matrix/android/sdk/internal/session/SessionModule.kt  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
index 32949d60c4..f3a9fc59e3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
@@ -169,8 +169,8 @@ internal abstract class SessionModule {
         @JvmStatic
         @Provides
         @SessionDownloadsDirectory
-        fun providesCacheDir(@SessionId sessionId: String,
-                             context: Context): File {
+        fun providesDownloadsCacheDir(@SessionId sessionId: String,
+                                      context: Context): File {
             return File(context.cacheDir, "downloads/$sessionId")
         }
 

From 24a9ddaa5e3aad9f42550948c87b1e78d85eee04 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 8 Dec 2020 17:20:40 +0100
Subject: [PATCH 07/20] FileService: remove useless FileService.DownloadMode

---
 CHANGES.md                                    |  2 +-
 .../sdk/api/session/file/FileService.kt       | 18 ---------------
 .../sdk/internal/di/MatrixComponent.kt        |  3 ---
 .../android/sdk/internal/di/MatrixModule.kt   |  7 ------
 .../internal/session/DefaultFileService.kt    | 23 ++-----------------
 .../sdk/internal/session/SessionModule.kt     |  5 ++--
 .../app/core/glide/VectorGlideModelLoader.kt  |  2 --
 .../home/room/detail/RoomDetailFragment.kt    |  3 ---
 .../home/room/detail/RoomDetailViewModel.kt   |  2 --
 .../features/media/BaseAttachmentProvider.kt  |  1 -
 .../media/DataAttachmentRoomProvider.kt       |  1 -
 .../media/RoomEventsAttachmentProvider.kt     |  1 -
 .../features/media/VideoContentRenderer.kt    |  3 ---
 .../uploads/RoomUploadsViewModel.kt           |  3 ---
 14 files changed, 6 insertions(+), 68 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index b0be0a1145..7ba7b7cb76 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -18,7 +18,7 @@ Translations 🗣:
  -
 
 SDK API changes ⚠️:
- -
+ - FileService: remove useless FileService.DownloadMode
 
 Build 🧱:
  - Upgrade some dependencies and Kotlin version
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
index 31f016be14..dd592d84a3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
@@ -27,23 +27,6 @@ import java.io.File
  */
 interface FileService {
 
-    enum class DownloadMode {
-        /**
-         * Download file in external storage
-         */
-        TO_EXPORT,
-
-        /**
-         * Download file in cache
-         */
-        FOR_INTERNAL_USE,
-
-        /**
-         * Download file in file provider path
-         */
-        FOR_EXTERNAL_SHARE
-    }
-
     enum class FileState {
         IN_CACHE,
         DOWNLOADING,
@@ -55,7 +38,6 @@ interface FileService {
      * 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(
-            downloadMode: DownloadMode,
             id: String,
             fileName: String,
             mimeType: String?,
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt
index d3f08fde36..f959104e11 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixComponent.kt
@@ -71,9 +71,6 @@ internal interface MatrixComponent {
     @CacheDirectory
     fun cacheDir(): File
 
-    @ExternalFilesDirectory
-    fun externalFilesDir(): File?
-
     fun olmManager(): OlmManager
 
     fun taskExecutor(): TaskExecutor
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt
index 71cbd8f1a1..b58fb3e683 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt
@@ -57,13 +57,6 @@ internal object MatrixModule {
         return context.cacheDir
     }
 
-    @JvmStatic
-    @Provides
-    @ExternalFilesDirectory
-    fun providesExternalFilesDir(context: Context): File? {
-        return context.getExternalFilesDir(null)
-    }
-
     @JvmStatic
     @Provides
     @MatrixScope
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
index 861ae7c7ee..d71c3262c2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
@@ -29,8 +29,6 @@ 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.CacheDirectory
-import org.matrix.android.sdk.internal.di.ExternalFilesDirectory
 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
@@ -54,10 +52,6 @@ import javax.inject.Inject
 
 internal class DefaultFileService @Inject constructor(
         private val context: Context,
-        @CacheDirectory
-        private val cacheDirectory: File,
-        @ExternalFilesDirectory
-        private val externalFilesDirectory: File?,
         @SessionDownloadsDirectory
         private val sessionCacheDirectory: File,
         private val contentUrlResolver: ContentUrlResolver,
@@ -81,8 +75,7 @@ internal class DefaultFileService @Inject constructor(
      * Download file in the cache folder, and eventually decrypt it
      * TODO looks like files are copied 3 times
      */
-    override fun downloadFile(downloadMode: FileService.DownloadMode,
-                              id: String,
+    override fun downloadFile(id: String,
                               fileName: String,
                               mimeType: String?,
                               url: String?,
@@ -162,7 +155,7 @@ internal class DefaultFileService @Inject constructor(
                         Timber.v("## FileService: cache hit for $url")
                     }
 
-                    Try.just(copyFile(destFile, downloadMode))
+                    Try.just(destFile)
                 }
             }.fold({
                 callback.onFailure(it)
@@ -232,18 +225,6 @@ internal class DefaultFileService @Inject constructor(
         return FileProvider.getUriForFile(context, authority, targetFile)
     }
 
-    private fun copyFile(file: File, downloadMode: FileService.DownloadMode): File {
-        // TODO some of this seems outdated, will need to be re-worked
-        return when (downloadMode) {
-            FileService.DownloadMode.TO_EXPORT          ->
-                file.copyTo(File(externalFilesDirectory, file.name), true)
-            FileService.DownloadMode.FOR_EXTERNAL_SHARE ->
-                file.copyTo(File(File(cacheDirectory, "ext_share"), file.name), true)
-            FileService.DownloadMode.FOR_INTERNAL_USE   ->
-                file
-        }
-    }
-
     override fun getCacheSize(): Int {
         return downloadFolder.walkTopDown()
                 .onEnter {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
index f3a9fc59e3..96b44917bd 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
@@ -50,6 +50,7 @@ import org.matrix.android.sdk.internal.database.EventInsertLiveObserver
 import org.matrix.android.sdk.internal.database.RealmSessionProvider
 import org.matrix.android.sdk.internal.database.SessionRealmConfigurationFactory
 import org.matrix.android.sdk.internal.di.Authenticated
+import org.matrix.android.sdk.internal.di.CacheDirectory
 import org.matrix.android.sdk.internal.di.DeviceId
 import org.matrix.android.sdk.internal.di.SessionDatabase
 import org.matrix.android.sdk.internal.di.SessionDownloadsDirectory
@@ -170,8 +171,8 @@ internal abstract class SessionModule {
         @Provides
         @SessionDownloadsDirectory
         fun providesDownloadsCacheDir(@SessionId sessionId: String,
-                                      context: Context): File {
-            return File(context.cacheDir, "downloads/$sessionId")
+                                      @CacheDirectory cacheFile: File): File {
+            return File(cacheFile, "downloads/$sessionId")
         }
 
         @JvmStatic
diff --git a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt
index 71bd3ccc05..cf40926ba4 100644
--- a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt
+++ b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt
@@ -28,7 +28,6 @@ import im.vector.app.core.di.ActiveSessionHolder
 import im.vector.app.features.media.ImageContentRenderer
 import okhttp3.OkHttpClient
 import org.matrix.android.sdk.api.MatrixCallback
-import org.matrix.android.sdk.api.session.file.FileService
 import timber.log.Timber
 import java.io.File
 import java.io.IOException
@@ -110,7 +109,6 @@ class VectorGlideDataFetcher(private val activeSessionHolder: ActiveSessionHolde
         }
         // Use the file vector service, will avoid flickering and redownload after upload
         fileService.downloadFile(
-                downloadMode = FileService.DownloadMode.FOR_INTERNAL_USE,
                 mimeType = data.mimeType,
                 id = data.eventId,
                 url = data.url,
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
index 3f5e476a5e..bbce180e80 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
@@ -174,7 +174,6 @@ 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
 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.Membership
 import org.matrix.android.sdk.api.session.room.model.RoomSummary
 import org.matrix.android.sdk.api.session.room.model.message.MessageContent
@@ -1657,7 +1656,6 @@ class RoomDetailFragment @Inject constructor(
             shareText(requireContext(), action.messageContent.body)
         } else if (action.messageContent is MessageWithAttachmentContent) {
             session.fileService().downloadFile(
-                    downloadMode = FileService.DownloadMode.FOR_EXTERNAL_SHARE,
                     id = action.eventId,
                     fileName = action.messageContent.body,
                     mimeType = action.messageContent.mimeType,
@@ -1692,7 +1690,6 @@ class RoomDetailFragment @Inject constructor(
             return
         }
         session.fileService().downloadFile(
-                downloadMode = FileService.DownloadMode.FOR_EXTERNAL_SHARE,
                 id = action.eventId,
                 fileName = action.messageContent.body,
                 mimeType = action.messageContent.mimeType,
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
index a83dddc9ac..a13ee3be62 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
@@ -69,7 +69,6 @@ import org.matrix.android.sdk.api.session.events.model.isAttachmentMessage
 import org.matrix.android.sdk.api.session.events.model.isTextMessage
 import org.matrix.android.sdk.api.session.events.model.toContent
 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.homeserver.HomeServerCapabilities
 import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
 import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams
@@ -1033,7 +1032,6 @@ class RoomDetailViewModel @AssistedInject constructor(
             }
         } else {
             session.fileService().downloadFile(
-                    downloadMode = FileService.DownloadMode.FOR_INTERNAL_USE,
                     id = action.eventId,
                     fileName = action.messageFileContent.getFileName(),
                     mimeType = action.messageFileContent.mimeType,
diff --git a/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt b/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt
index e23b905919..5f61ca36e4 100644
--- a/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt
+++ b/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt
@@ -153,7 +153,6 @@ abstract class BaseAttachmentProvider<Type>(
         } else {
             target.onVideoFileLoading(info.uid)
             fileService.downloadFile(
-                    downloadMode = FileService.DownloadMode.FOR_INTERNAL_USE,
                     id = data.eventId,
                     mimeType = data.mimeType,
                     elementToDecrypt = data.elementToDecrypt,
diff --git a/vector/src/main/java/im/vector/app/features/media/DataAttachmentRoomProvider.kt b/vector/src/main/java/im/vector/app/features/media/DataAttachmentRoomProvider.kt
index 18312b4aa0..6f58c1a4f3 100644
--- a/vector/src/main/java/im/vector/app/features/media/DataAttachmentRoomProvider.kt
+++ b/vector/src/main/java/im/vector/app/features/media/DataAttachmentRoomProvider.kt
@@ -77,7 +77,6 @@ class DataAttachmentRoomProvider(
     override fun getFileForSharing(position: Int, callback: (File?) -> Unit) {
         val item = getItem(position)
         fileService.downloadFile(
-                downloadMode = FileService.DownloadMode.FOR_EXTERNAL_SHARE,
                 id = item.eventId,
                 fileName = item.filename,
                 mimeType = item.mimeType,
diff --git a/vector/src/main/java/im/vector/app/features/media/RoomEventsAttachmentProvider.kt b/vector/src/main/java/im/vector/app/features/media/RoomEventsAttachmentProvider.kt
index 1e2761dde0..9b895dbc4d 100644
--- a/vector/src/main/java/im/vector/app/features/media/RoomEventsAttachmentProvider.kt
+++ b/vector/src/main/java/im/vector/app/features/media/RoomEventsAttachmentProvider.kt
@@ -125,7 +125,6 @@ class RoomEventsAttachmentProvider(
                     as? MessageWithAttachmentContent
                     ?: return@let
             fileService.downloadFile(
-                    downloadMode = FileService.DownloadMode.FOR_EXTERNAL_SHARE,
                     id = timelineEvent.eventId,
                     fileName = messageContent.body,
                     mimeType = messageContent.mimeType,
diff --git a/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt b/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt
index f8cd09ce2f..35375bc8ce 100644
--- a/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt
+++ b/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt
@@ -27,7 +27,6 @@ import im.vector.app.core.error.ErrorFormatter
 import im.vector.app.core.utils.isLocalFile
 import kotlinx.android.parcel.Parcelize
 import org.matrix.android.sdk.api.MatrixCallback
-import org.matrix.android.sdk.api.session.file.FileService
 import org.matrix.android.sdk.internal.crypto.attachments.ElementToDecrypt
 import timber.log.Timber
 import java.io.File
@@ -76,7 +75,6 @@ class VideoContentRenderer @Inject constructor(private val activeSessionHolder:
 
                 activeSessionHolder.getActiveSession().fileService()
                         .downloadFile(
-                                downloadMode = FileService.DownloadMode.FOR_INTERNAL_USE,
                                 id = data.eventId,
                                 fileName = data.filename,
                                 mimeType = data.mimeType,
@@ -116,7 +114,6 @@ class VideoContentRenderer @Inject constructor(private val activeSessionHolder:
 
                 activeSessionHolder.getActiveSession().fileService()
                         .downloadFile(
-                                downloadMode = FileService.DownloadMode.FOR_INTERNAL_USE,
                                 id = data.eventId,
                                 fileName = data.filename,
                                 mimeType = data.mimeType,
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt
index 763eed5474..95d7ce8e93 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt
@@ -30,7 +30,6 @@ import im.vector.app.core.extensions.exhaustive
 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.file.FileService
 import org.matrix.android.sdk.api.session.room.model.message.MessageType
 import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
 import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt
@@ -134,7 +133,6 @@ class RoomUploadsViewModel @AssistedInject constructor(
             try {
                 val file = awaitCallback<File> {
                     session.fileService().downloadFile(
-                            downloadMode = FileService.DownloadMode.FOR_EXTERNAL_SHARE,
                             id = action.uploadEvent.eventId,
                             fileName = action.uploadEvent.contentWithAttachmentContent.body,
                             url = action.uploadEvent.contentWithAttachmentContent.getFileUrl(),
@@ -155,7 +153,6 @@ class RoomUploadsViewModel @AssistedInject constructor(
             try {
                 val file = awaitCallback<File> {
                     session.fileService().downloadFile(
-                            downloadMode = FileService.DownloadMode.FOR_EXTERNAL_SHARE,
                             id = action.uploadEvent.eventId,
                             fileName = action.uploadEvent.contentWithAttachmentContent.body,
                             mimeType = action.uploadEvent.contentWithAttachmentContent.mimeType,

From 8e11ba21edfe1283b6055ac36bbbdac352fa27f5 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 8 Dec 2020 17:37:24 +0100
Subject: [PATCH 08/20] Glide: No Disk cache for encrypted images

---
 .../im/vector/app/features/media/ImageContentRenderer.kt | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/media/ImageContentRenderer.kt b/vector/src/main/java/im/vector/app/features/media/ImageContentRenderer.kt
index 187c2e85c3..cf214b391a 100644
--- a/vector/src/main/java/im/vector/app/features/media/ImageContentRenderer.kt
+++ b/vector/src/main/java/im/vector/app/features/media/ImageContentRenderer.kt
@@ -23,6 +23,7 @@ import android.view.View
 import android.widget.ImageView
 import androidx.core.view.updateLayoutParams
 import com.bumptech.glide.load.DataSource
+import com.bumptech.glide.load.engine.DiskCacheStrategy
 import com.bumptech.glide.load.engine.GlideException
 import com.bumptech.glide.load.resource.bitmap.RoundedCorners
 import com.bumptech.glide.request.RequestListener
@@ -129,6 +130,7 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
             GlideApp
                     .with(contextView)
                     .load(data)
+                    .diskCacheStrategy(DiskCacheStrategy.NONE)
         } else {
             // Clear image
             val resolvedUrl = resolveUrl(data)
@@ -183,6 +185,7 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
             GlideApp
                     .with(imageView)
                     .load(data)
+                    .diskCacheStrategy(DiskCacheStrategy.NONE)
         } else {
             // Clear image
             val resolvedUrl = resolveUrl(data)
@@ -214,14 +217,16 @@ class ImageContentRenderer @Inject constructor(private val activeSessionHolder:
                 .into(imageView)
     }
 
-    fun createGlideRequest(data: Data, mode: Mode, imageView: ImageView, size: Size): GlideRequest<Drawable> {
+    private fun createGlideRequest(data: Data, mode: Mode, imageView: ImageView, size: Size): GlideRequest<Drawable> {
         return createGlideRequest(data, mode, GlideApp.with(imageView), size)
     }
 
     fun createGlideRequest(data: Data, mode: Mode, glideRequests: GlideRequests, size: Size = processSize(data, mode)): GlideRequest<Drawable> {
         return if (data.elementToDecrypt != null) {
             // Encrypted image
-            glideRequests.load(data)
+            glideRequests
+                    .load(data)
+                    .diskCacheStrategy(DiskCacheStrategy.NONE)
         } else {
             // Clear image
             val contentUrlResolver = activeSessionHolder.getActiveSession().contentUrlResolver()

From 42ab7f1b4f8098cb18f24e41cbb9331ce70e6896 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 8 Dec 2020 17:42:42 +0100
Subject: [PATCH 09/20] Add space between image and text And remove useless
 `apply` block

---
 .../timeline/factory/EncryptedItemFactory.kt       | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt
index f77e39c245..e88c1f3797 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt
@@ -82,10 +82,9 @@ class EncryptedItemFactory @Inject constructor(private val messageInformationDat
                         when (cryptoError) {
                             MXCryptoError.ErrorType.KEYS_WITHHELD -> {
                                 span {
-                                    apply {
-                                        drawableProvider.getDrawable(R.drawable.ic_forbidden, colorFromAttribute)?.let {
-                                            image(it, "baseline")
-                                        }
+                                    drawableProvider.getDrawable(R.drawable.ic_forbidden, colorFromAttribute)?.let {
+                                        image(it, "baseline")
+                                        +" "
                                     }
                                     span(stringProvider.getString(R.string.notice_crypto_unable_to_decrypt_final)) {
                                         textStyle = "italic"
@@ -95,10 +94,9 @@ class EncryptedItemFactory @Inject constructor(private val messageInformationDat
                             }
                             else                                  -> {
                                 span {
-                                    apply {
-                                        drawableProvider.getDrawable(R.drawable.ic_clock, colorFromAttribute)?.let {
-                                            image(it, "baseline")
-                                        }
+                                    drawableProvider.getDrawable(R.drawable.ic_clock, colorFromAttribute)?.let {
+                                        image(it, "baseline")
+                                        +" "
                                     }
                                     span(stringProvider.getString(R.string.notice_crypto_unable_to_decrypt_friendly)) {
                                         textStyle = "italic"

From 237cb63fc2b21674e6e09ff064e451d4007f8ab3 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 8 Dec 2020 18:04:42 +0100
Subject: [PATCH 10/20] Small formatting

---
 .../internal/session/DefaultFileService.kt    | 63 ++++++++++---------
 1 file changed, 33 insertions(+), 30 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
index d71c3262c2..37b27cdbae 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
@@ -21,6 +21,13 @@ import android.net.Uri
 import android.webkit.MimeTypeMap
 import androidx.core.content.FileProvider
 import arrow.core.Try
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okio.buffer
+import okio.sink
+import okio.source
 import org.matrix.android.sdk.api.MatrixCallback
 import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.session.content.ContentUrlResolver
@@ -36,13 +43,6 @@ import org.matrix.android.sdk.internal.task.TaskExecutor
 import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
 import org.matrix.android.sdk.internal.util.toCancelable
 import org.matrix.android.sdk.internal.util.writeToFile
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import okhttp3.OkHttpClient
-import okhttp3.Request
-import okio.buffer
-import okio.sink
-import okio.source
 import timber.log.Timber
 import java.io.File
 import java.io.IOException
@@ -157,30 +157,33 @@ internal class DefaultFileService @Inject constructor(
 
                     Try.just(destFile)
                 }
-            }.fold({
-                callback.onFailure(it)
-                // notify concurrent requests
-                val toNotify = synchronized(ongoing) {
-                    ongoing[unwrappedUrl]?.also {
-                        ongoing.remove(unwrappedUrl)
+            }.fold(
+                    { throwable ->
+                        callback.onFailure(throwable)
+                        // notify concurrent requests
+                        val toNotify = synchronized(ongoing) {
+                            ongoing[unwrappedUrl]?.also {
+                                ongoing.remove(unwrappedUrl)
+                            }
+                        }
+                        toNotify?.forEach { otherCallbacks ->
+                            tryOrNull { otherCallbacks.onFailure(throwable) }
+                        }
+                    },
+                    { file ->
+                        callback.onSuccess(file)
+                        // notify concurrent requests
+                        val toNotify = synchronized(ongoing) {
+                            ongoing[unwrappedUrl]?.also {
+                                ongoing.remove(unwrappedUrl)
+                            }
+                        }
+                        Timber.v("## FileService additional to notify ${toNotify?.size ?: 0} ")
+                        toNotify?.forEach { otherCallbacks ->
+                            tryOrNull { otherCallbacks.onSuccess(file) }
+                        }
                     }
-                }
-                toNotify?.forEach { otherCallbacks ->
-                    tryOrNull { otherCallbacks.onFailure(it) }
-                }
-            }, { file ->
-                callback.onSuccess(file)
-                // notify concurrent requests
-                val toNotify = synchronized(ongoing) {
-                    ongoing[unwrappedUrl]?.also {
-                        ongoing.remove(unwrappedUrl)
-                    }
-                }
-                Timber.v("## FileService additional to notify ${toNotify?.size ?: 0} ")
-                toNotify?.forEach { otherCallbacks ->
-                    tryOrNull { otherCallbacks.onSuccess(file) }
-                }
-            })
+            )
         }.toCancelable()
     }
 

From 62791e4b36df0020af82fb2098f5bd12d5c3f5f1 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 8 Dec 2020 18:35:17 +0100
Subject: [PATCH 11/20] Encrypted files: store decrypted file in a dedicated
 folder

---
 .../sdk/api/session/file/FileService.kt       |  7 +-
 .../internal/session/DefaultFileService.kt    | 65 +++++++++++++------
 2 files changed, 51 insertions(+), 21 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
index dd592d84a3..e13aed628c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
@@ -60,10 +60,15 @@ interface FileService {
     fun fileState(mxcUrl: String, mimeType: String?): FileState
 
     /**
-     * Clears all the files downloaded by the service
+     * Clears all the files downloaded by the service, including decrypted files
      */
     fun clearCache()
 
+    /**
+     * Clears all the decrypted files by the service
+     */
+    fun clearDecryptedCache()
+
     /**
      * Get size of cached files
      */
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
index 37b27cdbae..062d09e101 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
@@ -63,7 +63,15 @@ internal class DefaultFileService @Inject constructor(
 
     private fun String.safeFileName() = URLEncoder.encode(this, Charsets.US_ASCII.displayName())
 
-    private val downloadFolder = File(sessionCacheDirectory, "MF")
+    // Folder to store downloaded file (not decrypted)
+    private val legacyFolder = File(sessionCacheDirectory, "MF")
+    private val downloadFolder = File(sessionCacheDirectory, "F")
+    private val decryptedFolder = File(downloadFolder, "D")
+
+    init {
+        // Clear the legacy downloaded files
+        legacyFolder.deleteRecursively()
+    }
 
     /**
      * Retain ongoing downloads to avoid re-downloading and already downloading file
@@ -103,8 +111,8 @@ internal class DefaultFileService @Inject constructor(
         return taskExecutor.executorScope.launch(coroutineDispatchers.main) {
             withContext(coroutineDispatchers.io) {
                 Try {
-                    if (!downloadFolder.exists()) {
-                        downloadFolder.mkdirs()
+                    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
@@ -134,29 +142,42 @@ internal class DefaultFileService @Inject constructor(
 
                         Timber.v("Response size ${response.body?.contentLength()} - Stream available: ${!source.exhausted()}")
 
-                        if (elementToDecrypt != null) {
-                            Timber.v("## FileService: decrypt file")
-                            val decryptSuccess = destFile.outputStream().buffered().use {
-                                MXEncryptedAttachments.decryptAttachment(
-                                        source.inputStream(),
-                                        elementToDecrypt,
-                                        it
-                                )
-                            }
-                            response.close()
-                            if (!decryptSuccess) {
-                                return@flatMap Try.Failure(IllegalStateException("Decryption error"))
-                            }
-                        } else {
-                            writeToFile(source.inputStream(), destFile)
-                            response.close()
-                        }
+                        // Write the file to cache (encrypted version if the file is encrypted)
+                        writeToFile(source.inputStream(), destFile)
+                        response.close()
                     } else {
                         Timber.v("## FileService: cache hit for $url")
                     }
 
                     Try.just(destFile)
                 }
+            }.flatMap { downloadedFile ->
+                // Decrypt if necessary
+                if (elementToDecrypt != null) {
+                    val decryptedFile = File(decryptedFolder, fileForUrl(unwrappedUrl, mimeType))
+
+                    if (!decryptedFile.exists()) {
+                        Timber.v("## FileService: decrypt file")
+                        val decryptSuccess = decryptedFile.outputStream().buffered().use { outputStream ->
+                            downloadedFile.inputStream().use { inputStream ->
+                                MXEncryptedAttachments.decryptAttachment(
+                                        inputStream,
+                                        elementToDecrypt,
+                                        outputStream
+                                )
+                            }
+                        }
+                        if (!decryptSuccess) {
+                            return@flatMap Try.Failure(IllegalStateException("Decryption error"))
+                        }
+                    } else {
+                        Timber.v("## FileService: cache hit for decrypted file")
+                    }
+                    Try.just(decryptedFile)
+                } else {
+                    // Clear file
+                    Try.just(downloadedFile)
+                }
             }.fold(
                     { throwable ->
                         callback.onFailure(throwable)
@@ -240,4 +261,8 @@ internal class DefaultFileService @Inject constructor(
     override fun clearCache() {
         downloadFolder.deleteRecursively()
     }
+
+    override fun clearDecryptedCache() {
+        decryptedFolder.deleteRecursively()
+    }
 }

From 7057b2970b4a84acbcce1fc31f6b92bcba6f4f50 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Tue, 8 Dec 2020 19:31:29 +0100
Subject: [PATCH 12/20] Improve FileService API: add facility methods to deal
 with MessageWithAttachment object

---
 .../sdk/api/session/file/FileService.kt       | 50 +++++++++++++++++--
 .../internal/session/DefaultFileService.kt    | 24 ++++++---
 .../home/room/detail/RoomDetailFragment.kt    | 10 +---
 .../home/room/detail/RoomDetailViewModel.kt   | 13 ++---
 .../timeline/factory/MessageItemFactory.kt    |  4 +-
 .../uploads/RoomUploadsViewModel.kt           | 10 +---
 6 files changed, 76 insertions(+), 35 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
index e13aed628c..d3327ba920 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
@@ -18,8 +18,12 @@ 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
 
 /**
@@ -45,19 +49,59 @@ interface FileService {
             elementToDecrypt: ElementToDecrypt?,
             callback: MatrixCallback<File>): Cancelable
 
-    fun isFileInCache(mxcUrl: String, mimeType: String?): Boolean
+    fun downloadFile(
+            id: String,
+            messageContent: MessageWithAttachmentContent,
+            callback: MatrixCallback<File>): Cancelable =
+            downloadFile(
+                    id = id,
+                    fileName = messageContent.getFileName(),
+                    mimeType = messageContent.mimeType,
+                    url = messageContent.getFileUrl(),
+                    elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt(),
+                    callback = callback
+            )
+
+    fun isFileInCache(mxcUrl: String?,
+                      mimeType: String?,
+                      elementToDecrypt: ElementToDecrypt?
+    ): Boolean
+
+    fun isFileInCache(messageContent: MessageWithAttachmentContent) =
+            isFileInCache(
+                    mxcUrl = messageContent.getFileUrl(),
+                    mimeType = messageContent.mimeType,
+                    elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt())
 
     /**
      * Use this URI and pass it to intent using flag Intent.FLAG_GRANT_READ_URI_PERMISSION
      * (if not other app won't be able to access it)
      */
-    fun getTemporarySharableURI(mxcUrl: String, mimeType: String?): Uri?
+    fun getTemporarySharableURI(mxcUrl: String?,
+                                mimeType: String?,
+                                elementToDecrypt: ElementToDecrypt?): Uri?
+
+    fun getTemporarySharableURI(messageContent: MessageWithAttachmentContent): Uri? =
+            getTemporarySharableURI(
+                    mxcUrl = messageContent.getFileUrl(),
+                    mimeType = messageContent.mimeType,
+                    elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt()
+            )
 
     /**
      * Get information on the given file.
      * Mimetype should be the same one as passed to downloadFile (limitation for now)
      */
-    fun fileState(mxcUrl: String, mimeType: String?): FileState
+    fun fileState(mxcUrl: String?,
+                  mimeType: String?,
+                  elementToDecrypt: ElementToDecrypt?): FileState
+
+    fun fileState(messageContent: MessageWithAttachmentContent): FileState =
+            fileState(
+                    mxcUrl = messageContent.getFileUrl(),
+                    mimeType = messageContent.mimeType,
+                    elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt()
+            )
 
     /**
      * Clears all the files downloaded by the service, including decrypted files
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
index 062d09e101..1e5dff107e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
@@ -225,12 +225,23 @@ internal class DefaultFileService @Inject constructor(
         return if (extension != null) "${url.safeFileName()}.$extension" else url.safeFileName()
     }
 
-    override fun isFileInCache(mxcUrl: String, mimeType: String?): Boolean {
-        return File(downloadFolder, fileForUrl(mxcUrl, mimeType)).exists()
+    override fun isFileInCache(mxcUrl: String?, mimeType: String?, elementToDecrypt: ElementToDecrypt?): Boolean {
+        return fileState(mxcUrl, mimeType, elementToDecrypt) == FileService.FileState.IN_CACHE
     }
 
-    override fun fileState(mxcUrl: String, mimeType: String?): FileService.FileState {
-        if (isFileInCache(mxcUrl, mimeType)) return FileService.FileState.IN_CACHE
+    private fun getClearFile(mxcUrl: String, mimeType: String?, elementToDecrypt: ElementToDecrypt?): File {
+        return if (elementToDecrypt == null) {
+            // Clear file
+            File(downloadFolder, fileForUrl(mxcUrl, mimeType))
+        } else {
+            // Encrypted file
+            File(decryptedFolder, fileForUrl(mxcUrl, mimeType))
+        }
+    }
+
+    override fun fileState(mxcUrl: String?, mimeType: String?, elementToDecrypt: ElementToDecrypt?): FileService.FileState {
+        mxcUrl ?: return FileService.FileState.UNKNOWN
+        if (getClearFile(mxcUrl, mimeType, elementToDecrypt).exists()) return FileService.FileState.IN_CACHE
         val isDownloading = synchronized(ongoing) {
             ongoing[mxcUrl] != null
         }
@@ -241,10 +252,11 @@ internal class DefaultFileService @Inject constructor(
      * Use this URI and pass it to intent using flag Intent.FLAG_GRANT_READ_URI_PERMISSION
      * (if not other app won't be able to access it)
      */
-    override fun getTemporarySharableURI(mxcUrl: String, mimeType: String?): Uri? {
+    override fun getTemporarySharableURI(mxcUrl: String?, mimeType: String?, elementToDecrypt: ElementToDecrypt?): Uri? {
+        mxcUrl ?: return null
         // this string could be extracted no?
         val authority = "${context.packageName}.mx-sdk.fileprovider"
-        val targetFile = File(downloadFolder, fileForUrl(mxcUrl, mimeType))
+        val targetFile = getClearFile(mxcUrl, mimeType, elementToDecrypt)
         if (!targetFile.exists()) return null
         return FileProvider.getUriForFile(context, authority, targetFile)
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
index bbce180e80..a229f72755 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
@@ -1657,10 +1657,7 @@ class RoomDetailFragment @Inject constructor(
         } else if (action.messageContent is MessageWithAttachmentContent) {
             session.fileService().downloadFile(
                     id = action.eventId,
-                    fileName = action.messageContent.body,
-                    mimeType = action.messageContent.mimeType,
-                    url = action.messageContent.getFileUrl(),
-                    elementToDecrypt = action.messageContent.encryptedFileInfo?.toElementToDecrypt(),
+                    messageContent = action.messageContent,
                     callback = object : MatrixCallback<File> {
                         override fun onSuccess(data: File) {
                             if (isAdded) {
@@ -1691,10 +1688,7 @@ class RoomDetailFragment @Inject constructor(
         }
         session.fileService().downloadFile(
                 id = action.eventId,
-                fileName = action.messageContent.body,
-                mimeType = action.messageContent.mimeType,
-                url = action.messageContent.getFileUrl(),
-                elementToDecrypt = action.messageContent.encryptedFileInfo?.toElementToDecrypt(),
+                messageContent = action.messageContent,
                 callback = object : MatrixCallback<File> {
                     override fun onSuccess(data: File) {
                         if (isAdded) {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
index a13ee3be62..86f22a55ad 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
@@ -1009,10 +1009,10 @@ class RoomDetailViewModel @AssistedInject constructor(
     }
 
     private fun handleOpenOrDownloadFile(action: RoomDetailAction.DownloadOrOpen) {
-        val mxcUrl = action.messageFileContent.getFileUrl()
+        val mxcUrl = action.messageFileContent.getFileUrl() ?: return
         val isLocalSendingFile = action.senderId == session.myUserId
-                && mxcUrl?.startsWith("content://") ?: false
-        val isDownloaded = mxcUrl?.let { session.fileService().isFileInCache(it, action.messageFileContent.mimeType) } ?: false
+                && mxcUrl.startsWith("content://")
+        val isDownloaded = session.fileService().isFileInCache(action.messageFileContent)
         if (isLocalSendingFile) {
             tryOrNull { Uri.parse(mxcUrl) }?.let {
                 _viewEvents.post(RoomDetailViewEvents.OpenFile(
@@ -1023,7 +1023,7 @@ class RoomDetailViewModel @AssistedInject constructor(
             }
         } else if (isDownloaded) {
             // we can open it
-            session.fileService().getTemporarySharableURI(mxcUrl!!, action.messageFileContent.mimeType)?.let { uri ->
+            session.fileService().getTemporarySharableURI(action.messageFileContent)?.let { uri ->
                 _viewEvents.post(RoomDetailViewEvents.OpenFile(
                         action.messageFileContent.mimeType,
                         uri,
@@ -1033,10 +1033,7 @@ class RoomDetailViewModel @AssistedInject constructor(
         } else {
             session.fileService().downloadFile(
                     id = action.eventId,
-                    fileName = action.messageFileContent.getFileName(),
-                    mimeType = action.messageFileContent.mimeType,
-                    url = mxcUrl,
-                    elementToDecrypt = action.messageFileContent.encryptedFileInfo?.toElementToDecrypt(),
+                    messageContent = action.messageFileContent,
                     callback = object : MatrixCallback<File> {
                         override fun onSuccess(data: File) {
                             _viewEvents.post(RoomDetailViewEvents.DownloadFileState(
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
index 2b067ccf3f..213c50b6ac 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
@@ -204,7 +204,7 @@ class MessageItemFactory @Inject constructor(
         return MessageFileItem_()
                 .attributes(attributes)
                 .izLocalFile(fileUrl.isLocalFile())
-                .izDownloaded(session.fileService().isFileInCache(fileUrl, messageContent.mimeType))
+                .izDownloaded(session.fileService().isFileInCache(fileUrl, messageContent.mimeType, messageContent.encryptedFileInfo?.toElementToDecrypt()))
                 .mxcUrl(fileUrl)
                 .contentUploadStateTrackerBinder(contentUploadStateTrackerBinder)
                 .contentDownloadStateTrackerBinder(contentDownloadStateTrackerBinder)
@@ -264,7 +264,7 @@ class MessageItemFactory @Inject constructor(
                 .attributes(attributes)
                 .leftGuideline(avatarSizeProvider.leftGuideline)
                 .izLocalFile(messageContent.getFileUrl().isLocalFile())
-                .izDownloaded(session.fileService().isFileInCache(mxcUrl, messageContent.mimeType))
+                .izDownloaded(session.fileService().isFileInCache(messageContent))
                 .mxcUrl(mxcUrl)
                 .contentUploadStateTrackerBinder(contentUploadStateTrackerBinder)
                 .contentDownloadStateTrackerBinder(contentDownloadStateTrackerBinder)
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt
index 95d7ce8e93..bd37cecd56 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt
@@ -134,10 +134,7 @@ class RoomUploadsViewModel @AssistedInject constructor(
                 val file = awaitCallback<File> {
                     session.fileService().downloadFile(
                             id = action.uploadEvent.eventId,
-                            fileName = action.uploadEvent.contentWithAttachmentContent.body,
-                            url = action.uploadEvent.contentWithAttachmentContent.getFileUrl(),
-                            mimeType = action.uploadEvent.contentWithAttachmentContent.mimeType,
-                            elementToDecrypt = action.uploadEvent.contentWithAttachmentContent.encryptedFileInfo?.toElementToDecrypt(),
+                            messageContent = action.uploadEvent.contentWithAttachmentContent,
                             callback = it
                     )
                 }
@@ -154,10 +151,7 @@ class RoomUploadsViewModel @AssistedInject constructor(
                 val file = awaitCallback<File> {
                     session.fileService().downloadFile(
                             id = action.uploadEvent.eventId,
-                            fileName = action.uploadEvent.contentWithAttachmentContent.body,
-                            mimeType = action.uploadEvent.contentWithAttachmentContent.mimeType,
-                            url = action.uploadEvent.contentWithAttachmentContent.getFileUrl(),
-                            elementToDecrypt = action.uploadEvent.contentWithAttachmentContent.encryptedFileInfo?.toElementToDecrypt(),
+                            messageContent = action.uploadEvent.contentWithAttachmentContent,
                             callback = it)
                 }
                 _viewEvents.post(RoomUploadsViewEvents.FileReadyForSaving(file, action.uploadEvent.contentWithAttachmentContent.body))

From ca7796114cb56aac7f645c4c20e70082403b531a Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 9 Dec 2020 10:50:21 +0100
Subject: [PATCH 13/20] DefaultFileService: better management of the files and
 the filenames

---
 .../sdk/api/session/file/FileService.kt       |   6 +
 .../internal/session/DefaultFileService.kt    | 141 ++++++++++++------
 .../session/content/UploadContentWorker.kt    |   3 +-
 .../android/sdk/internal/util/FileSaver.kt    |   3 +
 .../timeline/factory/MessageItemFactory.kt    |   8 +-
 5 files changed, 110 insertions(+), 51 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
index d3327ba920..d0f53f25de 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
@@ -63,6 +63,7 @@ interface FileService {
             )
 
     fun isFileInCache(mxcUrl: String?,
+                      fileName: String,
                       mimeType: String?,
                       elementToDecrypt: ElementToDecrypt?
     ): Boolean
@@ -70,6 +71,7 @@ interface FileService {
     fun isFileInCache(messageContent: MessageWithAttachmentContent) =
             isFileInCache(
                     mxcUrl = messageContent.getFileUrl(),
+                    fileName = messageContent.getFileName(),
                     mimeType = messageContent.mimeType,
                     elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt())
 
@@ -78,12 +80,14 @@ interface FileService {
      * (if not other app won't be able to access it)
      */
     fun getTemporarySharableURI(mxcUrl: String?,
+                                fileName: String,
                                 mimeType: String?,
                                 elementToDecrypt: ElementToDecrypt?): Uri?
 
     fun getTemporarySharableURI(messageContent: MessageWithAttachmentContent): Uri? =
             getTemporarySharableURI(
                     mxcUrl = messageContent.getFileUrl(),
+                    fileName = messageContent.getFileName(),
                     mimeType = messageContent.mimeType,
                     elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt()
             )
@@ -93,12 +97,14 @@ interface FileService {
      * Mimetype should be the same one as passed to downloadFile (limitation for now)
      */
     fun fileState(mxcUrl: String?,
+                  fileName: String,
                   mimeType: String?,
                   elementToDecrypt: ElementToDecrypt?): FileState
 
     fun fileState(messageContent: MessageWithAttachmentContent): FileState =
             fileState(
                     mxcUrl = messageContent.getFileUrl(),
+                    fileName = messageContent.getFileName(),
                     mimeType = messageContent.mimeType,
                     elementToDecrypt = messageContent.encryptedFileInfo?.toElementToDecrypt()
             )
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
index 1e5dff107e..006ced8530 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
@@ -25,9 +25,6 @@ import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import okhttp3.OkHttpClient
 import okhttp3.Request
-import okio.buffer
-import okio.sink
-import okio.source
 import org.matrix.android.sdk.api.MatrixCallback
 import org.matrix.android.sdk.api.extensions.tryOrNull
 import org.matrix.android.sdk.api.session.content.ContentUrlResolver
@@ -41,13 +38,12 @@ import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificateWithProg
 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
 import java.io.IOException
-import java.io.InputStream
-import java.net.URLEncoder
 import javax.inject.Inject
 
 internal class DefaultFileService @Inject constructor(
@@ -61,8 +57,6 @@ internal class DefaultFileService @Inject constructor(
         private val taskExecutor: TaskExecutor
 ) : FileService {
 
-    private fun String.safeFileName() = URLEncoder.encode(this, Charsets.US_ASCII.displayName())
-
     // Folder to store downloaded file (not decrypted)
     private val legacyFolder = File(sessionCacheDirectory, "MF")
     private val downloadFolder = File(sessionCacheDirectory, "F")
@@ -89,21 +83,21 @@ internal class DefaultFileService @Inject constructor(
                               url: String?,
                               elementToDecrypt: ElementToDecrypt?,
                               callback: MatrixCallback<File>): Cancelable {
-        val unwrappedUrl = url ?: return NoOpCancellable.also {
+        url ?: return NoOpCancellable.also {
             callback.onFailure(IllegalArgumentException("url is null"))
         }
 
-        Timber.v("## FileService downloadFile $unwrappedUrl")
+        Timber.v("## FileService downloadFile $url")
 
         synchronized(ongoing) {
-            val existing = ongoing[unwrappedUrl]
+            val existing = ongoing[url]
             if (existing != null) {
                 Timber.v("## FileService downloadFile is already downloading.. ")
                 existing.add(callback)
                 return NoOpCancellable
             } else {
                 // mark as tracked
-                ongoing[unwrappedUrl] = ArrayList()
+                ongoing[url] = ArrayList()
                 // and proceed to download
             }
         }
@@ -117,9 +111,9 @@ internal class DefaultFileService @Inject constructor(
                     // 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)
-                    File(downloadFolder, fileForUrl(unwrappedUrl, mimeType))
-                }.flatMap { destFile ->
-                    if (!destFile.exists()) {
+                    getFiles(url, fileName, mimeType, elementToDecrypt)
+                }.flatMap { cachedFiles ->
+                    if (!cachedFiles.file.exists()) {
                         val resolvedUrl = contentUrlResolver.resolveFullSize(url) ?: return@flatMap Try.Failure(IllegalArgumentException("url is null"))
 
                         val request = Request.Builder()
@@ -143,23 +137,23 @@ internal class DefaultFileService @Inject constructor(
                         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(), destFile)
+                        writeToFile(source.inputStream(), cachedFiles.file)
                         response.close()
                     } else {
                         Timber.v("## FileService: cache hit for $url")
                     }
 
-                    Try.just(destFile)
+                    Try.just(cachedFiles)
                 }
-            }.flatMap { downloadedFile ->
+            }.flatMap { cachedFiles ->
                 // Decrypt if necessary
-                if (elementToDecrypt != null) {
-                    val decryptedFile = File(decryptedFolder, fileForUrl(unwrappedUrl, mimeType))
-
-                    if (!decryptedFile.exists()) {
+                if (cachedFiles.decryptedFile != null) {
+                    if (!cachedFiles.decryptedFile.exists()) {
                         Timber.v("## FileService: decrypt file")
-                        val decryptSuccess = decryptedFile.outputStream().buffered().use { outputStream ->
-                            downloadedFile.inputStream().use { inputStream ->
+                        // 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,
@@ -173,18 +167,18 @@ internal class DefaultFileService @Inject constructor(
                     } else {
                         Timber.v("## FileService: cache hit for decrypted file")
                     }
-                    Try.just(decryptedFile)
+                    Try.just(cachedFiles.decryptedFile)
                 } else {
                     // Clear file
-                    Try.just(downloadedFile)
+                    Try.just(cachedFiles.file)
                 }
             }.fold(
                     { throwable ->
                         callback.onFailure(throwable)
                         // notify concurrent requests
                         val toNotify = synchronized(ongoing) {
-                            ongoing[unwrappedUrl]?.also {
-                                ongoing.remove(unwrappedUrl)
+                            ongoing[url]?.also {
+                                ongoing.remove(url)
                             }
                         }
                         toNotify?.forEach { otherCallbacks ->
@@ -195,8 +189,8 @@ internal class DefaultFileService @Inject constructor(
                         callback.onSuccess(file)
                         // notify concurrent requests
                         val toNotify = synchronized(ongoing) {
-                            ongoing[unwrappedUrl]?.also {
-                                ongoing.remove(unwrappedUrl)
+                            ongoing[url]?.also {
+                                ongoing.remove(url)
                             }
                         }
                         Timber.v("## FileService additional to notify ${toNotify?.size ?: 0} ")
@@ -208,6 +202,7 @@ internal class DefaultFileService @Inject constructor(
         }.toCancelable()
     }
 
+    /*
     fun storeDataFor(url: String, mimeType: String?, inputStream: InputStream) {
         val file = File(downloadFolder, fileForUrl(url, mimeType))
         val source = inputStream.source().buffer()
@@ -219,29 +214,70 @@ internal class DefaultFileService @Inject constructor(
             }
         }
     }
+     */
 
-    private fun fileForUrl(url: String, mimeType: String?): String {
-        val extension = mimeType?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) }
-        return if (extension != null) "${url.safeFileName()}.$extension" else url.safeFileName()
-    }
-
-    override fun isFileInCache(mxcUrl: String?, mimeType: String?, elementToDecrypt: ElementToDecrypt?): Boolean {
-        return fileState(mxcUrl, mimeType, elementToDecrypt) == FileService.FileState.IN_CACHE
-    }
-
-    private fun getClearFile(mxcUrl: String, mimeType: String?, elementToDecrypt: ElementToDecrypt?): File {
-        return if (elementToDecrypt == null) {
-            // Clear file
-            File(downloadFolder, fileForUrl(mxcUrl, mimeType))
-        } else {
-            // Encrypted file
-            File(decryptedFolder, fileForUrl(mxcUrl, mimeType))
+    private fun safeFileName(fileName: String, mimeType: String?): String {
+        return buildString {
+            // filename has to be safe for the Android System
+            val result = fileName.replace("[^a-z A-Z0-9\\\\.\\-]".toRegex(), "_")
+            append(result)
+            // Check that the extension is correct regarding the mimeType
+            val extensionFromMime = mimeType?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) }
+            if (extensionFromMime != null) {
+                // Compare
+                val fileExtension = result.substringAfterLast(delimiter = ".", missingDelimiterValue = "")
+                if (fileExtension.isEmpty() || fileExtension != extensionFromMime) {
+                    // Missing extension, or diff in extension, add the one provided by the mimetype
+                    append(".")
+                    append(extensionFromMime)
+                }
+            }
         }
     }
 
-    override fun fileState(mxcUrl: String?, mimeType: String?, elementToDecrypt: ElementToDecrypt?): FileService.FileState {
+    override fun isFileInCache(mxcUrl: String?,
+                               fileName: String,
+                               mimeType: String?,
+                               elementToDecrypt: ElementToDecrypt?): Boolean {
+        return fileState(mxcUrl, fileName, mimeType, elementToDecrypt) == FileService.FileState.IN_CACHE
+    }
+
+    internal data class CachedFiles(
+            // This is the downloaded file. Can be clear or encrypted
+            val file: File,
+            // This is the decrypted file. Null if the original file is not encrypted
+            val decryptedFile: File?
+    ) {
+        fun getClearFile(): File = decryptedFile ?: file
+    }
+
+    private fun getFiles(mxcUrl: String,
+                         fileName: String,
+                         mimeType: String?,
+                         elementToDecrypt: ElementToDecrypt?): CachedFiles {
+        val hashFolder = mxcUrl.md5()
+        val safeFileName = safeFileName(fileName, mimeType)
+        return if (elementToDecrypt == null) {
+            // Clear file
+            CachedFiles(
+                    File(downloadFolder, "$hashFolder/$safeFileName"),
+                    null
+            )
+        } else {
+            // Encrypted file
+            CachedFiles(
+                    File(downloadFolder, "$hashFolder/$ENCRYPTED_FILENAME"),
+                    File(decryptedFolder, "$hashFolder/$safeFileName"),
+            )
+        }
+    }
+
+    override fun fileState(mxcUrl: String?,
+                           fileName: String,
+                           mimeType: String?,
+                           elementToDecrypt: ElementToDecrypt?): FileService.FileState {
         mxcUrl ?: return FileService.FileState.UNKNOWN
-        if (getClearFile(mxcUrl, mimeType, elementToDecrypt).exists()) return FileService.FileState.IN_CACHE
+        if (getFiles(mxcUrl, fileName, mimeType, elementToDecrypt).file.exists()) return FileService.FileState.IN_CACHE
         val isDownloading = synchronized(ongoing) {
             ongoing[mxcUrl] != null
         }
@@ -252,11 +288,14 @@ internal class DefaultFileService @Inject constructor(
      * Use this URI and pass it to intent using flag Intent.FLAG_GRANT_READ_URI_PERMISSION
      * (if not other app won't be able to access it)
      */
-    override fun getTemporarySharableURI(mxcUrl: String?, mimeType: String?, elementToDecrypt: ElementToDecrypt?): Uri? {
+    override fun getTemporarySharableURI(mxcUrl: String?,
+                                         fileName: String,
+                                         mimeType: String?,
+                                         elementToDecrypt: ElementToDecrypt?): Uri? {
         mxcUrl ?: return null
         // this string could be extracted no?
         val authority = "${context.packageName}.mx-sdk.fileprovider"
-        val targetFile = getClearFile(mxcUrl, mimeType, elementToDecrypt)
+        val targetFile = getFiles(mxcUrl, fileName, mimeType, elementToDecrypt).getClearFile()
         if (!targetFile.exists()) return null
         return FileProvider.getUriForFile(context, authority, targetFile)
     }
@@ -277,4 +316,8 @@ internal class DefaultFileService @Inject constructor(
     override fun clearDecryptedCache() {
         decryptedFolder.deleteRecursively()
     }
+
+    companion object {
+        private const val ENCRYPTED_FILENAME = "encrypted.bin"
+    }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
index 4a30d6c1e6..8df5082c33 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
@@ -199,9 +199,10 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
 
                 Timber.v("## FileService: Update cache storage for ${contentUploadResponse.contentUri}")
                 try {
+                    /* TODO
                     context.contentResolver.openInputStream(attachment.queryUri)?.let {
                         fileService.storeDataFor(contentUploadResponse.contentUri, params.attachment.getSafeMimeType(), it)
-                    }
+                    } */
                     Timber.v("## FileService: cache storage updated")
                 } catch (failure: Throwable) {
                     Timber.e(failure, "## FileService: Failed to update file cache")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/FileSaver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/FileSaver.kt
index 4dc54d3b19..fb5e3a5774 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/FileSaver.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/FileSaver.kt
@@ -25,6 +25,9 @@ import java.io.InputStream
  */
 @WorkerThread
 fun writeToFile(inputStream: InputStream, outputFile: File) {
+    // Ensure the parent folder exists, else it will crash
+    outputFile.parentFile?.mkdirs()
+
     outputFile.outputStream().use {
         inputStream.copyTo(it)
     }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
index 213c50b6ac..34086043da 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
@@ -84,6 +84,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageVerification
 import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
 import org.matrix.android.sdk.api.session.room.model.message.OPTION_TYPE_BUTTONS
 import org.matrix.android.sdk.api.session.room.model.message.OPTION_TYPE_POLL
+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.session.room.timeline.TimelineEvent
 import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
@@ -204,7 +205,12 @@ class MessageItemFactory @Inject constructor(
         return MessageFileItem_()
                 .attributes(attributes)
                 .izLocalFile(fileUrl.isLocalFile())
-                .izDownloaded(session.fileService().isFileInCache(fileUrl, messageContent.mimeType, messageContent.encryptedFileInfo?.toElementToDecrypt()))
+                .izDownloaded(session.fileService().isFileInCache(
+                        fileUrl,
+                        messageContent.getFileName(),
+                        messageContent.mimeType,
+                        messageContent.encryptedFileInfo?.toElementToDecrypt())
+                )
                 .mxcUrl(fileUrl)
                 .contentUploadStateTrackerBinder(contentUploadStateTrackerBinder)
                 .contentDownloadStateTrackerBinder(contentDownloadStateTrackerBinder)

From 1c43f92e49aae4d21cc28b2b193ccb3f2d1d973c Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 9 Dec 2020 12:20:48 +0100
Subject: [PATCH 14/20] DefaultFileService: store just sent file

---
 .../internal/session/DefaultFileService.kt    | 57 +++++++++++--------
 .../session/content/UploadContentWorker.kt    | 19 ++++---
 2 files changed, 44 insertions(+), 32 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
index 006ced8530..54ff90631b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
@@ -111,7 +111,7 @@ internal class DefaultFileService @Inject constructor(
                     // 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)
+                    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"))
@@ -202,24 +202,29 @@ internal class DefaultFileService @Inject constructor(
         }.toCancelable()
     }
 
-    /*
-    fun storeDataFor(url: String, mimeType: String?, inputStream: InputStream) {
-        val file = File(downloadFolder, fileForUrl(url, mimeType))
-        val source = inputStream.source().buffer()
-        file.sink().buffer().let { sink ->
-            source.use { input ->
-                sink.use { output ->
-                    output.writeAll(input)
-                }
-            }
+    fun storeDataFor(mxcUrl: String,
+                     filename: String?,
+                     mimeType: String?,
+                     originalFile: File,
+                     encryptedFile: File?) {
+        val files = getFiles(mxcUrl, filename, mimeType, encryptedFile != null)
+        if (encryptedFile != null) {
+            // We switch the two files here, original file it the decrypted file
+            files.decryptedFile?.let { originalFile.copyTo(it) }
+            encryptedFile.copyTo(files.file)
+        } else {
+            // Just copy the original file
+            originalFile.copyTo(files.file)
         }
     }
-     */
 
-    private fun safeFileName(fileName: String, mimeType: String?): String {
+    private fun safeFileName(fileName: String?, mimeType: String?): String {
         return buildString {
             // filename has to be safe for the Android System
-            val result = fileName.replace("[^a-z A-Z0-9\\\\.\\-]".toRegex(), "_")
+            val result = fileName
+                    ?.replace("[^a-z A-Z0-9\\\\.\\-]".toRegex(), "_")
+                    ?.takeIf { it.isNotEmpty() }
+                    ?: DEFAULT_FILENAME
             append(result)
             // Check that the extension is correct regarding the mimeType
             val extensionFromMime = mimeType?.let { MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) }
@@ -252,23 +257,23 @@ internal class DefaultFileService @Inject constructor(
     }
 
     private fun getFiles(mxcUrl: String,
-                         fileName: String,
+                         fileName: String?,
                          mimeType: String?,
-                         elementToDecrypt: ElementToDecrypt?): CachedFiles {
+                         isEncrypted: Boolean): CachedFiles {
         val hashFolder = mxcUrl.md5()
         val safeFileName = safeFileName(fileName, mimeType)
-        return if (elementToDecrypt == null) {
-            // Clear file
-            CachedFiles(
-                    File(downloadFolder, "$hashFolder/$safeFileName"),
-                    null
-            )
-        } else {
+        return if (isEncrypted) {
             // Encrypted file
             CachedFiles(
                     File(downloadFolder, "$hashFolder/$ENCRYPTED_FILENAME"),
                     File(decryptedFolder, "$hashFolder/$safeFileName"),
             )
+        } else {
+            // Clear file
+            CachedFiles(
+                    File(downloadFolder, "$hashFolder/$safeFileName"),
+                    null
+            )
         }
     }
 
@@ -277,7 +282,7 @@ internal class DefaultFileService @Inject constructor(
                            mimeType: String?,
                            elementToDecrypt: ElementToDecrypt?): FileService.FileState {
         mxcUrl ?: return FileService.FileState.UNKNOWN
-        if (getFiles(mxcUrl, fileName, mimeType, elementToDecrypt).file.exists()) return FileService.FileState.IN_CACHE
+        if (getFiles(mxcUrl, fileName, mimeType, elementToDecrypt != null).file.exists()) return FileService.FileState.IN_CACHE
         val isDownloading = synchronized(ongoing) {
             ongoing[mxcUrl] != null
         }
@@ -295,7 +300,7 @@ internal class DefaultFileService @Inject constructor(
         mxcUrl ?: return null
         // this string could be extracted no?
         val authority = "${context.packageName}.mx-sdk.fileprovider"
-        val targetFile = getFiles(mxcUrl, fileName, mimeType, elementToDecrypt).getClearFile()
+        val targetFile = getFiles(mxcUrl, fileName, mimeType, elementToDecrypt != null).getClearFile()
         if (!targetFile.exists()) return null
         return FileProvider.getUriForFile(context, authority, targetFile)
     }
@@ -319,5 +324,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"
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
index 8df5082c33..77f39a7768 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt
@@ -174,14 +174,15 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
                     }
                 }
 
+                val encryptedFile: File?
                 val contentUploadResponse = if (params.isEncrypted) {
                     Timber.v("## FileService: Encrypt file")
 
-                    val tmpEncrypted = File.createTempFile(UUID.randomUUID().toString(), null, context.cacheDir)
+                    encryptedFile = File.createTempFile(UUID.randomUUID().toString(), null, context.cacheDir)
                             .also { filesToDelete.add(it) }
 
                     uploadedFileEncryptedFileInfo =
-                            MXEncryptedAttachments.encrypt(fileToUpload.inputStream(), attachment.getSafeMimeType(), tmpEncrypted) { read, total ->
+                            MXEncryptedAttachments.encrypt(fileToUpload.inputStream(), attachment.getSafeMimeType(), encryptedFile) { read, total ->
                                 notifyTracker(params) {
                                     contentUploadStateTracker.setEncrypting(it, read.toLong(), total.toLong())
                                 }
@@ -190,19 +191,23 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
                     Timber.v("## FileService: Uploading file")
 
                     fileUploader
-                            .uploadFile(tmpEncrypted, attachment.name, "application/octet-stream", progressListener)
+                            .uploadFile(encryptedFile, attachment.name, "application/octet-stream", progressListener)
                 } else {
                     Timber.v("## FileService: Clear file")
+                    encryptedFile = null
                     fileUploader
                             .uploadFile(fileToUpload, attachment.name, attachment.getSafeMimeType(), progressListener)
                 }
 
                 Timber.v("## FileService: Update cache storage for ${contentUploadResponse.contentUri}")
                 try {
-                    /* TODO
-                    context.contentResolver.openInputStream(attachment.queryUri)?.let {
-                        fileService.storeDataFor(contentUploadResponse.contentUri, params.attachment.getSafeMimeType(), it)
-                    } */
+                    fileService.storeDataFor(
+                            mxcUrl = contentUploadResponse.contentUri,
+                            filename = params.attachment.name,
+                            mimeType = params.attachment.getSafeMimeType(),
+                            originalFile = workingFile,
+                            encryptedFile = encryptedFile
+                    )
                     Timber.v("## FileService: cache storage updated")
                 } catch (failure: Throwable) {
                     Timber.e(failure, "## FileService: Failed to update file cache")

From 283e10dfefd3a3ead990b1a649bc525d1df3127e Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoitm@matrix.org>
Date: Wed, 9 Dec 2020 12:26:49 +0100
Subject: [PATCH 15/20] Use filename if available

---
 .../sdk/internal/session/room/send/DefaultSendService.kt        | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
index 5a71ff7b76..8828f3dfed 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt
@@ -177,7 +177,7 @@ internal class DefaultSendService @AssistedInject constructor(
                     val attachmentData = ContentAttachmentData(
                             size = messageContent.info!!.size,
                             mimeType = messageContent.info.mimeType!!,
-                            name = messageContent.body,
+                            name = messageContent.getFileName(),
                             queryUri = Uri.parse(messageContent.url),
                             type = ContentAttachmentData.Type.FILE
                     )

From e4968c4119014a1bc6ff796b6b8ccf5a720aa7a9 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoitm@matrix.org>
Date: Wed, 9 Dec 2020 12:27:03 +0100
Subject: [PATCH 16/20] Doc and internal

---
 .../sdk/internal/session/content/ContentUploadResponse.kt    | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ContentUploadResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ContentUploadResponse.kt
index b5de26b39d..1ebe5b2eb6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ContentUploadResponse.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/ContentUploadResponse.kt
@@ -20,6 +20,9 @@ import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
-data class ContentUploadResponse(
+internal data class ContentUploadResponse(
+        /**
+         * Required. The MXC URI to the uploaded content.
+         */
         @Json(name = "content_uri") val contentUri: String
 )

From 0956baecf958bc6db35a26b3af74da8eb4ed8214 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoitm@matrix.org>
Date: Wed, 9 Dec 2020 12:27:37 +0100
Subject: [PATCH 17/20] Delete unencrypted files each time the app is started

---
 .../im/vector/app/features/home/HomeActivityViewModel.kt    | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
index 680ec17415..90d128320b 100644
--- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt
@@ -71,12 +71,18 @@ class HomeActivityViewModel @AssistedInject constructor(
     private var onceTrusted = false
 
     init {
+        cleanupFiles()
         observeInitialSync()
         mayBeInitializeCrossSigning()
         checkSessionPushIsOn()
         observeCrossSigningReset()
     }
 
+    private fun cleanupFiles() {
+        // Mitigation: delete all cached decrypted files each time the application is started.
+        activeSessionHolder.getSafeActiveSession()?.fileService()?.clearDecryptedCache()
+    }
+
     private fun observeCrossSigningReset() {
         val safeActiveSession = activeSessionHolder.getSafeActiveSession() ?: return
 

From 4bd538e448a62d2775ef34a6236e988a3c5983ab Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 9 Dec 2020 12:49:25 +0100
Subject: [PATCH 18/20] Changelog and update comment

---
 CHANGES.md                                                    | 1 +
 .../matrix/android/sdk/internal/session/DefaultFileService.kt | 4 +++-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/CHANGES.md b/CHANGES.md
index 7ba7b7cb76..7904834e62 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -4,6 +4,7 @@ Changes in Element 1.0.12 (2020-XX-XX)
 Features ✨:
  - Add room aliases management, and room directory visibility management in a dedicated screen (#1579, #2428)
  - Room setting: update join rules and guest access (#2442)
+ - Store encrypted file in cache and cleanup decrypted file at each app start
 
 Improvements 🙌:
  - Add Setting Item to Change PIN (#2462)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
index 54ff90631b..730c0dd82f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
@@ -57,9 +57,11 @@ internal class DefaultFileService @Inject constructor(
         private val taskExecutor: TaskExecutor
 ) : FileService {
 
-    // Folder to store downloaded file (not decrypted)
+    // 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")
 
     init {

From 75071cf1d9ab15ee715df1697e1faedc1cc41e81 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Wed, 9 Dec 2020 13:50:14 +0100
Subject: [PATCH 19/20] Cleanup

---
 .../internal/network/interceptors/FormattedJsonHttpLogger.kt    | 1 -
 .../matrix/android/sdk/internal/session/DefaultFileService.kt   | 2 +-
 .../vector/app/features/home/room/detail/RoomDetailFragment.kt  | 2 --
 .../vector/app/features/home/room/detail/RoomDetailViewModel.kt | 2 --
 .../app/features/roomprofile/uploads/RoomUploadsViewModel.kt    | 2 --
 5 files changed, 1 insertion(+), 8 deletions(-)

diff --git a/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt
index 849a464867..34ed28d467 100644
--- a/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt
+++ b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/FormattedJsonHttpLogger.kt
@@ -17,7 +17,6 @@
 package org.matrix.android.sdk.internal.network.interceptors
 
 import androidx.annotation.NonNull
-import org.matrix.android.sdk.BuildConfig
 import okhttp3.logging.HttpLoggingInterceptor
 import org.json.JSONArray
 import org.json.JSONException
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
index 730c0dd82f..ee4f5da41e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
@@ -268,7 +268,7 @@ internal class DefaultFileService @Inject constructor(
             // Encrypted file
             CachedFiles(
                     File(downloadFolder, "$hashFolder/$ENCRYPTED_FILENAME"),
-                    File(decryptedFolder, "$hashFolder/$safeFileName"),
+                    File(decryptedFolder, "$hashFolder/$safeFileName")
             )
         } else {
             // Clear file
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
index a229f72755..f8168140a3 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
@@ -184,7 +184,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
 import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
 import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent
 import org.matrix.android.sdk.api.session.room.model.message.MessageWithAttachmentContent
-import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
 import org.matrix.android.sdk.api.session.room.send.SendState
 import org.matrix.android.sdk.api.session.room.timeline.Timeline
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
@@ -193,7 +192,6 @@ import org.matrix.android.sdk.api.session.widgets.model.Widget
 import org.matrix.android.sdk.api.session.widgets.model.WidgetType
 import org.matrix.android.sdk.api.util.MatrixItem
 import org.matrix.android.sdk.api.util.toMatrixItem
-import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt
 import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
 import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
 import timber.log.Timber
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
index 86f22a55ad..182ee6016d 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
@@ -79,7 +79,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary
 import org.matrix.android.sdk.api.session.room.model.message.MessageContent
 import org.matrix.android.sdk.api.session.room.model.message.MessageType
 import org.matrix.android.sdk.api.session.room.model.message.OptionItem
-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.session.room.model.tombstone.RoomTombstoneContent
 import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
@@ -91,7 +90,6 @@ import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent
 import org.matrix.android.sdk.api.session.widgets.model.Widget
 import org.matrix.android.sdk.api.session.widgets.model.WidgetType
 import org.matrix.android.sdk.api.util.toOptional
-import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt
 import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
 import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
 import org.matrix.android.sdk.internal.util.awaitCallback
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt
index bd37cecd56..bf2b56fc9b 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt
@@ -31,8 +31,6 @@ 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.api.session.room.model.message.getFileUrl
-import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt
 import org.matrix.android.sdk.internal.util.awaitCallback
 import org.matrix.android.sdk.rx.rx
 import org.matrix.android.sdk.rx.unwrap

From 5e2f091ec105e0018e97f086b6d124eaba8ec3b3 Mon Sep 17 00:00:00 2001
From: Benoit Marty <benoit@matrix.org>
Date: Thu, 10 Dec 2020 13:36:00 +0100
Subject: [PATCH 20/20] Remove useless parameter `id`

---
 .../sdk/api/session/file/FileService.kt       | 19 +++++++------------
 .../internal/session/DefaultFileService.kt    |  3 +--
 .../app/core/glide/VectorGlideModelLoader.kt  |  5 ++---
 .../home/room/detail/RoomDetailFragment.kt    |  2 --
 .../home/room/detail/RoomDetailViewModel.kt   |  1 -
 .../features/media/BaseAttachmentProvider.kt  |  5 ++---
 .../media/DataAttachmentRoomProvider.kt       |  3 +--
 .../media/RoomEventsAttachmentProvider.kt     |  1 -
 .../features/media/VideoContentRenderer.kt    |  2 --
 .../uploads/RoomUploadsViewModel.kt           |  2 --
 10 files changed, 13 insertions(+), 30 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
index d0f53f25de..bcdb5ea257 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/file/FileService.kt
@@ -41,20 +41,15 @@ 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(
-            id: String,
-            fileName: String,
-            mimeType: String?,
-            url: String?,
-            elementToDecrypt: ElementToDecrypt?,
-            callback: MatrixCallback<File>): Cancelable
+    fun downloadFile(fileName: String,
+                     mimeType: String?,
+                     url: String?,
+                     elementToDecrypt: ElementToDecrypt?,
+                     callback: MatrixCallback<File>): Cancelable
 
-    fun downloadFile(
-            id: String,
-            messageContent: MessageWithAttachmentContent,
-            callback: MatrixCallback<File>): Cancelable =
+    fun downloadFile(messageContent: MessageWithAttachmentContent,
+                     callback: MatrixCallback<File>): Cancelable =
             downloadFile(
-                    id = id,
                     fileName = messageContent.getFileName(),
                     mimeType = messageContent.mimeType,
                     url = messageContent.getFileUrl(),
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
index ee4f5da41e..07cde3da60 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultFileService.kt
@@ -79,8 +79,7 @@ internal class DefaultFileService @Inject constructor(
      * Download file in the cache folder, and eventually decrypt it
      * TODO looks like files are copied 3 times
      */
-    override fun downloadFile(id: String,
-                              fileName: String,
+    override fun downloadFile(fileName: String,
                               mimeType: String?,
                               url: String?,
                               elementToDecrypt: ElementToDecrypt?,
diff --git a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt
index cf40926ba4..9a7cf1eb76 100644
--- a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt
+++ b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt
@@ -109,10 +109,9 @@ class VectorGlideDataFetcher(private val activeSessionHolder: ActiveSessionHolde
         }
         // Use the file vector service, will avoid flickering and redownload after upload
         fileService.downloadFile(
-                mimeType = data.mimeType,
-                id = data.eventId,
-                url = data.url,
                 fileName = data.filename,
+                mimeType = data.mimeType,
+                url = data.url,
                 elementToDecrypt = data.elementToDecrypt,
                 callback = object : MatrixCallback<File> {
                     override fun onSuccess(data: File) {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
index f8168140a3..f1ae79a0aa 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
@@ -1654,7 +1654,6 @@ class RoomDetailFragment @Inject constructor(
             shareText(requireContext(), action.messageContent.body)
         } else if (action.messageContent is MessageWithAttachmentContent) {
             session.fileService().downloadFile(
-                    id = action.eventId,
                     messageContent = action.messageContent,
                     callback = object : MatrixCallback<File> {
                         override fun onSuccess(data: File) {
@@ -1685,7 +1684,6 @@ class RoomDetailFragment @Inject constructor(
             return
         }
         session.fileService().downloadFile(
-                id = action.eventId,
                 messageContent = action.messageContent,
                 callback = object : MatrixCallback<File> {
                     override fun onSuccess(data: File) {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
index 182ee6016d..7bba9728ca 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
@@ -1030,7 +1030,6 @@ class RoomDetailViewModel @AssistedInject constructor(
             }
         } else {
             session.fileService().downloadFile(
-                    id = action.eventId,
                     messageContent = action.messageFileContent,
                     callback = object : MatrixCallback<File> {
                         override fun onSuccess(data: File) {
diff --git a/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt b/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt
index 5f61ca36e4..90b17f80d7 100644
--- a/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt
+++ b/vector/src/main/java/im/vector/app/features/media/BaseAttachmentProvider.kt
@@ -153,11 +153,10 @@ abstract class BaseAttachmentProvider<Type>(
         } else {
             target.onVideoFileLoading(info.uid)
             fileService.downloadFile(
-                    id = data.eventId,
-                    mimeType = data.mimeType,
-                    elementToDecrypt = data.elementToDecrypt,
                     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)
diff --git a/vector/src/main/java/im/vector/app/features/media/DataAttachmentRoomProvider.kt b/vector/src/main/java/im/vector/app/features/media/DataAttachmentRoomProvider.kt
index 6f58c1a4f3..584b13f32b 100644
--- a/vector/src/main/java/im/vector/app/features/media/DataAttachmentRoomProvider.kt
+++ b/vector/src/main/java/im/vector/app/features/media/DataAttachmentRoomProvider.kt
@@ -77,10 +77,9 @@ class DataAttachmentRoomProvider(
     override fun getFileForSharing(position: Int, callback: (File?) -> Unit) {
         val item = getItem(position)
         fileService.downloadFile(
-                id = item.eventId,
                 fileName = item.filename,
                 mimeType = item.mimeType,
-                url = item.url ?: "",
+                url = item.url,
                 elementToDecrypt = item.elementToDecrypt,
                 callback = object : MatrixCallback<File> {
                     override fun onSuccess(data: File) {
diff --git a/vector/src/main/java/im/vector/app/features/media/RoomEventsAttachmentProvider.kt b/vector/src/main/java/im/vector/app/features/media/RoomEventsAttachmentProvider.kt
index 9b895dbc4d..569d006fba 100644
--- a/vector/src/main/java/im/vector/app/features/media/RoomEventsAttachmentProvider.kt
+++ b/vector/src/main/java/im/vector/app/features/media/RoomEventsAttachmentProvider.kt
@@ -125,7 +125,6 @@ class RoomEventsAttachmentProvider(
                     as? MessageWithAttachmentContent
                     ?: return@let
             fileService.downloadFile(
-                    id = timelineEvent.eventId,
                     fileName = messageContent.body,
                     mimeType = messageContent.mimeType,
                     url = messageContent.getFileUrl(),
diff --git a/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt b/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt
index 35375bc8ce..d8eddc7331 100644
--- a/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt
+++ b/vector/src/main/java/im/vector/app/features/media/VideoContentRenderer.kt
@@ -75,7 +75,6 @@ class VideoContentRenderer @Inject constructor(private val activeSessionHolder:
 
                 activeSessionHolder.getActiveSession().fileService()
                         .downloadFile(
-                                id = data.eventId,
                                 fileName = data.filename,
                                 mimeType = data.mimeType,
                                 url = data.url,
@@ -114,7 +113,6 @@ class VideoContentRenderer @Inject constructor(private val activeSessionHolder:
 
                 activeSessionHolder.getActiveSession().fileService()
                         .downloadFile(
-                                id = data.eventId,
                                 fileName = data.filename,
                                 mimeType = data.mimeType,
                                 url = data.url,
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt
index bf2b56fc9b..b62b633a36 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt
@@ -131,7 +131,6 @@ class RoomUploadsViewModel @AssistedInject constructor(
             try {
                 val file = awaitCallback<File> {
                     session.fileService().downloadFile(
-                            id = action.uploadEvent.eventId,
                             messageContent = action.uploadEvent.contentWithAttachmentContent,
                             callback = it
                     )
@@ -148,7 +147,6 @@ class RoomUploadsViewModel @AssistedInject constructor(
             try {
                 val file = awaitCallback<File> {
                     session.fileService().downloadFile(
-                            id = action.uploadEvent.eventId,
                             messageContent = action.uploadEvent.contentWithAttachmentContent,
                             callback = it)
                 }