Merge remote-tracking branch 'origin/feature/enhance_big_files' into feature/enhance_big_files

This commit is contained in:
Valere 2020-09-02 16:42:54 +02:00
commit 40f7dc4824
9 changed files with 104 additions and 72 deletions

View file

@ -110,13 +110,13 @@ interface SendService {
* Schedule this message to be resent
* @param localEcho the unsent local echo
*/
fun resendTextMessage(localEcho: TimelineEvent): Cancelable?
fun resendTextMessage(localEcho: TimelineEvent): Cancelable
/**
* Schedule this message to be resent
* @param localEcho the unsent local echo
*/
fun resendMediaMessage(localEcho: TimelineEvent): Cancelable?
fun resendMediaMessage(localEcho: TimelineEvent): Cancelable
/**
* Remove this failed message from the timeline
@ -124,8 +124,14 @@ interface SendService {
*/
fun deleteFailedEcho(localEcho: TimelineEvent)
/**
* Delete all the events in one of the sending states
*/
fun clearSendingQueue()
/**
* Cancel sending a specific event. It has to be in one of the sending states
*/
fun cancelSend(eventId: String)
/**

View file

@ -20,6 +20,9 @@ package org.matrix.android.sdk.internal.crypto.attachments
import android.util.Base64
import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo
import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileKey
import org.matrix.android.sdk.internal.util.base64ToBase64Url
import org.matrix.android.sdk.internal.util.base64ToUnpaddedBase64
import org.matrix.android.sdk.internal.util.base64UrlToBase64
import timber.log.Timber
import java.io.ByteArrayOutputStream
import java.io.File
@ -226,8 +229,8 @@ internal object MXEncryptedAttachments {
/**
* Decrypt an attachment
*
* @param attachmentStream the attachment stream. Will be closed after this method call.
* @param encryptedFileInfo the encryption file info
* @param attachmentStream the attachment stream. Will be closed after this method call.
* @param elementToDecrypt the element to decrypt the file
* @return the decrypted attachment stream
*/
fun decryptAttachment(attachmentStream: InputStream?, elementToDecrypt: ElementToDecrypt): InputStream? {
@ -254,7 +257,8 @@ internal object MXEncryptedAttachments {
*
* @param attachmentStream the attachment stream. Will be closed after this method call.
* @param elementToDecrypt the elementToDecrypt info
* @return the decrypted attachment stream
* @param outputStream the outputStream where the decrypted attachment will be write.
* @return true in case of success, false in case of error
*/
fun decryptAttachment(attachmentStream: InputStream?, elementToDecrypt: ElementToDecrypt?, outputStream: OutputStream): Boolean {
// sanity checks
@ -310,25 +314,4 @@ internal object MXEncryptedAttachments {
return false
}
/**
* Base64 URL conversion methods
*/
private fun base64UrlToBase64(base64Url: String): String {
return base64Url.replace('-', '+')
.replace('_', '/')
}
internal fun base64ToBase64Url(base64: String): String {
return base64.replace("\n".toRegex(), "")
.replace("\\+".toRegex(), "-")
.replace('/', '_')
.replace("=", "")
}
internal fun base64ToUnpaddedBase64(base64: String): String {
return base64.replace("\n".toRegex(), "")
.replace("=", "")
}
}

View file

@ -17,14 +17,18 @@
package org.matrix.android.sdk.internal.crypto.attachments
import android.util.Base64
import org.matrix.android.sdk.internal.util.base64ToUnpaddedBase64
import java.io.FilterInputStream
import java.io.IOException
import java.io.InputStream
import java.security.MessageDigest
class MatrixDigestCheckInputStream(`in`: InputStream?, val expectedDigest: String) : FilterInputStream(`in`) {
class MatrixDigestCheckInputStream(
inputStream: InputStream?,
private val expectedDigest: String
) : FilterInputStream(inputStream) {
val digest = MessageDigest.getInstance("SHA-256")
private val digest = MessageDigest.getInstance("SHA-256")
@Throws(IOException::class)
override fun read(): Int {
@ -57,7 +61,7 @@ class MatrixDigestCheckInputStream(`in`: InputStream?, val expectedDigest: Strin
@Throws(IOException::class)
private fun ensureDigest() {
val currentDigestValue = MXEncryptedAttachments.base64ToUnpaddedBase64(Base64.encodeToString(digest.digest(), Base64.DEFAULT))
val currentDigestValue = base64ToUnpaddedBase64(Base64.encodeToString(digest.digest(), Base64.DEFAULT))
if (currentDigestValue != expectedDigest) {
throw IOException("Bad digest")
}

View file

@ -23,7 +23,6 @@ import org.matrix.android.sdk.api.session.identity.FoundThreePid
import org.matrix.android.sdk.api.session.identity.IdentityServiceError
import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.identity.toMedium
import org.matrix.android.sdk.internal.crypto.attachments.MXEncryptedAttachments.base64ToBase64Url
import org.matrix.android.sdk.internal.crypto.tools.withOlmUtility
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.network.executeRequest
@ -32,6 +31,7 @@ import org.matrix.android.sdk.internal.session.identity.model.IdentityHashDetail
import org.matrix.android.sdk.internal.session.identity.model.IdentityLookUpParams
import org.matrix.android.sdk.internal.session.identity.model.IdentityLookUpResponse
import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.base64ToBase64Url
import java.util.Locale
import javax.inject.Inject

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2020 New Vector Ltd
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -28,7 +29,7 @@ import javax.inject.Inject
* Known limitation, for now requests are not persisted
*/
@SessionScope
class CancelSendTracker @Inject constructor() {
internal class CancelSendTracker @Inject constructor() {
data class Request(
val localId: String,

View file

@ -24,17 +24,28 @@ import androidx.work.OneTimeWorkRequest
import androidx.work.Operation
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.events.model.Event
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.toModel
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageFileContent
import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent
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.OptionItem
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
import org.matrix.android.sdk.api.session.room.send.SendService
import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.CancelableBag
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.api.util.NoOpCancellable
import org.matrix.android.sdk.internal.di.SessionId
import org.matrix.android.sdk.internal.di.WorkManagerProvider
import org.matrix.android.sdk.internal.session.content.UploadContentWorker
@ -44,16 +55,6 @@ import org.matrix.android.sdk.internal.util.CancelableWork
import org.matrix.android.sdk.internal.worker.AlwaysSuccessfulWorker
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.android.sdk.internal.worker.startChain
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.events.model.isAttachmentMessage
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageFileContent
import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent
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 timber.log.Timber
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
@ -137,29 +138,28 @@ internal class DefaultSendService @AssistedInject constructor(
.let { timelineSendEventWorkCommon.postWork(roomId, it) }
}
override fun resendTextMessage(localEcho: TimelineEvent): Cancelable? {
override fun resendTextMessage(localEcho: TimelineEvent): Cancelable {
if (localEcho.root.isTextMessage() && localEcho.root.sendState.hasFailed()) {
localEchoRepository.updateSendState(localEcho.eventId, SendState.UNSENT)
return sendEvent(localEcho.root)
}
return null
return NoOpCancellable
}
override fun resendMediaMessage(localEcho: TimelineEvent): Cancelable? {
override fun resendMediaMessage(localEcho: TimelineEvent): Cancelable {
if (localEcho.root.sendState.hasFailed()) {
// TODO this need a refactoring of attachement sending
val clearContent = localEcho.root.getClearContent()
val messageContent = clearContent?.toModel<MessageContent>() as? MessageWithAttachmentContent ?: return null
val messageContent = clearContent?.toModel<MessageContent>() as? MessageWithAttachmentContent ?: return NoOpCancellable
val url = messageContent.getFileUrl() ?: return null
val url = messageContent.getFileUrl() ?: return NoOpCancellable
if (url.startsWith("mxc://")) {
// We need to resend only the message as the attachment is ok
localEchoRepository.updateSendState(localEcho.eventId, SendState.UNSENT)
return sendEvent(localEcho.root)
}
// we need to resend the media
when (messageContent) {
// we need to resend the media
return when (messageContent) {
is MessageImageContent -> {
// The image has not yet been sent
val attachmentData = ContentAttachmentData(
@ -172,7 +172,7 @@ internal class DefaultSendService @AssistedInject constructor(
type = ContentAttachmentData.Type.IMAGE
)
localEchoRepository.updateSendState(localEcho.eventId, SendState.UNSENT)
return internalSendMedia(listOf(localEcho.root), attachmentData, true)
internalSendMedia(listOf(localEcho.root), attachmentData, true)
}
is MessageVideoContent -> {
val attachmentData = ContentAttachmentData(
@ -186,9 +186,9 @@ internal class DefaultSendService @AssistedInject constructor(
type = ContentAttachmentData.Type.VIDEO
)
localEchoRepository.updateSendState(localEcho.eventId, SendState.UNSENT)
return internalSendMedia(listOf(localEcho.root), attachmentData, true)
internalSendMedia(listOf(localEcho.root), attachmentData, true)
}
is MessageFileContent -> {
is MessageFileContent -> {
val attachmentData = ContentAttachmentData(
size = messageContent.info!!.size,
mimeType = messageContent.info.mimeType!!,
@ -197,7 +197,7 @@ internal class DefaultSendService @AssistedInject constructor(
type = ContentAttachmentData.Type.FILE
)
localEchoRepository.updateSendState(localEcho.eventId, SendState.UNSENT)
return internalSendMedia(listOf(localEcho.root), attachmentData, true)
internalSendMedia(listOf(localEcho.root), attachmentData, true)
}
is MessageAudioContent -> {
val attachmentData = ContentAttachmentData(
@ -209,12 +209,12 @@ internal class DefaultSendService @AssistedInject constructor(
type = ContentAttachmentData.Type.AUDIO
)
localEchoRepository.updateSendState(localEcho.eventId, SendState.UNSENT)
return internalSendMedia(listOf(localEcho.root), attachmentData, true)
internalSendMedia(listOf(localEcho.root), attachmentData, true)
}
else -> NoOpCancellable
}
return null
}
return null
return NoOpCancellable
}
override fun deleteFailedEcho(localEcho: TimelineEvent) {

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2020 New Vector Ltd
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.internal.util
/**
* Base64 URL conversion methods
*/
internal fun base64UrlToBase64(base64Url: String): String {
return base64Url.replace('-', '+')
.replace('_', '/')
}
internal fun base64ToBase64Url(base64: String): String {
return base64.replace("\n".toRegex(), "")
.replace("\\+".toRegex(), "-")
.replace('/', '_')
.replace("=", "")
}
internal fun base64ToUnpaddedBase64(base64: String): String {
return base64.replace("\n".toRegex(), "")
.replace("=", "")
}

View file

@ -1076,12 +1076,12 @@ class RoomDetailViewModel @AssistedInject constructor(
private fun handleCancel(action: RoomDetailAction.CancelSend) {
val targetEventId = action.eventId
room.getTimeLineEvent(targetEventId)?.let {
// State must be UNDELIVERED or Failed
// State must be in one of the sending states
if (!it.root.sendState.isSending()) {
Timber.e("Cannot resend message, it is not failed, Cancel first")
Timber.e("Cannot cancel message, it is not sending")
return
}
room.cancelSend(action.eventId)
room.cancelSend(targetEventId)
}
}

View file

@ -51,6 +51,7 @@ import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited
import org.matrix.android.sdk.rx.rx
import org.matrix.android.sdk.rx.unwrap
import java.util.ArrayList
/**
* Information related to an event and used to display preview in contextual bottom sheet.
@ -232,12 +233,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
}
add(EventSharedAction.Remove(eventId))
if (vectorPreferences.developerMode()) {
add(EventSharedAction.ViewSource(timelineEvent.root.toContentStringWithIndent()))
if (timelineEvent.isEncrypted() && timelineEvent.root.mxDecryptionResult != null) {
val decryptedContent = timelineEvent.root.toClearContentStringWithIndent()
?: stringProvider.getString(R.string.encryption_information_decryption_error)
add(EventSharedAction.ViewDecryptedSource(decryptedContent))
}
addViewSourceItems(timelineEvent)
}
} else if (timelineEvent.root.sendState.isSending()) {
// TODO is uploading attachment?
@ -307,13 +303,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
add(EventSharedAction.ReRequestKey(timelineEvent.eventId))
}
}
add(EventSharedAction.ViewSource(timelineEvent.root.toContentStringWithIndent()))
if (timelineEvent.isEncrypted() && timelineEvent.root.mxDecryptionResult != null) {
val decryptedContent = timelineEvent.root.toClearContentStringWithIndent()
?: stringProvider.getString(R.string.encryption_information_decryption_error)
add(EventSharedAction.ViewDecryptedSource(decryptedContent))
}
addViewSourceItems(timelineEvent)
}
add(EventSharedAction.CopyPermalink(eventId))
if (session.myUserId != timelineEvent.root.senderId) {
@ -329,6 +319,15 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
}
}
private fun ArrayList<EventSharedAction>.addViewSourceItems(timelineEvent: TimelineEvent) {
add(EventSharedAction.ViewSource(timelineEvent.root.toContentStringWithIndent()))
if (timelineEvent.isEncrypted() && timelineEvent.root.mxDecryptionResult != null) {
val decryptedContent = timelineEvent.root.toClearContentStringWithIndent()
?: stringProvider.getString(R.string.encryption_information_decryption_error)
add(EventSharedAction.ViewDecryptedSource(decryptedContent))
}
}
private fun canCancel(@Suppress("UNUSED_PARAMETER") event: TimelineEvent): Boolean {
return true
}