From f6cc05634ff3852be95b20a35c783bb1c0e59660 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 27 Nov 2020 16:38:46 +0100 Subject: [PATCH 01/42] Send task: small rework and cleanup --- .../room/send/queue/EventSenderProcessor.kt | 6 +---- .../session/room/send/queue/QueueMemento.kt | 1 - .../session/room/send/queue/QueuedTask.kt | 12 +++++++--- .../room/send/queue/RedactQueuedTask.kt | 22 +++++++------------ .../room/send/queue/SendEventQueuedTask.kt | 10 ++------- 5 files changed, 20 insertions(+), 31 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt index 62e225c624..b8f6e52674 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt @@ -106,11 +106,7 @@ internal class EventSenderProcessor @Inject constructor( // non blocking add to queue sendingQueue.add(task) markAsManaged(task) - return object : Cancelable { - override fun cancel() { - task.cancel() - } - } + return task } companion object { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt index e69c65ec4c..dfbac347d9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueueMemento.kt @@ -17,7 +17,6 @@ package org.matrix.android.sdk.internal.session.room.send.queue import android.content.Context -import org.matrix.android.sdk.api.auth.data.sessionId import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.room.send.SendState diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt index bccbc97ff4..e5c1cf7435 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt @@ -16,14 +16,20 @@ package org.matrix.android.sdk.internal.session.room.send.queue -abstract class QueuedTask { +import org.matrix.android.sdk.api.util.Cancelable + +abstract class QueuedTask : Cancelable { var retryCount = 0 + private var hasBeenCancelled: Boolean = false + abstract suspend fun execute() abstract fun onTaskFailed() - abstract fun isCancelled() : Boolean + open fun isCancelled() = hasBeenCancelled - abstract fun cancel() + final override fun cancel() { + hasBeenCancelled = true + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt index a3c19a1f7c..e2fb978cd0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt @@ -22,18 +22,16 @@ import org.matrix.android.sdk.internal.session.room.send.CancelSendTracker import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository internal class RedactQueuedTask( - val toRedactEventId: String, + private val toRedactEventId: String, val redactionLocalEchoId: String, - val roomId: String, - val reason: String?, - val redactEventTask: RedactEventTask, - val localEchoRepository: LocalEchoRepository, - val cancelSendTracker: CancelSendTracker + private val roomId: String, + private val reason: String?, + private val redactEventTask: RedactEventTask, + private val localEchoRepository: LocalEchoRepository, + private val cancelSendTracker: CancelSendTracker ) : QueuedTask() { - private var _isCancelled: Boolean = false - - override fun toString() = "[RedactEventRunnableTask $redactionLocalEchoId]" + override fun toString() = "[RedactQueuedTask $redactionLocalEchoId]" override suspend fun execute() { redactEventTask.execute(RedactEventTask.Params(redactionLocalEchoId, roomId, toRedactEventId, reason)) @@ -44,10 +42,6 @@ internal class RedactQueuedTask( } override fun isCancelled(): Boolean { - return _isCancelled || cancelSendTracker.isCancelRequestedFor(redactionLocalEchoId, roomId) - } - - override fun cancel() { - _isCancelled = true + return super.isCancelled() || cancelSendTracker.isCancelRequestedFor(redactionLocalEchoId, roomId) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt index 21a4145a9d..f934aad67b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt @@ -33,9 +33,7 @@ internal class SendEventQueuedTask( val cancelSendTracker: CancelSendTracker ) : QueuedTask() { - private var _isCancelled: Boolean = false - - override fun toString() = "[SendEventRunnableTask ${event.eventId}]" + override fun toString() = "[SendEventQueuedTask ${event.eventId}]" override suspend fun execute() { sendEventTask.execute(SendEventTask.Params(event, encrypt)) @@ -56,10 +54,6 @@ internal class SendEventQueuedTask( } override fun isCancelled(): Boolean { - return _isCancelled || cancelSendTracker.isCancelRequestedFor(event.eventId, event.roomId) - } - - override fun cancel() { - _isCancelled = true + return super.isCancelled() || cancelSendTracker.isCancelRequestedFor(event.eventId, event.roomId) } } From cd983de058557ae0bd9902138d80f863dfa78323 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 30 Nov 2020 10:08:31 +0100 Subject: [PATCH 02/42] Fix cancellation of sending event (#2438) --- CHANGES.md | 2 +- .../session/room/send/DefaultSendService.kt | 2 ++ .../room/send/queue/EventSenderProcessor.kt | 14 ++++++++++++++ .../internal/session/room/send/queue/QueuedTask.kt | 8 +++++++- .../session/room/send/queue/RedactQueuedTask.kt | 2 +- .../session/room/send/queue/SendEventQueuedTask.kt | 2 +- 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e48281081b..7f626da497 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,7 +8,7 @@ Improvements πŸ™Œ: - Bugfix πŸ›: - - + - Fix cancellation of sending event (#2438) Translations πŸ—£: - 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 b13ce15da6..5a71ff7b76 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 @@ -210,6 +210,8 @@ internal class DefaultSendService @AssistedInject constructor( override fun cancelSend(eventId: String) { cancelSendTracker.markLocalEchoForCancel(eventId, roomId) + // This is maybe the current task, so cancel it too + eventSenderProcessor.cancel(eventId, roomId) taskExecutor.executorScope.launch { localEchoRepository.deleteFailedEcho(roomId, eventId) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt index b8f6e52674..5014d94558 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.room.send.queue +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.matrix.android.sdk.api.auth.data.SessionParams @@ -109,10 +110,18 @@ internal class EventSenderProcessor @Inject constructor( return task } + fun cancel(eventId: String, roomId: String) { + (currentTask as? SendEventQueuedTask) + ?.takeIf { it -> it.event.eventId == eventId && it.event.roomId == roomId } + ?.cancel() + } + companion object { private const val RETRY_WAIT_TIME_MS = 10_000L } + private var currentTask: QueuedTask? = null + private var sendingQueue = LinkedBlockingQueue() private var networkAvailableLock = Object() @@ -125,6 +134,7 @@ internal class EventSenderProcessor @Inject constructor( while (!isInterrupted) { Timber.v("## SendThread wait for task to process") val task = sendingQueue.take() + .also { currentTask = it } Timber.v("## SendThread Found task to process $task") if (task.isCancelled()) { @@ -179,6 +189,10 @@ internal class EventSenderProcessor @Inject constructor( task.onTaskFailed() throw InterruptedException() } + exception is CancellationException -> { + Timber.v("## SendThread task has been cancelled") + break@retryLoop + } else -> { Timber.v("## SendThread retryLoop Un-Retryable error, try next task") // this task is in error, check next one? diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt index e5c1cf7435..9a7fcd8d91 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/QueuedTask.kt @@ -23,7 +23,13 @@ abstract class QueuedTask : Cancelable { private var hasBeenCancelled: Boolean = false - abstract suspend fun execute() + suspend fun execute() { + if (!isCancelled()) { + doExecute() + } + } + + abstract suspend fun doExecute() abstract fun onTaskFailed() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt index e2fb978cd0..8e7ba2f155 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/RedactQueuedTask.kt @@ -33,7 +33,7 @@ internal class RedactQueuedTask( override fun toString() = "[RedactQueuedTask $redactionLocalEchoId]" - override suspend fun execute() { + override suspend fun doExecute() { redactEventTask.execute(RedactEventTask.Params(redactionLocalEchoId, roomId, toRedactEventId, reason)) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt index f934aad67b..ea097082c7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/SendEventQueuedTask.kt @@ -35,7 +35,7 @@ internal class SendEventQueuedTask( override fun toString() = "[SendEventQueuedTask ${event.eventId}]" - override suspend fun execute() { + override suspend fun doExecute() { sendEventTask.execute(SendEventTask.Params(event, encrypt)) } From 42bc4d2445d2a5ee8f06a093ba8e11cc7dda992c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Dec 2020 12:08:29 +0100 Subject: [PATCH 03/42] Upgrade some dependencies and Kotlin version --- CHANGES.md | 2 +- build.gradle | 6 +++--- matrix-sdk-android/build.gradle | 2 +- multipicker/build.gradle | 2 +- vector/build.gradle | 8 ++++---- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f6f63db534..9bcd0f2eea 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,7 +20,7 @@ SDK API changes ⚠️: - Build 🧱: - - + - Upgrade some dependencies and Kotlin version Test: - diff --git a/build.gradle b/build.gradle index 0c4b35b060..6dd61a720c 100644 --- a/build.gradle +++ b/build.gradle @@ -2,8 +2,8 @@ buildscript { // Ref: https://kotlinlang.org/releases.html - ext.kotlin_version = '1.4.10' - ext.kotlin_coroutines_version = "1.3.9" + ext.kotlin_version = '1.4.20' + ext.kotlin_coroutines_version = "1.4.1" repositories { google() jcenter() @@ -12,7 +12,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:4.1.1' classpath 'com.google.gms:google-services:4.3.4' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1' diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 29c709844a..1923ab2606 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -146,7 +146,7 @@ dependencies { implementation "ru.noties.markwon:core:$markwon_version" // Image - implementation 'androidx.exifinterface:exifinterface:1.3.0' + implementation 'androidx.exifinterface:exifinterface:1.3.1' // Database implementation 'com.github.Zhuinden:realm-monarchy:0.7.1' diff --git a/multipicker/build.gradle b/multipicker/build.gradle index b6e500e493..6e22c8207e 100644 --- a/multipicker/build.gradle +++ b/multipicker/build.gradle @@ -44,7 +44,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.appcompat:appcompat:1.2.0' implementation "androidx.fragment:fragment:1.3.0-beta01" - implementation 'androidx.exifinterface:exifinterface:1.3.0' + implementation 'androidx.exifinterface:exifinterface:1.3.1' // Log implementation 'com.jakewharton.timber:timber:4.7.1' diff --git a/vector/build.gradle b/vector/build.gradle index 561e1fd824..811189c693 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -317,7 +317,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation "androidx.fragment:fragment:$fragment_version" implementation "androidx.fragment:fragment-ktx:$fragment_version" - implementation 'androidx.constraintlayout:constraintlayout:2.0.2' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation "androidx.sharetarget:sharetarget:1.0.0" implementation 'androidx.core:core-ktx:1.3.2' @@ -366,7 +366,7 @@ dependencies { // UI implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' - implementation 'com.google.android.material:material:1.3.0-alpha02' + implementation 'com.google.android.material:material:1.3.0-alpha04' implementation 'me.gujun.android:span:1.7' implementation "io.noties.markwon:core:$markwon_version" implementation "io.noties.markwon:html:$markwon_version" @@ -374,7 +374,7 @@ dependencies { implementation 'me.saket:better-link-movement-method:2.2.0' implementation 'com.google.android:flexbox:1.1.1' implementation "androidx.autofill:autofill:$autofill_version" - implementation 'com.github.vector-im:PFLockScreen-Android:1.0.0-beta10' + implementation 'com.github.vector-im:PFLockScreen-Android:1.0.0-beta12' // Custom Tab implementation 'androidx.browser:browser:1.2.0' @@ -418,7 +418,7 @@ dependencies { kapt 'com.squareup.inject:assisted-inject-processor-dagger2:0.5.0' // gplay flavor only - gplayImplementation('com.google.firebase:firebase-messaging:20.3.0') { + gplayImplementation('com.google.firebase:firebase-messaging:21.0.0') { exclude group: 'com.google.firebase', module: 'firebase-core' exclude group: 'com.google.firebase', module: 'firebase-analytics' exclude group: 'com.google.firebase', module: 'firebase-measurement-connector' From cc5264a587d704b9d1f7563cb7149f0c7eef6ee3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Dec 2020 12:38:07 +0100 Subject: [PATCH 04/42] Try to clarify the code --- .../android/sdk/rx/RxCallbackBuilders.kt | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxCallbackBuilders.kt b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxCallbackBuilders.kt index f6dbe3d160..d5bb53e9c9 100644 --- a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxCallbackBuilders.kt +++ b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxCallbackBuilders.kt @@ -21,34 +21,34 @@ import org.matrix.android.sdk.api.util.Cancelable import io.reactivex.Completable import io.reactivex.Single -fun singleBuilder(builder: (callback: MatrixCallback) -> Cancelable): Single = Single.create { - val callback: MatrixCallback = object : MatrixCallback { +fun singleBuilder(builder: (MatrixCallback) -> Cancelable): Single = Single.create { emitter -> + val callback = object : MatrixCallback { override fun onSuccess(data: T) { - it.onSuccess(data) + emitter.onSuccess(data) } override fun onFailure(failure: Throwable) { - it.tryOnError(failure) + emitter.tryOnError(failure) } } val cancelable = builder(callback) - it.setCancellable { + emitter.setCancellable { cancelable.cancel() } } -fun completableBuilder(builder: (callback: MatrixCallback) -> Cancelable): Completable = Completable.create { - val callback: MatrixCallback = object : MatrixCallback { +fun completableBuilder(builder: (MatrixCallback) -> Cancelable): Completable = Completable.create { emitter -> + val callback = object : MatrixCallback { override fun onSuccess(data: T) { - it.onComplete() + emitter.onComplete() } override fun onFailure(failure: Throwable) { - it.tryOnError(failure) + emitter.tryOnError(failure) } } val cancelable = builder(callback) - it.setCancellable { + emitter.setCancellable { cancelable.cancel() } } From c426364618b95f26716598b57e0fb2ac7691c41c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Dec 2020 12:45:31 +0100 Subject: [PATCH 05/42] Remove unused dependencies --- attachment-viewer/build.gradle | 1 - matrix-sdk-android-rx/build.gradle | 2 +- matrix-sdk-android/build.gradle | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/attachment-viewer/build.gradle b/attachment-viewer/build.gradle index 91ddd519df..59ba6c4500 100644 --- a/attachment-viewer/build.gradle +++ b/attachment-viewer/build.gradle @@ -66,7 +66,6 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.appcompat:appcompat:1.2.0' - implementation "androidx.fragment:fragment:1.3.0-beta01" implementation "androidx.recyclerview:recyclerview:1.1.0" implementation 'com.google.android.material:material:1.2.1' diff --git a/matrix-sdk-android-rx/build.gradle b/matrix-sdk-android-rx/build.gradle index 3d62758065..37f41d0a2a 100644 --- a/matrix-sdk-android-rx/build.gradle +++ b/matrix-sdk-android-rx/build.gradle @@ -36,9 +36,9 @@ android { dependencies { implementation project(":matrix-sdk-android") implementation 'androidx.appcompat:appcompat:1.2.0' - implementation "androidx.fragment:fragment:1.3.0-beta01" implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' + // Paging implementation "androidx.paging:paging-runtime-ktx:2.1.2" diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 1923ab2606..d961560c17 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -125,7 +125,6 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" implementation "androidx.appcompat:appcompat:1.2.0" - implementation "androidx.fragment:fragment:1.3.0-beta01" implementation "androidx.core:core-ktx:1.3.2" implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" From 89a7ec6d4b78208c440cd4b0bcf1b0323fe6c166 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Dec 2020 12:46:15 +0100 Subject: [PATCH 06/42] Use fragment-ktx and preference-ktx dependencies (fix lint issue KtxExtensionAvailable) --- CHANGES.md | 1 + multipicker/build.gradle | 2 +- vector/build.gradle | 2 +- vector/lint.xml | 3 +++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 9bcd0f2eea..b16a6690bc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,7 @@ SDK API changes ⚠️: Build 🧱: - Upgrade some dependencies and Kotlin version + - Use fragment-ktx and preference-ktx dependencies (fix lint issue KtxExtensionAvailable) Test: - diff --git a/multipicker/build.gradle b/multipicker/build.gradle index 6e22c8207e..7c29a5539f 100644 --- a/multipicker/build.gradle +++ b/multipicker/build.gradle @@ -43,7 +43,7 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.appcompat:appcompat:1.2.0' - implementation "androidx.fragment:fragment:1.3.0-beta01" + implementation "androidx.fragment:fragment-ktx:1.3.0-beta01" implementation 'androidx.exifinterface:exifinterface:1.3.1' // Log diff --git a/vector/build.gradle b/vector/build.gradle index 811189c693..c4164255db 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -362,7 +362,7 @@ dependencies { implementation "io.arrow-kt:arrow-core:$arrow_version" // Pref - implementation 'androidx.preference:preference:1.1.1' + implementation 'androidx.preference:preference-ktx:1.1.1' // UI implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' diff --git a/vector/lint.xml b/vector/lint.xml index 4ac0f20e51..51d795b669 100644 --- a/vector/lint.xml +++ b/vector/lint.xml @@ -52,6 +52,9 @@ + + + From 9881c9f61c7e86f909cc034a61e85f4bfb78892c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Dec 2020 13:34:34 +0100 Subject: [PATCH 07/42] Fix compilation issue after library upgrade --- .../main/java/org/matrix/android/sdk/rx/RxCallbackBuilders.kt | 4 +++- .../src/main/java/im/vector/app/features/pin/PinFragment.kt | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxCallbackBuilders.kt b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxCallbackBuilders.kt index d5bb53e9c9..ec30a31f6d 100644 --- a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxCallbackBuilders.kt +++ b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxCallbackBuilders.kt @@ -24,7 +24,9 @@ import io.reactivex.Single fun singleBuilder(builder: (MatrixCallback) -> Cancelable): Single = Single.create { emitter -> val callback = object : MatrixCallback { override fun onSuccess(data: T) { - emitter.onSuccess(data) + // Add `!!` to fix the warning: + // "Type mismatch: type parameter with nullable bounds is used T is used where T was expected. This warning will become an error soon" + emitter.onSuccess(data!!) } override fun onFailure(failure: Throwable) { diff --git a/vector/src/main/java/im/vector/app/features/pin/PinFragment.kt b/vector/src/main/java/im/vector/app/features/pin/PinFragment.kt index 378c7b853d..1aa4846f38 100644 --- a/vector/src/main/java/im/vector/app/features/pin/PinFragment.kt +++ b/vector/src/main/java/im/vector/app/features/pin/PinFragment.kt @@ -74,6 +74,10 @@ class PinFragment @Inject constructor( Toast.makeText(requireContext(), getString(R.string.create_pin_confirm_failure), Toast.LENGTH_SHORT).show() } + override fun onPinCodeEnteredFirst(pinCode: String?): Boolean { + return false + } + override fun onCodeCreated(encodedCode: String) { lifecycleScope.launch { pinCodeStore.storeEncodedPin(encodedCode) From f0afd5ceea2f359f46ed4b2879b71d942ffd4d67 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Dec 2020 13:49:07 +0100 Subject: [PATCH 08/42] Update deprecated code --- .../settings/troubleshoot/TestFirebaseToken.kt | 6 +++--- .../gplay/java/im/vector/app/push/fcm/FcmHelper.kt | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt index 32888dafd7..fd541c0528 100644 --- a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt @@ -18,7 +18,7 @@ package im.vector.app.gplay.features.settings.troubleshoot import android.content.Intent import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.app.AppCompatActivity -import com.google.firebase.iid.FirebaseInstanceId +import com.google.firebase.messaging.FirebaseMessaging import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.startAddGoogleAccountIntent @@ -36,7 +36,7 @@ class TestFirebaseToken @Inject constructor(private val context: AppCompatActivi override fun perform(activityResultLauncher: ActivityResultLauncher) { status = TestStatus.RUNNING try { - FirebaseInstanceId.getInstance().instanceId + FirebaseMessaging.getInstance().token .addOnCompleteListener(context) { task -> if (!task.isSuccessful) { val errorMsg = if (task.exception == null) "Unknown" else task.exception!!.localizedMessage @@ -57,7 +57,7 @@ class TestFirebaseToken @Inject constructor(private val context: AppCompatActivi } status = TestStatus.FAILED } else { - task.result?.token?.let { token -> + task.result?.let { token -> val tok = token.substring(0, Math.min(8, token.length)) + "********************" description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_success, tok) Timber.e("Retrieved FCM token success [$tok].") diff --git a/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt b/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt index 913eab211d..f3bdcafb1c 100755 --- a/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt +++ b/vector/src/gplay/java/im/vector/app/push/fcm/FcmHelper.kt @@ -21,7 +21,7 @@ import android.widget.Toast import androidx.core.content.edit import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability -import com.google.firebase.iid.FirebaseInstanceId +import com.google.firebase.messaging.FirebaseMessaging import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.DefaultSharedPreferences @@ -71,14 +71,16 @@ object FcmHelper { // 'app should always check the device for a compatible Google Play services APK before accessing Google Play services features' if (checkPlayServices(activity)) { try { - FirebaseInstanceId.getInstance().instanceId - .addOnSuccessListener(activity) { instanceIdResult -> - storeFcmToken(activity, instanceIdResult.token) + FirebaseMessaging.getInstance().token + .addOnSuccessListener { token -> + storeFcmToken(activity, token) if (registerPusher) { - pushersManager.registerPusherWithFcmKey(instanceIdResult.token) + pushersManager.registerPusherWithFcmKey(token) } } - .addOnFailureListener(activity) { e -> Timber.e(e, "## ensureFcmTokenIsRetrieved() : failed") } + .addOnFailureListener { e -> + Timber.e(e, "## ensureFcmTokenIsRetrieved() : failed") + } } catch (e: Throwable) { Timber.e(e, "## ensureFcmTokenIsRetrieved() : failed") } From 1058bfecf463133bc255bc2b2fed2913e2ad4706 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Dec 2020 13:54:59 +0100 Subject: [PATCH 09/42] Kotlinification --- .../troubleshoot/TestFirebaseToken.kt | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt index fd541c0528..1107737888 100644 --- a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestFirebaseToken.kt @@ -39,26 +39,30 @@ class TestFirebaseToken @Inject constructor(private val context: AppCompatActivi FirebaseMessaging.getInstance().token .addOnCompleteListener(context) { task -> if (!task.isSuccessful) { - val errorMsg = if (task.exception == null) "Unknown" else task.exception!!.localizedMessage // Can't find where this constant is (not documented -or deprecated in docs- and all obfuscated) - if ("SERVICE_NOT_AVAILABLE".equals(errorMsg)) { - description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_service_not_available, errorMsg) - } else if ("TOO_MANY_REGISTRATIONS".equals(errorMsg)) { - description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_too_many_registration, errorMsg) - } else if ("ACCOUNT_MISSING".equals(errorMsg)) { - description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_account_missing, errorMsg) - quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_fcm_failed_account_missing_quick_fix) { - override fun doFix() { - startAddGoogleAccountIntent(context, activityResultLauncher) - } + description = when (val errorMsg = task.exception?.localizedMessage ?: "Unknown") { + "SERVICE_NOT_AVAILABLE" -> { + stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_service_not_available, errorMsg) + } + "TOO_MANY_REGISTRATIONS" -> { + stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_too_many_registration, errorMsg) + } + "ACCOUNT_MISSING" -> { + quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_fcm_failed_account_missing_quick_fix) { + override fun doFix() { + startAddGoogleAccountIntent(context, activityResultLauncher) + } + } + stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_account_missing, errorMsg) + } + else -> { + stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed, errorMsg) } - } else { - description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed, errorMsg) } status = TestStatus.FAILED } else { task.result?.let { token -> - val tok = token.substring(0, Math.min(8, token.length)) + "********************" + val tok = token.take(8) + "********************" description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_success, tok) Timber.e("Retrieved FCM token success [$tok].") // Ensure it is well store in our local storage From 6c56b5f45b4d9b6aa5e6e7286520a43e13fcae07 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Dec 2020 14:39:33 +0100 Subject: [PATCH 10/42] Fix warnings after library upgrade --- .../main/java/im/vector/app/core/platform/VectorViewModel.kt | 4 ++-- vector/src/main/java/im/vector/app/core/utils/DataSource.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt index 002dfcf068..d6f43beaf7 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt @@ -43,7 +43,7 @@ abstract class VectorViewModel Single.toAsync(stateReducer: S.(Async) -> S): Single> { setState { stateReducer(Loading()) } return map { Success(it) as Async } @@ -56,7 +56,7 @@ abstract class VectorViewModel Observable.toAsync(stateReducer: S.(Async) -> S): Observable> { setState { stateReducer(Loading()) } return map { Success(it) as Async } diff --git a/vector/src/main/java/im/vector/app/core/utils/DataSource.kt b/vector/src/main/java/im/vector/app/core/utils/DataSource.kt index 8a908ad1d4..06bdeb9277 100644 --- a/vector/src/main/java/im/vector/app/core/utils/DataSource.kt +++ b/vector/src/main/java/im/vector/app/core/utils/DataSource.kt @@ -44,7 +44,7 @@ open class BehaviorDataSource(private val defaultValue: T? = null) : MutableD } override fun post(value: T) { - behaviorRelay.accept(value) + behaviorRelay.accept(value!!) } private fun createRelay(): BehaviorRelay { @@ -68,6 +68,6 @@ open class PublishDataSource : MutableDataSource { } override fun post(value: T) { - publishRelay.accept(value) + publishRelay.accept(value!!) } } From ee96d5c68f4ed3ecc4bb0f0af7763e5f204bccac Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Dec 2020 14:59:09 +0100 Subject: [PATCH 11/42] Remove useless Factories --- .../preview/AttachmentsPreviewFragment.kt | 1 - .../preview/AttachmentsPreviewViewModel.kt | 21 +------------------ .../RoomHistoryVisibilityBottomSheet.kt | 1 - .../RoomHistoryVisibilityViewModel.kt | 20 +----------------- .../joinrule/RoomJoinRuleBottomSheet.kt | 1 - .../joinrule/RoomJoinRuleViewModel.kt | 20 +----------------- 6 files changed, 3 insertions(+), 61 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt index ba0250724c..f67b0946cc 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewFragment.kt @@ -59,7 +59,6 @@ data class AttachmentsPreviewArgs( ) : Parcelable class AttachmentsPreviewFragment @Inject constructor( - val viewModelFactory: AttachmentsPreviewViewModel.Factory, private val attachmentMiniaturePreviewController: AttachmentMiniaturePreviewController, private val attachmentBigPreviewController: AttachmentBigPreviewController, private val colorProvider: ColorProvider diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewViewModel.kt index 59a0937d89..28d617e613 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewViewModel.kt @@ -17,31 +17,12 @@ package im.vector.app.features.attachments.preview -import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory -import com.airbnb.mvrx.ViewModelContext -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel -class AttachmentsPreviewViewModel @AssistedInject constructor(@Assisted initialState: AttachmentsPreviewViewState) +class AttachmentsPreviewViewModel(initialState: AttachmentsPreviewViewState) : VectorViewModel(initialState) { - @AssistedInject.Factory - interface Factory { - fun create(initialState: AttachmentsPreviewViewState): AttachmentsPreviewViewModel - } - - companion object : MvRxViewModelFactory { - - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: AttachmentsPreviewViewState): AttachmentsPreviewViewModel? { - val fragment: AttachmentsPreviewFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewModelFactory.create(state) - } - } - override fun handle(action: AttachmentsPreviewAction) { when (action) { is AttachmentsPreviewAction.SetCurrentAttachment -> handleSetCurrentAttachment(action) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt index 813a58838c..c12dc621a9 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityBottomSheet.kt @@ -37,7 +37,6 @@ class RoomHistoryVisibilityBottomSheet : BottomSheetGeneric(initialState) { - @AssistedInject.Factory - interface Factory { - fun create(initialState: RoomHistoryVisibilityState): RoomHistoryVisibilityViewModel - } - - companion object : MvRxViewModelFactory { - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomHistoryVisibilityState): RoomHistoryVisibilityViewModel? { - val fragment: RoomHistoryVisibilityBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.roomHistoryVisibilityViewModelFactory.create(state) - } - } - override fun handle(action: EmptyAction) { // No op } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt index 4996187029..66c6be6086 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt @@ -39,7 +39,6 @@ class RoomJoinRuleBottomSheet : BottomSheetGeneric(initialState) { - @AssistedInject.Factory - interface Factory { - fun create(initialState: RoomJoinRuleState): RoomJoinRuleViewModel - } - - companion object : MvRxViewModelFactory { - @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: RoomJoinRuleState): RoomJoinRuleViewModel? { - val fragment: RoomJoinRuleBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.roomJoinRuleViewModelFactory.create(state) - } - } - override fun handle(action: EmptyAction) { // No op } From f5af15454e71e271ff25fa9b234616c33031a5a8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Dec 2020 15:47:21 +0100 Subject: [PATCH 12/42] Create a generic class for ViewModel --- .../BottomSheetGenericViewModel.kt | 30 +++++++++++++++++++ .../RoomHistoryVisibilityViewModel.kt | 11 ++----- .../joinrule/RoomJoinRuleViewModel.kt | 11 ++----- 3 files changed, 34 insertions(+), 18 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericViewModel.kt diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericViewModel.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericViewModel.kt new file mode 100644 index 0000000000..6cc2c4c981 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericViewModel.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.core.ui.bottomsheet + +import com.airbnb.mvrx.MvRxState +import im.vector.app.core.platform.EmptyAction +import im.vector.app.core.platform.EmptyViewEvents +import im.vector.app.core.platform.VectorViewModel + +abstract class BottomSheetGenericViewModel(initialState: State) : + VectorViewModel(initialState) { + + override fun handle(action: EmptyAction) { + // No op + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityViewModel.kt index 252865e4c5..c2a8ae967f 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityViewModel.kt @@ -16,14 +16,7 @@ package im.vector.app.features.roomprofile.settings.historyvisibility -import im.vector.app.core.platform.EmptyAction -import im.vector.app.core.platform.EmptyViewEvents -import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.ui.bottomsheet.BottomSheetGenericViewModel class RoomHistoryVisibilityViewModel(initialState: RoomHistoryVisibilityState) - : VectorViewModel(initialState) { - - override fun handle(action: EmptyAction) { - // No op - } -} + : BottomSheetGenericViewModel(initialState) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleViewModel.kt index 28c6f99f7c..4305bfa72d 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleViewModel.kt @@ -16,14 +16,7 @@ package im.vector.app.features.roomprofile.settings.joinrule -import im.vector.app.core.platform.EmptyAction -import im.vector.app.core.platform.EmptyViewEvents -import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.ui.bottomsheet.BottomSheetGenericViewModel class RoomJoinRuleViewModel(initialState: RoomJoinRuleState) - : VectorViewModel(initialState) { - - override fun handle(action: EmptyAction) { - // No op - } -} + : BottomSheetGenericViewModel(initialState) From c4577f28b286a8420c7f26b1ac55d9efc3210750 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Dec 2020 16:43:52 +0100 Subject: [PATCH 13/42] Remove unnecessary dependency (we have -ktx dependency) --- vector/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/build.gradle b/vector/build.gradle index c4164255db..62ff3951af 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -315,7 +315,6 @@ dependencies { implementation "androidx.recyclerview:recyclerview:1.2.0-alpha06" implementation 'androidx.appcompat:appcompat:1.2.0' - implementation "androidx.fragment:fragment:$fragment_version" implementation "androidx.fragment:fragment-ktx:$fragment_version" implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation "androidx.sharetarget:sharetarget:1.0.0" From d889598b20d251929da3639904629ef368746886 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 2 Dec 2020 17:04:54 +0100 Subject: [PATCH 14/42] Fix DefaultLocale lint issue --- .../SASDefaultVerificationTransaction.kt | 12 ++++++------ .../org/matrix/android/sdk/internal/util/Hash.kt | 3 ++- vector/lint.xml | 1 + .../java/im/vector/app/core/intent/VectorMimeType.kt | 3 ++- .../main/java/im/vector/app/core/utils/FileUtils.kt | 3 ++- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt index 22a190c68e..c7885ce449 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SASDefaultVerificationTransaction.kt @@ -31,6 +31,7 @@ import org.matrix.android.sdk.internal.extensions.toUnsignedInt import org.matrix.olm.OlmSAS import org.matrix.olm.OlmUtility import timber.log.Timber +import java.util.Locale /** * Represents an ongoing short code interactive key verification between two devices. @@ -344,7 +345,7 @@ internal abstract class SASDefaultVerificationTransaction( } protected fun hashUsingAgreedHashMethod(toHash: String): String? { - if ("sha256".toLowerCase() == accepted?.hash?.toLowerCase()) { + if ("sha256" == accepted?.hash?.toLowerCase(Locale.ROOT)) { val olmUtil = OlmUtility() val hashBytes = olmUtil.sha256(toHash) olmUtil.releaseUtility() @@ -354,12 +355,11 @@ internal abstract class SASDefaultVerificationTransaction( } private fun macUsingAgreedMethod(message: String, info: String): String? { - if (SAS_MAC_SHA256_LONGKDF.toLowerCase() == accepted?.messageAuthenticationCode?.toLowerCase()) { - return getSAS().calculateMacLongKdf(message, info) - } else if (SAS_MAC_SHA256.toLowerCase() == accepted?.messageAuthenticationCode?.toLowerCase()) { - return getSAS().calculateMac(message, info) + return when (accepted?.messageAuthenticationCode?.toLowerCase(Locale.ROOT)) { + SAS_MAC_SHA256_LONGKDF -> getSAS().calculateMacLongKdf(message, info) + SAS_MAC_SHA256 -> getSAS().calculateMac(message, info) + else -> null } - return null } override fun getDecimalCodeRepresentation(): String { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Hash.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Hash.kt index 3d80ad01d5..e19b1bcca7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Hash.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Hash.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.util import java.security.MessageDigest +import java.util.Locale /** * Compute a Hash of a String, using md5 algorithm @@ -26,7 +27,7 @@ fun String.md5() = try { digest.update(toByteArray()) digest.digest() .joinToString("") { String.format("%02X", it) } - .toLowerCase() + .toLowerCase(Locale.ROOT) } catch (exc: Exception) { // Should not happen, but just in case hashCode().toString() diff --git a/vector/lint.xml b/vector/lint.xml index 51d795b669..572f937406 100644 --- a/vector/lint.xml +++ b/vector/lint.xml @@ -41,6 +41,7 @@ + diff --git a/vector/src/main/java/im/vector/app/core/intent/VectorMimeType.kt b/vector/src/main/java/im/vector/app/core/intent/VectorMimeType.kt index c8a2bf65d5..1299f4086b 100644 --- a/vector/src/main/java/im/vector/app/core/intent/VectorMimeType.kt +++ b/vector/src/main/java/im/vector/app/core/intent/VectorMimeType.kt @@ -21,6 +21,7 @@ import android.net.Uri import android.webkit.MimeTypeMap import im.vector.app.core.utils.getFileExtension import timber.log.Timber +import java.util.Locale /** * Returns the mimetype from a uri. @@ -44,7 +45,7 @@ fun getMimeTypeFromUri(context: Context, uri: Uri): String? { if (null != mimeType) { // the mimetype is sometimes in uppercase. - mimeType = mimeType.toLowerCase() + mimeType = mimeType.toLowerCase(Locale.ROOT) } } catch (e: Exception) { Timber.e(e, "Failed to open resource input stream") diff --git a/vector/src/main/java/im/vector/app/core/utils/FileUtils.kt b/vector/src/main/java/im/vector/app/core/utils/FileUtils.kt index ab99ba61bd..aa36dd0959 100644 --- a/vector/src/main/java/im/vector/app/core/utils/FileUtils.kt +++ b/vector/src/main/java/im/vector/app/core/utils/FileUtils.kt @@ -19,6 +19,7 @@ package im.vector.app.core.utils import android.content.Context import timber.log.Timber import java.io.File +import java.util.Locale // Implementation should return true in case of success typealias ActionOnFile = (file: File) -> Boolean @@ -113,7 +114,7 @@ fun getFileExtension(fileUri: String): String? { val ext = filename.substring(dotPos + 1) if (ext.isNotBlank()) { - return ext.toLowerCase() + return ext.toLowerCase(Locale.ROOT) } } } From f3bc39a0c57c7f28d71313f6fa26a8e24164de49 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 8 Dec 2020 11:14:55 +0100 Subject: [PATCH 15/42] 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(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 Date: Tue, 8 Dec 2020 11:31:16 +0100 Subject: [PATCH 16/42] 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 Date: Tue, 8 Dec 2020 11:36:12 +0100 Subject: [PATCH 17/42] 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 Date: Tue, 8 Dec 2020 11:57:18 +0100 Subject: [PATCH 18/42] 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 executeRequest(eventBus: EventBus?, @@ -49,6 +50,9 @@ internal class Request(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 Date: Tue, 8 Dec 2020 13:33:01 +0100 Subject: [PATCH 19/42] 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 Date: Tue, 8 Dec 2020 16:47:29 +0100 Subject: [PATCH 20/42] 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 Date: Tue, 8 Dec 2020 17:20:40 +0100 Subject: [PATCH 21/42] 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( } 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 { 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 { 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 Date: Tue, 8 Dec 2020 17:37:24 +0100 Subject: [PATCH 22/42] 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 { + private fun createGlideRequest(data: Data, mode: Mode, imageView: ImageView, size: Size): GlideRequest { return createGlideRequest(data, mode, GlideApp.with(imageView), size) } fun createGlideRequest(data: Data, mode: Mode, glideRequests: GlideRequests, size: Size = processSize(data, mode)): GlideRequest { 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 Date: Tue, 8 Dec 2020 17:42:42 +0100 Subject: [PATCH 23/42] 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 Date: Tue, 8 Dec 2020 18:04:42 +0100 Subject: [PATCH 24/42] 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 Date: Tue, 8 Dec 2020 18:35:17 +0100 Subject: [PATCH 25/42] 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 Date: Tue, 8 Dec 2020 19:31:29 +0100 Subject: [PATCH 26/42] 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): Cancelable - fun isFileInCache(mxcUrl: String, mimeType: String?): Boolean + fun downloadFile( + id: String, + messageContent: MessageWithAttachmentContent, + callback: MatrixCallback): 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 { 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 { 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 { 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 { 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 { 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 Date: Wed, 9 Dec 2020 10:50:21 +0100 Subject: [PATCH 27/42] 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): 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 Date: Wed, 9 Dec 2020 12:20:48 +0100 Subject: [PATCH 28/42] 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 Date: Wed, 9 Dec 2020 12:26:49 +0100 Subject: [PATCH 29/42] 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 Date: Wed, 9 Dec 2020 12:27:03 +0100 Subject: [PATCH 30/42] 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 Date: Wed, 9 Dec 2020 12:27:37 +0100 Subject: [PATCH 31/42] 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 Date: Wed, 9 Dec 2020 12:49:25 +0100 Subject: [PATCH 32/42] 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 Date: Wed, 9 Dec 2020 13:50:14 +0100 Subject: [PATCH 33/42] 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 d54571d0a6a768997327c053d7231a8ba1adcc54 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 9 Dec 2020 18:45:33 +0300 Subject: [PATCH 34/42] Emoji library added with Google style. --- vector/build.gradle | 4 ++++ vector/src/main/java/im/vector/app/VectorApplication.kt | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/vector/build.gradle b/vector/build.gradle index 62ff3951af..28d8fe5c1b 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -440,6 +440,10 @@ dependencies { implementation 'com.google.zxing:core:3.3.3' implementation 'me.dm7.barcodescanner:zxing:1.9.13' + // Emoji Keyboard + implementation 'com.vanniktech:emoji-material:0.7.0' + implementation 'com.vanniktech:emoji-google:0.7.0' + // TESTS testImplementation 'junit:junit:4.13' testImplementation "org.amshove.kluent:kluent-android:$kluent_version" diff --git a/vector/src/main/java/im/vector/app/VectorApplication.kt b/vector/src/main/java/im/vector/app/VectorApplication.kt index 5be313d719..921e8c0780 100644 --- a/vector/src/main/java/im/vector/app/VectorApplication.kt +++ b/vector/src/main/java/im/vector/app/VectorApplication.kt @@ -36,6 +36,8 @@ import com.airbnb.epoxy.EpoxyAsyncUtil import com.airbnb.epoxy.EpoxyController import com.facebook.stetho.Stetho import com.gabrielittner.threetenbp.LazyThreeTen +import com.vanniktech.emoji.EmojiManager +import com.vanniktech.emoji.google.GoogleEmojiProvider import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.DaggerVectorComponent import im.vector.app.core.di.HasVectorInjector @@ -184,6 +186,8 @@ class VectorApplication : addAction(Intent.ACTION_SCREEN_OFF) addAction(Intent.ACTION_SCREEN_ON) }) + + EmojiManager.install(GoogleEmojiProvider()) } private fun enableStrictModeIfNeeded() { From 3d975b7fba2716fa385429a82eefc7200f06df62 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 9 Dec 2020 18:46:33 +0300 Subject: [PATCH 35/42] Update composer icons. --- vector/src/main/res/drawable/bg_send.xml | 6 +++++ .../src/main/res/drawable/ic_attachment.xml | 26 ++++++++++++++----- .../src/main/res/drawable/ic_insert_emoji.xml | 21 +++++++++++++++ vector/src/main/res/drawable/ic_keyboard.xml | 4 +++ 4 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 vector/src/main/res/drawable/bg_send.xml create mode 100644 vector/src/main/res/drawable/ic_insert_emoji.xml create mode 100644 vector/src/main/res/drawable/ic_keyboard.xml diff --git a/vector/src/main/res/drawable/bg_send.xml b/vector/src/main/res/drawable/bg_send.xml new file mode 100644 index 0000000000..6b12664032 --- /dev/null +++ b/vector/src/main/res/drawable/bg_send.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/vector/src/main/res/drawable/ic_attachment.xml b/vector/src/main/res/drawable/ic_attachment.xml index 944998c1cd..ea01e94372 100644 --- a/vector/src/main/res/drawable/ic_attachment.xml +++ b/vector/src/main/res/drawable/ic_attachment.xml @@ -1,7 +1,21 @@ - - + + + + diff --git a/vector/src/main/res/drawable/ic_insert_emoji.xml b/vector/src/main/res/drawable/ic_insert_emoji.xml new file mode 100644 index 0000000000..c35000342b --- /dev/null +++ b/vector/src/main/res/drawable/ic_insert_emoji.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/vector/src/main/res/drawable/ic_keyboard.xml b/vector/src/main/res/drawable/ic_keyboard.xml new file mode 100644 index 0000000000..5e5d431abb --- /dev/null +++ b/vector/src/main/res/drawable/ic_keyboard.xml @@ -0,0 +1,4 @@ + + + From a96cc19eb600aa8857ec46d82fa87f06059834fd Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Wed, 9 Dec 2020 18:47:34 +0300 Subject: [PATCH 36/42] Update composer layout by adding emoji icon. --- ...constraint_set_composer_layout_compact.xml | 69 ++++++++--------- ...onstraint_set_composer_layout_expanded.xml | 76 +++++++++---------- .../main/res/layout/merge_composer_layout.xml | 18 ++--- 3 files changed, 78 insertions(+), 85 deletions(-) diff --git a/vector/src/main/res/layout/constraint_set_composer_layout_compact.xml b/vector/src/main/res/layout/constraint_set_composer_layout_compact.xml index ac67db6a64..6b9fbd4885 100644 --- a/vector/src/main/res/layout/constraint_set_composer_layout_compact.xml +++ b/vector/src/main/res/layout/constraint_set_composer_layout_compact.xml @@ -77,9 +77,9 @@ android:alpha="0" app:layout_constraintEnd_toStartOf="parent" app:layout_constraintTop_toBottomOf="parent" + app:tint="?riotx_text_primary" tools:ignore="MissingConstraints,MissingPrefix" - tools:src="@drawable/ic_edit" - app:tint="?riotx_text_primary" /> + tools:src="@drawable/ic_edit" /> + app:tint="@color/riotx_notice" + tools:ignore="MissingPrefix" + tools:visibility="visible" /> - - + tools:ignore="MissingPrefix" /> - + tools:visibility="visible" /> + app:layout_constraintTop_toTopOf="@id/sendButton" + app:layout_goneMarginEnd="8dp" /> diff --git a/vector/src/main/res/layout/constraint_set_composer_layout_expanded.xml b/vector/src/main/res/layout/constraint_set_composer_layout_expanded.xml index dba996309e..f52b072ece 100644 --- a/vector/src/main/res/layout/constraint_set_composer_layout_expanded.xml +++ b/vector/src/main/res/layout/constraint_set_composer_layout_expanded.xml @@ -83,9 +83,9 @@ app:layout_constraintEnd_toEndOf="@id/composer_related_message_avatar_view" app:layout_constraintStart_toStartOf="@id/composer_related_message_avatar_view" app:layout_constraintTop_toBottomOf="@id/composer_related_message_avatar_view" - tools:src="@drawable/ic_edit" app:tint="?riotx_text_primary" - tools:ignore="MissingPrefix" /> + tools:ignore="MissingPrefix" + tools:src="@drawable/ic_edit" /> - - - - - - - - + + + + diff --git a/vector/src/main/res/layout/merge_composer_layout.xml b/vector/src/main/res/layout/merge_composer_layout.xml index 908b3f009b..ea2bc1bf30 100644 --- a/vector/src/main/res/layout/merge_composer_layout.xml +++ b/vector/src/main/res/layout/merge_composer_layout.xml @@ -70,8 +70,8 @@ android:id="@+id/composer_related_message_action_image" android:layout_width="0dp" android:layout_height="0dp" - tools:ignore="MissingConstraints,MissingPrefix" - app:tint="?riotx_text_primary" /> + app:tint="?riotx_text_primary" + tools:ignore="MissingConstraints,MissingPrefix" /> - - + android:background="?android:attr/selectableItemBackground" + android:src="@drawable/ic_insert_emoji" + tools:ignore="MissingConstraints" /> Date: Wed, 9 Dec 2020 18:48:14 +0300 Subject: [PATCH 37/42] Add emoji keyboard, remove profile picture from composer. --- .../home/room/detail/RoomDetailFragment.kt | 24 ++++++++++++++++--- .../room/detail/composer/ComposerEditText.kt | 4 ++-- .../room/detail/composer/TextComposerView.kt | 4 ++-- 3 files changed, 25 insertions(+), 7 deletions(-) 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..f880b632d4 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 @@ -69,6 +69,7 @@ import com.airbnb.mvrx.withState import com.google.android.material.snackbar.Snackbar import com.google.android.material.textfield.TextInputEditText import com.jakewharton.rxbinding3.widget.textChanges +import com.vanniktech.emoji.EmojiPopup import im.vector.app.R import im.vector.app.core.dialogs.ConfirmationDialogBuilder import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper @@ -296,6 +297,8 @@ class RoomDetailFragment @Inject constructor( private var lockSendButton = false private val activeCallViewHolder = ActiveCallViewHolder() + private lateinit var emojiPopup: EmojiPopup + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) @@ -311,6 +314,7 @@ class RoomDetailFragment @Inject constructor( setupActiveCallView() setupJumpToBottomView() setupConfBannerView() + setupEmojiPopup() roomToolbarContentView.debouncedClicks { navigator.openRoomProfile(requireActivity(), roomDetailArgs.roomId) @@ -478,6 +482,23 @@ class RoomDetailFragment @Inject constructor( } } + private fun setupEmojiPopup() { + EmojiPopup + .Builder + .fromRootView(rootConstraintLayout) + .setKeyboardAnimationStyle(R.style.emoji_fade_animation_style) + .setOnEmojiPopupShownListener { composerLayout.composerEmojiButton.setImageResource(R.drawable.ic_keyboard) } + .setOnEmojiPopupDismissListener { composerLayout.composerEmojiButton.setImageResource(R.drawable.ic_insert_emoji) } + .build(composerLayout.composerEditText) + .also { + emojiPopup = it + } + + composerLayout.composerEmojiButton.debouncedClicks { + emojiPopup.toggle() + } + } + private fun joinJitsiRoom(jitsiWidget: Widget, enableVideo: Boolean) { navigator.openRoomWidget(requireContext(), roomDetailArgs.roomId, jitsiWidget, mapOf(JitsiCallViewModel.ENABLE_VIDEO_OPTION to enableVideo)) } @@ -1211,9 +1232,6 @@ class RoomDetailFragment @Inject constructor( scrollOnHighlightedEventCallback.timeline = roomDetailViewModel.timeline timelineEventController.update(state) inviteView.visibility = View.GONE - val uid = session.myUserId - val meMember = state.myRoomMember() - avatarRenderer.render(MatrixItem.UserItem(uid, meMember?.displayName, meMember?.avatarUrl), composerLayout.composerAvatarImageView) if (state.tombstoneEvent == null) { if (state.canSendMessage) { composerLayout.visibility = View.VISIBLE diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/ComposerEditText.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/ComposerEditText.kt index 353ab783db..2257e5ee81 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/ComposerEditText.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/ComposerEditText.kt @@ -24,16 +24,16 @@ import android.text.Editable import android.util.AttributeSet import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputConnection -import androidx.appcompat.widget.AppCompatEditText import androidx.core.view.inputmethod.EditorInfoCompat import androidx.core.view.inputmethod.InputConnectionCompat +import com.vanniktech.emoji.EmojiEditText import im.vector.app.core.extensions.ooi import im.vector.app.core.platform.SimpleTextWatcher import im.vector.app.features.html.PillImageSpan import timber.log.Timber class ComposerEditText @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.editTextStyle) - : AppCompatEditText(context, attrs, defStyleAttr) { + : EmojiEditText(context, attrs, defStyleAttr) { interface Callback { fun onRichContentSelected(contentUri: Uri): Boolean diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt index f4b14571c0..af0e1a91f0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt @@ -72,8 +72,8 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib @BindView(R.id.composerEditText) lateinit var composerEditText: ComposerEditText - @BindView(R.id.composer_avatar_view) - lateinit var composerAvatarImageView: ImageView + @BindView(R.id.composer_emoji) + lateinit var composerEmojiButton: ImageButton @BindView(R.id.composer_shield) lateinit var composerShieldImageView: ImageView From 08964d8548bbf1544885e950b5bcdf73b724a6d8 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 10 Dec 2020 12:51:15 +0300 Subject: [PATCH 38/42] Fix emoji keyboard orientation bug. --- .../home/room/detail/RoomDetailFragment.kt | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) 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 f880b632d4..bc0f5ce6dc 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 @@ -53,7 +53,6 @@ import androidx.lifecycle.Observer import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import butterknife.BindView import com.airbnb.epoxy.EpoxyModel import com.airbnb.epoxy.OnModelBuildFinishedListener import com.airbnb.epoxy.addGlidePreloader @@ -290,15 +289,11 @@ class RoomDetailFragment @Inject constructor( private lateinit var attachmentsHelper: AttachmentsHelper private lateinit var keyboardStateUtils: KeyboardStateUtils - @BindView(R.id.composerLayout) - lateinit var composerLayout: TextComposerView private lateinit var attachmentTypeSelector: AttachmentTypeSelectorView private var lockSendButton = false private val activeCallViewHolder = ActiveCallViewHolder() - private lateinit var emojiPopup: EmojiPopup - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) @@ -483,16 +478,13 @@ class RoomDetailFragment @Inject constructor( } private fun setupEmojiPopup() { - EmojiPopup + val emojiPopup = EmojiPopup .Builder .fromRootView(rootConstraintLayout) .setKeyboardAnimationStyle(R.style.emoji_fade_animation_style) - .setOnEmojiPopupShownListener { composerLayout.composerEmojiButton.setImageResource(R.drawable.ic_keyboard) } - .setOnEmojiPopupDismissListener { composerLayout.composerEmojiButton.setImageResource(R.drawable.ic_insert_emoji) } + .setOnEmojiPopupShownListener { composerLayout?.composerEmojiButton?.setImageResource(R.drawable.ic_keyboard) } + .setOnEmojiPopupDismissListener { composerLayout?.composerEmojiButton?.setImageResource(R.drawable.ic_insert_emoji) } .build(composerLayout.composerEditText) - .also { - emojiPopup = it - } composerLayout.composerEmojiButton.debouncedClicks { emojiPopup.toggle() From a9f5ed3869e412136f89a8d66d5cf54781c774d9 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Thu, 10 Dec 2020 12:56:09 +0300 Subject: [PATCH 39/42] Add emoji license. --- vector/src/main/assets/open_source_licenses.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vector/src/main/assets/open_source_licenses.html b/vector/src/main/assets/open_source_licenses.html index ac80b0d691..acf0bec14f 100755 --- a/vector/src/main/assets/open_source_licenses.html +++ b/vector/src/main/assets/open_source_licenses.html @@ -347,11 +347,6 @@ SOFTWARE.
Copyright 2017 Gabriel Ittner. -
  • - Android-multipicker-library -
    - Copyright 2018 Kumar Bibek -
  • htmlcompressor
    @@ -390,6 +385,11 @@ SOFTWARE.
    Copyright 2018, Aleksandr Nikiforov
  • +
  • + Emoji +
    + Copyright (C) 2016 - Niklas Baudy, Ruben Gees, Mario ĐaniΔ‡ and contributors +
  •  Apache License
    
    From 4007982db6df660bd9d3e9bc401249504f773f77 Mon Sep 17 00:00:00 2001
    From: Onuray Sahin 
    Date: Thu, 10 Dec 2020 12:59:09 +0300
    Subject: [PATCH 40/42] Changelog added.
    
    ---
     CHANGES.md | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/CHANGES.md b/CHANGES.md
    index 1005040328..1381a1e7ca 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)
    + - Emoji Keyboard (#2520)
     
     Improvements πŸ™Œ:
      - Add Setting Item to Change PIN (#2462)
    
    From c1cb23d728296cf71c3b97f0bd67f0d548603b54 Mon Sep 17 00:00:00 2001
    From: Onuray Sahin 
    Date: Thu, 10 Dec 2020 13:12:19 +0300
    Subject: [PATCH 41/42] Fix ripple effect of the send button.
    
    ---
     vector/src/main/res/drawable/bg_send.xml | 13 ++++++++-----
     1 file changed, 8 insertions(+), 5 deletions(-)
    
    diff --git a/vector/src/main/res/drawable/bg_send.xml b/vector/src/main/res/drawable/bg_send.xml
    index 6b12664032..4b357d7ab1 100644
    --- a/vector/src/main/res/drawable/bg_send.xml
    +++ b/vector/src/main/res/drawable/bg_send.xml
    @@ -1,6 +1,9 @@
     
    -
    -
    -    
    -
    \ No newline at end of file
    +
    +    
    +        
    +            
    +        
    +    
    +    
    +
    \ No newline at end of file
    
    From 5e2f091ec105e0018e97f086b6d124eaba8ec3b3 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Thu, 10 Dec 2020 13:36:00 +0100
    Subject: [PATCH 42/42] 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): Cancelable
    +    fun downloadFile(fileName: String,
    +                     mimeType: String?,
    +                     url: String?,
    +                     elementToDecrypt: ElementToDecrypt?,
    +                     callback: MatrixCallback): Cancelable
     
    -    fun downloadFile(
    -            id: String,
    -            messageContent: MessageWithAttachmentContent,
    -            callback: MatrixCallback): Cancelable =
    +    fun downloadFile(messageContent: MessageWithAttachmentContent,
    +                     callback: MatrixCallback): 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 {
                         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 {
                             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 {
                         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 {
                             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(
             } 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 {
                             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 {
                         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 {
                         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 {
                         session.fileService().downloadFile(
    -                            id = action.uploadEvent.eventId,
                                 messageContent = action.uploadEvent.contentWithAttachmentContent,
                                 callback = it)
                     }