Send files to several rooms at a time
This commit is contained in:
parent
81de914360
commit
06ba478232
|
@ -52,20 +52,27 @@ interface SendService {
|
||||||
* Method to send a media asynchronously.
|
* Method to send a media asynchronously.
|
||||||
* @param attachment the media to send
|
* @param attachment the media to send
|
||||||
* @param compressBeforeSending set to true to compress media before sending them
|
* @param compressBeforeSending set to true to compress media before sending them
|
||||||
|
* @param roomIds set of roomIds to where the media will be sent. The current roomId will be add to this set if not present.
|
||||||
|
* It can be useful to send media to multiple room. It's safe to include the current roomId in this set
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun sendMedia(attachment: ContentAttachmentData,
|
fun sendMedia(attachment: ContentAttachmentData,
|
||||||
// TODO Change to a Compression Level Enum
|
// TODO Change to a Compression Level Enum
|
||||||
compressBeforeSending: Boolean): Cancelable
|
compressBeforeSending: Boolean,
|
||||||
|
roomIds: Set<String>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to send a list of media asynchronously.
|
* Method to send a list of media asynchronously.
|
||||||
* @param attachments the list of media to send
|
* @param attachments the list of media to send
|
||||||
|
* @param compressBeforeSending set to true to compress media before sending them
|
||||||
|
* @param roomIds set of roomIds to where the media will be sent. The current roomId will be add to this set if not present.
|
||||||
|
* It can be useful to send media to multiple room. It's safe to include the current roomId in this set
|
||||||
* @return a [Cancelable]
|
* @return a [Cancelable]
|
||||||
*/
|
*/
|
||||||
fun sendMedias(attachments: List<ContentAttachmentData>,
|
fun sendMedias(attachments: List<ContentAttachmentData>,
|
||||||
// TODO Change to a Compression Level Enum
|
// TODO Change to a Compression Level Enum
|
||||||
compressBeforeSending: Boolean): Cancelable
|
compressBeforeSending: Boolean,
|
||||||
|
roomIds: Set<String>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a poll to the room.
|
* Send a poll to the room.
|
||||||
|
|
|
@ -17,7 +17,11 @@
|
||||||
package im.vector.matrix.android.internal.di
|
package im.vector.matrix.android.internal.di
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.work.*
|
import androidx.work.Constraints
|
||||||
|
import androidx.work.ListenableWorker
|
||||||
|
import androidx.work.NetworkType
|
||||||
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
|
import androidx.work.WorkManager
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class WorkManagerProvider @Inject constructor(
|
internal class WorkManagerProvider @Inject constructor(
|
||||||
|
@ -54,5 +58,7 @@ internal class WorkManagerProvider @Inject constructor(
|
||||||
val workConstraints = Constraints.Builder()
|
val workConstraints = Constraints.Builder()
|
||||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
const val BACKOFF_DELAY = 10_000L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import im.vector.matrix.android.internal.session.pushers.PushersModule
|
||||||
import im.vector.matrix.android.internal.session.room.RoomModule
|
import im.vector.matrix.android.internal.session.room.RoomModule
|
||||||
import im.vector.matrix.android.internal.session.room.relation.SendRelationWorker
|
import im.vector.matrix.android.internal.session.room.relation.SendRelationWorker
|
||||||
import im.vector.matrix.android.internal.session.room.send.EncryptEventWorker
|
import im.vector.matrix.android.internal.session.room.send.EncryptEventWorker
|
||||||
|
import im.vector.matrix.android.internal.session.room.send.MultipleEventSendingDispatcherWorker
|
||||||
import im.vector.matrix.android.internal.session.room.send.RedactEventWorker
|
import im.vector.matrix.android.internal.session.room.send.RedactEventWorker
|
||||||
import im.vector.matrix.android.internal.session.room.send.SendEventWorker
|
import im.vector.matrix.android.internal.session.room.send.SendEventWorker
|
||||||
import im.vector.matrix.android.internal.session.signout.SignOutModule
|
import im.vector.matrix.android.internal.session.signout.SignOutModule
|
||||||
|
@ -85,23 +86,25 @@ internal interface SessionComponent {
|
||||||
|
|
||||||
fun taskExecutor(): TaskExecutor
|
fun taskExecutor(): TaskExecutor
|
||||||
|
|
||||||
fun inject(sendEventWorker: SendEventWorker)
|
fun inject(worker: SendEventWorker)
|
||||||
|
|
||||||
fun inject(sendEventWorker: SendRelationWorker)
|
fun inject(worker: SendRelationWorker)
|
||||||
|
|
||||||
fun inject(encryptEventWorker: EncryptEventWorker)
|
fun inject(worker: EncryptEventWorker)
|
||||||
|
|
||||||
fun inject(redactEventWorker: RedactEventWorker)
|
fun inject(worker: MultipleEventSendingDispatcherWorker)
|
||||||
|
|
||||||
fun inject(getGroupDataWorker: GetGroupDataWorker)
|
fun inject(worker: RedactEventWorker)
|
||||||
|
|
||||||
fun inject(uploadContentWorker: UploadContentWorker)
|
fun inject(worker: GetGroupDataWorker)
|
||||||
|
|
||||||
fun inject(syncWorker: SyncWorker)
|
fun inject(worker: UploadContentWorker)
|
||||||
|
|
||||||
fun inject(addHttpPusherWorker: AddHttpPusherWorker)
|
fun inject(worker: SyncWorker)
|
||||||
|
|
||||||
fun inject(sendVerificationMessageWorker: SendVerificationMessageWorker)
|
fun inject(worker: AddHttpPusherWorker)
|
||||||
|
|
||||||
|
fun inject(worker: SendVerificationMessageWorker)
|
||||||
|
|
||||||
@Component.Factory
|
@Component.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
|
|
|
@ -24,11 +24,15 @@ import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.toContent
|
import im.vector.matrix.android.api.session.events.model.toContent
|
||||||
import im.vector.matrix.android.api.session.events.model.toModel
|
import im.vector.matrix.android.api.session.events.model.toModel
|
||||||
import im.vector.matrix.android.api.session.room.model.message.*
|
import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageContent
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageImageContent
|
||||||
|
import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
|
||||||
import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments
|
import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments
|
||||||
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo
|
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo
|
||||||
import im.vector.matrix.android.internal.network.ProgressRequestBody
|
import im.vector.matrix.android.internal.network.ProgressRequestBody
|
||||||
import im.vector.matrix.android.internal.session.room.send.SendEventWorker
|
import im.vector.matrix.android.internal.session.room.send.MultipleEventSendingDispatcherWorker
|
||||||
import im.vector.matrix.android.internal.worker.SessionWorkerParams
|
import im.vector.matrix.android.internal.worker.SessionWorkerParams
|
||||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||||
import im.vector.matrix.android.internal.worker.getSessionComponent
|
import im.vector.matrix.android.internal.worker.getSessionComponent
|
||||||
|
@ -43,8 +47,7 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class Params(
|
internal data class Params(
|
||||||
override val sessionId: String,
|
override val sessionId: String,
|
||||||
val roomId: String,
|
val events: List<Event>,
|
||||||
val event: Event,
|
|
||||||
val attachment: ContentAttachmentData,
|
val attachment: ContentAttachmentData,
|
||||||
val isRoomEncrypted: Boolean,
|
val isRoomEncrypted: Boolean,
|
||||||
val compressBeforeSending: Boolean,
|
val compressBeforeSending: Boolean,
|
||||||
|
@ -68,14 +71,17 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
||||||
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
|
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
|
||||||
sessionComponent.inject(this)
|
sessionComponent.inject(this)
|
||||||
|
|
||||||
val eventId = params.event.eventId ?: return Result.success()
|
|
||||||
val attachment = params.attachment
|
val attachment = params.attachment
|
||||||
|
|
||||||
val attachmentFile = try {
|
val attachmentFile = try {
|
||||||
File(attachment.path)
|
File(attachment.path)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
contentUploadStateTracker.setFailure(params.event.eventId, e)
|
params.events
|
||||||
|
.mapNotNull { it.eventId }
|
||||||
|
.forEach {
|
||||||
|
contentUploadStateTracker.setFailure(it, e)
|
||||||
|
}
|
||||||
return Result.success(
|
return Result.success(
|
||||||
WorkerParamsFactory.toData(params.copy(
|
WorkerParamsFactory.toData(params.copy(
|
||||||
lastFailureMessage = e.localizedMessage
|
lastFailureMessage = e.localizedMessage
|
||||||
|
@ -91,14 +97,22 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
||||||
ThumbnailExtractor.extractThumbnail(params.attachment)?.let { thumbnailData ->
|
ThumbnailExtractor.extractThumbnail(params.attachment)?.let { thumbnailData ->
|
||||||
val thumbnailProgressListener = object : ProgressRequestBody.Listener {
|
val thumbnailProgressListener = object : ProgressRequestBody.Listener {
|
||||||
override fun onProgress(current: Long, total: Long) {
|
override fun onProgress(current: Long, total: Long) {
|
||||||
contentUploadStateTracker.setProgressThumbnail(eventId, current, total)
|
params.events
|
||||||
|
.mapNotNull { it.eventId }
|
||||||
|
.forEach {
|
||||||
|
contentUploadStateTracker.setProgressThumbnail(it, current, total)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val contentUploadResponse = if (params.isRoomEncrypted) {
|
val contentUploadResponse = if (params.isRoomEncrypted) {
|
||||||
Timber.v("Encrypt thumbnail")
|
Timber.v("Encrypt thumbnail")
|
||||||
contentUploadStateTracker.setEncryptingThumbnail(eventId)
|
params.events
|
||||||
|
.mapNotNull { it.eventId }
|
||||||
|
.forEach {
|
||||||
|
contentUploadStateTracker.setEncryptingThumbnail(it)
|
||||||
|
}
|
||||||
val encryptionResult = MXEncryptedAttachments.encryptAttachment(ByteArrayInputStream(thumbnailData.bytes), thumbnailData.mimeType)
|
val encryptionResult = MXEncryptedAttachments.encryptAttachment(ByteArrayInputStream(thumbnailData.bytes), thumbnailData.mimeType)
|
||||||
uploadedThumbnailEncryptedFileInfo = encryptionResult.encryptedFileInfo
|
uploadedThumbnailEncryptedFileInfo = encryptionResult.encryptedFileInfo
|
||||||
fileUploader.uploadByteArray(encryptionResult.encryptedByteArray,
|
fileUploader.uploadByteArray(encryptionResult.encryptedByteArray,
|
||||||
|
@ -121,11 +135,15 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
||||||
|
|
||||||
val progressListener = object : ProgressRequestBody.Listener {
|
val progressListener = object : ProgressRequestBody.Listener {
|
||||||
override fun onProgress(current: Long, total: Long) {
|
override fun onProgress(current: Long, total: Long) {
|
||||||
if (isStopped) {
|
params.events
|
||||||
contentUploadStateTracker.setFailure(eventId, Throwable("Cancelled"))
|
.mapNotNull { it.eventId }
|
||||||
} else {
|
.forEach {
|
||||||
contentUploadStateTracker.setProgress(eventId, current, total)
|
if (isStopped) {
|
||||||
}
|
contentUploadStateTracker.setFailure(it, Throwable("Cancelled"))
|
||||||
|
} else {
|
||||||
|
contentUploadStateTracker.setProgress(it, current, total)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +152,11 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
||||||
return try {
|
return try {
|
||||||
val contentUploadResponse = if (params.isRoomEncrypted) {
|
val contentUploadResponse = if (params.isRoomEncrypted) {
|
||||||
Timber.v("Encrypt file")
|
Timber.v("Encrypt file")
|
||||||
contentUploadStateTracker.setEncrypting(eventId)
|
params.events
|
||||||
|
.mapNotNull { it.eventId }
|
||||||
|
.forEach {
|
||||||
|
contentUploadStateTracker.setEncrypting(it)
|
||||||
|
}
|
||||||
|
|
||||||
val encryptionResult = MXEncryptedAttachments.encryptAttachment(FileInputStream(attachmentFile), attachment.mimeType)
|
val encryptionResult = MXEncryptedAttachments.encryptAttachment(FileInputStream(attachmentFile), attachment.mimeType)
|
||||||
uploadedFileEncryptedFileInfo = encryptionResult.encryptedFileInfo
|
uploadedFileEncryptedFileInfo = encryptionResult.encryptedFileInfo
|
||||||
|
@ -154,7 +176,12 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleFailure(params: Params, failure: Throwable): Result {
|
private fun handleFailure(params: Params, failure: Throwable): Result {
|
||||||
contentUploadStateTracker.setFailure(params.event.eventId!!, failure)
|
params.events
|
||||||
|
.mapNotNull { it.eventId }
|
||||||
|
.forEach {
|
||||||
|
contentUploadStateTracker.setFailure(it, failure)
|
||||||
|
}
|
||||||
|
|
||||||
return Result.success(
|
return Result.success(
|
||||||
WorkerParamsFactory.toData(
|
WorkerParamsFactory.toData(
|
||||||
params.copy(
|
params.copy(
|
||||||
|
@ -170,9 +197,18 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
||||||
thumbnailUrl: String?,
|
thumbnailUrl: String?,
|
||||||
thumbnailEncryptedFileInfo: EncryptedFileInfo?): Result {
|
thumbnailEncryptedFileInfo: EncryptedFileInfo?): Result {
|
||||||
Timber.v("handleSuccess $attachmentUrl, work is stopped $isStopped")
|
Timber.v("handleSuccess $attachmentUrl, work is stopped $isStopped")
|
||||||
contentUploadStateTracker.setSuccess(params.event.eventId!!)
|
params.events
|
||||||
val event = updateEvent(params.event, attachmentUrl, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo)
|
.mapNotNull { it.eventId }
|
||||||
val sendParams = SendEventWorker.Params(params.sessionId, params.roomId, event)
|
.forEach {
|
||||||
|
contentUploadStateTracker.setSuccess(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
val updatedEvents = params.events
|
||||||
|
.map {
|
||||||
|
updateEvent(it, attachmentUrl, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
val sendParams = MultipleEventSendingDispatcherWorker.Params(params.sessionId, updatedEvents, params.isRoomEncrypted)
|
||||||
return Result.success(WorkerParamsFactory.toData(sendParams))
|
return Result.success(WorkerParamsFactory.toData(sendParams))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -196,13 +196,13 @@ internal class DefaultRelationService @AssistedInject constructor(
|
||||||
|
|
||||||
private fun createEncryptEventWork(event: Event, keepKeys: List<String>?): OneTimeWorkRequest {
|
private fun createEncryptEventWork(event: Event, keepKeys: List<String>?): OneTimeWorkRequest {
|
||||||
// Same parameter
|
// Same parameter
|
||||||
val params = EncryptEventWorker.Params(sessionId, roomId, event, keepKeys)
|
val params = EncryptEventWorker.Params(sessionId, event, keepKeys)
|
||||||
val sendWorkData = WorkerParamsFactory.toData(params)
|
val sendWorkData = WorkerParamsFactory.toData(params)
|
||||||
return timeLineSendEventWorkCommon.createWork<EncryptEventWorker>(sendWorkData, true)
|
return timeLineSendEventWorkCommon.createWork<EncryptEventWorker>(sendWorkData, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
|
private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
|
||||||
val sendContentWorkerParams = SendEventWorker.Params(sessionId, roomId, event)
|
val sendContentWorkerParams = SendEventWorker.Params(sessionId, event)
|
||||||
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
||||||
return timeLineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
return timeLineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import androidx.work.OneTimeWorkRequest
|
||||||
import androidx.work.Operation
|
import androidx.work.Operation
|
||||||
import com.squareup.inject.assisted.Assisted
|
import com.squareup.inject.assisted.Assisted
|
||||||
import com.squareup.inject.assisted.AssistedInject
|
import com.squareup.inject.assisted.AssistedInject
|
||||||
import com.zhuinden.monarchy.Monarchy
|
|
||||||
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
import im.vector.matrix.android.api.session.content.ContentAttachmentData
|
||||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
@ -49,7 +48,6 @@ import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
private const val UPLOAD_WORK = "UPLOAD_WORK"
|
private const val UPLOAD_WORK = "UPLOAD_WORK"
|
||||||
private const val BACKOFF_DELAY = 10_000L
|
|
||||||
|
|
||||||
internal class DefaultSendService @AssistedInject constructor(
|
internal class DefaultSendService @AssistedInject constructor(
|
||||||
@Assisted private val roomId: String,
|
@Assisted private val roomId: String,
|
||||||
|
@ -58,7 +56,6 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
@SessionId private val sessionId: String,
|
@SessionId private val sessionId: String,
|
||||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||||
private val cryptoService: CryptoService,
|
private val cryptoService: CryptoService,
|
||||||
private val monarchy: Monarchy,
|
|
||||||
private val taskExecutor: TaskExecutor,
|
private val taskExecutor: TaskExecutor,
|
||||||
private val localEchoRepository: LocalEchoRepository
|
private val localEchoRepository: LocalEchoRepository
|
||||||
) : SendService {
|
) : SendService {
|
||||||
|
@ -103,6 +100,7 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
return if (cryptoService.isRoomEncrypted(roomId)) {
|
return if (cryptoService.isRoomEncrypted(roomId)) {
|
||||||
Timber.v("Send event in encrypted room")
|
Timber.v("Send event in encrypted room")
|
||||||
val encryptWork = createEncryptEventWork(event, true)
|
val encryptWork = createEncryptEventWork(event, true)
|
||||||
|
// Note that event will be replaced by the result of the previous work
|
||||||
val sendWork = createSendEventWork(event, false)
|
val sendWork = createSendEventWork(event, false)
|
||||||
timelineSendEventWorkCommon.postSequentialWorks(roomId, encryptWork, sendWork)
|
timelineSendEventWorkCommon.postSequentialWorks(roomId, encryptWork, sendWork)
|
||||||
} else {
|
} else {
|
||||||
|
@ -111,9 +109,11 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sendMedias(attachments: List<ContentAttachmentData>, compressBeforeSending: Boolean): Cancelable {
|
override fun sendMedias(attachments: List<ContentAttachmentData>,
|
||||||
|
compressBeforeSending: Boolean,
|
||||||
|
roomIds: Set<String>): Cancelable {
|
||||||
return attachments.mapTo(CancelableBag()) {
|
return attachments.mapTo(CancelableBag()) {
|
||||||
sendMedia(it, compressBeforeSending)
|
sendMedia(it, compressBeforeSending, roomIds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,43 +201,66 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sendMedia(attachment: ContentAttachmentData, compressBeforeSending: Boolean): Cancelable {
|
override fun sendMedia(attachment: ContentAttachmentData,
|
||||||
|
compressBeforeSending: Boolean,
|
||||||
|
roomIds: Set<String>): Cancelable {
|
||||||
// Create an event with the media file path
|
// Create an event with the media file path
|
||||||
val event = localEchoEventFactory.createMediaEvent(roomId, attachment).also {
|
// Ensure current roomId is included in the set
|
||||||
createLocalEcho(it)
|
val allRoomIds = (roomIds + roomId).toList()
|
||||||
|
|
||||||
|
// Create local echo for each room
|
||||||
|
val allLocalEchoes = allRoomIds.map {
|
||||||
|
localEchoEventFactory.createMediaEvent(it, attachment).also { event ->
|
||||||
|
createLocalEcho(event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return internalSendMedia(event, attachment, compressBeforeSending)
|
return internalSendMedia(allLocalEchoes, attachment, compressBeforeSending)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun internalSendMedia(localEcho: Event, attachment: ContentAttachmentData, compressBeforeSending: Boolean): Cancelable {
|
/**
|
||||||
val isRoomEncrypted = cryptoService.isRoomEncrypted(roomId)
|
* We use the roomId of the local echo event
|
||||||
|
*/
|
||||||
|
private fun internalSendMedia(allLocalEchoes: List<Event>, attachment: ContentAttachmentData, compressBeforeSending: Boolean): Cancelable {
|
||||||
|
val splitLocalEchoes = allLocalEchoes.groupBy { cryptoService.isRoomEncrypted(it.roomId!!) }
|
||||||
|
|
||||||
val uploadWork = createUploadMediaWork(localEcho, attachment, isRoomEncrypted, compressBeforeSending, startChain = true)
|
val encryptedLocalEchoes = splitLocalEchoes[true].orEmpty()
|
||||||
val sendWork = createSendEventWork(localEcho, false)
|
val clearLocalEchoes = splitLocalEchoes[false].orEmpty()
|
||||||
|
|
||||||
if (isRoomEncrypted) {
|
val cancelableBag = CancelableBag()
|
||||||
val encryptWork = createEncryptEventWork(localEcho, false /*not start of chain, take input error*/)
|
|
||||||
|
|
||||||
val op: Operation = workManagerProvider.workManager
|
if (encryptedLocalEchoes.isNotEmpty()) {
|
||||||
|
val uploadWork = createUploadMediaWork(encryptedLocalEchoes, attachment, true, compressBeforeSending, startChain = true)
|
||||||
|
|
||||||
|
val dispatcherWork = createMultipleEventDispatcherWork(true)
|
||||||
|
|
||||||
|
val operation = workManagerProvider.workManager
|
||||||
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
|
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
|
||||||
.then(encryptWork)
|
.then(dispatcherWork)
|
||||||
.then(sendWork)
|
|
||||||
.enqueue()
|
.enqueue()
|
||||||
op.result.addListener(Runnable {
|
operation.result.addListener(Runnable {
|
||||||
if (op.result.isCancelled) {
|
if (operation.result.isCancelled) {
|
||||||
Timber.e("CHAIN WAS CANCELLED")
|
Timber.e("CHAIN WAS CANCELLED")
|
||||||
} else if (op.state.value is Operation.State.FAILURE) {
|
} else if (operation.state.value is Operation.State.FAILURE) {
|
||||||
Timber.e("CHAIN DID FAIL")
|
Timber.e("CHAIN DID FAIL")
|
||||||
}
|
}
|
||||||
}, workerFutureListenerExecutor)
|
}, workerFutureListenerExecutor)
|
||||||
} else {
|
|
||||||
workManagerProvider.workManager
|
cancelableBag.add(CancelableWork(workManagerProvider.workManager, dispatcherWork.id))
|
||||||
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
|
|
||||||
.then(sendWork)
|
|
||||||
.enqueue()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return CancelableWork(workManagerProvider.workManager, sendWork.id)
|
if (clearLocalEchoes.isNotEmpty()) {
|
||||||
|
val uploadWork = createUploadMediaWork(clearLocalEchoes, attachment, false, compressBeforeSending, startChain = true)
|
||||||
|
val dispatcherWork = createMultipleEventDispatcherWork(false)
|
||||||
|
|
||||||
|
workManagerProvider.workManager
|
||||||
|
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
|
||||||
|
.then(dispatcherWork)
|
||||||
|
.enqueue()
|
||||||
|
|
||||||
|
cancelableBag.add(CancelableWork(workManagerProvider.workManager, dispatcherWork.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
return cancelableBag
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createLocalEcho(event: Event) {
|
private fun createLocalEcho(event: Event) {
|
||||||
|
@ -250,19 +273,19 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
|
|
||||||
private fun createEncryptEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
|
private fun createEncryptEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
|
||||||
// Same parameter
|
// Same parameter
|
||||||
val params = EncryptEventWorker.Params(sessionId, roomId, event)
|
val params = EncryptEventWorker.Params(sessionId, event)
|
||||||
val sendWorkData = WorkerParamsFactory.toData(params)
|
val sendWorkData = WorkerParamsFactory.toData(params)
|
||||||
|
|
||||||
return workManagerProvider.matrixOneTimeWorkRequestBuilder<EncryptEventWorker>()
|
return workManagerProvider.matrixOneTimeWorkRequestBuilder<EncryptEventWorker>()
|
||||||
.setConstraints(WorkManagerProvider.workConstraints)
|
.setConstraints(WorkManagerProvider.workConstraints)
|
||||||
.setInputData(sendWorkData)
|
.setInputData(sendWorkData)
|
||||||
.startChain(startChain)
|
.startChain(startChain)
|
||||||
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
.setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
|
private fun createSendEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
|
||||||
val sendContentWorkerParams = SendEventWorker.Params(sessionId, roomId, event)
|
val sendContentWorkerParams = SendEventWorker.Params(sessionId, event)
|
||||||
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
||||||
|
|
||||||
return timelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
return timelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
||||||
|
@ -277,19 +300,33 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||||
return timelineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData, true)
|
return timelineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createUploadMediaWork(event: Event,
|
private fun createUploadMediaWork(allLocalEchos: List<Event>,
|
||||||
attachment: ContentAttachmentData,
|
attachment: ContentAttachmentData,
|
||||||
isRoomEncrypted: Boolean,
|
isRoomEncrypted: Boolean,
|
||||||
compressBeforeSending: Boolean,
|
compressBeforeSending: Boolean,
|
||||||
startChain: Boolean): OneTimeWorkRequest {
|
startChain: Boolean): OneTimeWorkRequest {
|
||||||
val uploadMediaWorkerParams = UploadContentWorker.Params(sessionId, roomId, event, attachment, isRoomEncrypted, compressBeforeSending)
|
val uploadMediaWorkerParams = UploadContentWorker.Params(sessionId, allLocalEchos, attachment, isRoomEncrypted, compressBeforeSending)
|
||||||
val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams)
|
val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams)
|
||||||
|
|
||||||
return workManagerProvider.matrixOneTimeWorkRequestBuilder<UploadContentWorker>()
|
return workManagerProvider.matrixOneTimeWorkRequestBuilder<UploadContentWorker>()
|
||||||
.setConstraints(WorkManagerProvider.workConstraints)
|
.setConstraints(WorkManagerProvider.workConstraints)
|
||||||
.startChain(startChain)
|
.startChain(startChain)
|
||||||
.setInputData(uploadWorkData)
|
.setInputData(uploadWorkData)
|
||||||
.setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
.setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createMultipleEventDispatcherWork(isRoomEncrypted: Boolean): OneTimeWorkRequest {
|
||||||
|
// the list of events will be replaced by the result of the media upload work
|
||||||
|
val params = MultipleEventSendingDispatcherWorker.Params(sessionId, emptyList(), isRoomEncrypted)
|
||||||
|
val workData = WorkerParamsFactory.toData(params)
|
||||||
|
|
||||||
|
return workManagerProvider.matrixOneTimeWorkRequestBuilder<MultipleEventSendingDispatcherWorker>()
|
||||||
|
// No constraint
|
||||||
|
// .setConstraints(WorkManagerProvider.workConstraints)
|
||||||
|
.startChain(false)
|
||||||
|
.setInputData(workData)
|
||||||
|
.setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import android.content.Context
|
||||||
import androidx.work.CoroutineWorker
|
import androidx.work.CoroutineWorker
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
@ -39,9 +38,8 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class Params(
|
internal data class Params(
|
||||||
override val sessionId: String,
|
override val sessionId: String,
|
||||||
val roomId: String,
|
|
||||||
val event: Event,
|
val event: Event,
|
||||||
/**Do not encrypt these keys, keep them as is in encrypted content (e.g. m.relates_to)*/
|
/** Do not encrypt these keys, keep them as is in encrypted content (e.g. m.relates_to) */
|
||||||
val keepKeys: List<String>? = null,
|
val keepKeys: List<String>? = null,
|
||||||
override val lastFailureMessage: String? = null
|
override val lastFailureMessage: String? = null
|
||||||
) : SessionWorkerParams
|
) : SessionWorkerParams
|
||||||
|
@ -53,7 +51,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||||
Timber.v("Start Encrypt work")
|
Timber.v("Start Encrypt work")
|
||||||
val params = WorkerParamsFactory.fromData<Params>(inputData)
|
val params = WorkerParamsFactory.fromData<Params>(inputData)
|
||||||
?: return Result.success().also {
|
?: return Result.success().also {
|
||||||
Timber.v("Work cancelled due to input error from parent")
|
Timber.e("Work cancelled due to input error from parent")
|
||||||
}
|
}
|
||||||
|
|
||||||
Timber.v("Start Encrypt work for event ${params.event.eventId}")
|
Timber.v("Start Encrypt work for event ${params.event.eventId}")
|
||||||
|
@ -80,7 +78,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||||
var result: MXEncryptEventContentResult? = null
|
var result: MXEncryptEventContentResult? = null
|
||||||
try {
|
try {
|
||||||
result = awaitCallback {
|
result = awaitCallback {
|
||||||
crypto.encryptEventContent(localMutableContent, localEvent.type, params.roomId, it)
|
crypto.encryptEventContent(localMutableContent, localEvent.type, localEvent.roomId!!, it)
|
||||||
}
|
}
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
error = throwable
|
error = throwable
|
||||||
|
@ -98,7 +96,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||||
type = safeResult.eventType,
|
type = safeResult.eventType,
|
||||||
content = safeResult.eventContent
|
content = safeResult.eventContent
|
||||||
)
|
)
|
||||||
val nextWorkerParams = SendEventWorker.Params(params.sessionId, params.roomId, encryptedEvent)
|
val nextWorkerParams = SendEventWorker.Params(params.sessionId, encryptedEvent)
|
||||||
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
|
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
|
||||||
} else {
|
} else {
|
||||||
val sendState = when (error) {
|
val sendState = when (error) {
|
||||||
|
@ -107,7 +105,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||||
}
|
}
|
||||||
localEchoUpdater.updateSendState(localEvent.eventId, sendState)
|
localEchoUpdater.updateSendState(localEvent.eventId, sendState)
|
||||||
// always return success, or the chain will be stuck for ever!
|
// always return success, or the chain will be stuck for ever!
|
||||||
val nextWorkerParams = SendEventWorker.Params(params.sessionId, params.roomId, localEvent, error?.localizedMessage
|
val nextWorkerParams = SendEventWorker.Params(params.sessionId, localEvent, error?.localizedMessage
|
||||||
?: "Error")
|
?: "Error")
|
||||||
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
|
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* 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.matrix.android.internal.session.room.send
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.work.BackoffPolicy
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.OneTimeWorkRequest
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||||
|
import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEventWorkCommon
|
||||||
|
import im.vector.matrix.android.internal.worker.SessionWorkerParams
|
||||||
|
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||||
|
import im.vector.matrix.android.internal.worker.getSessionComponent
|
||||||
|
import im.vector.matrix.android.internal.worker.startChain
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This worker creates a new work for each events passed in parameter
|
||||||
|
*/
|
||||||
|
internal class MultipleEventSendingDispatcherWorker(context: Context, params: WorkerParameters)
|
||||||
|
: CoroutineWorker(context, params) {
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
internal data class Params(
|
||||||
|
override val sessionId: String,
|
||||||
|
val events: List<Event>,
|
||||||
|
val isEncrypted: Boolean,
|
||||||
|
override val lastFailureMessage: String? = null
|
||||||
|
) : SessionWorkerParams
|
||||||
|
|
||||||
|
@Inject lateinit var workManagerProvider: WorkManagerProvider
|
||||||
|
@Inject lateinit var timelineSendEventWorkCommon: TimelineSendEventWorkCommon
|
||||||
|
|
||||||
|
override suspend fun doWork(): Result {
|
||||||
|
Timber.v("Start dispatch sending multiple event work")
|
||||||
|
val params = WorkerParamsFactory.fromData<Params>(inputData)
|
||||||
|
?: return Result.success().also {
|
||||||
|
Timber.e("Work cancelled due to input error from parent")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.lastFailureMessage != null) {
|
||||||
|
// Transmit the error
|
||||||
|
return Result.success(inputData)
|
||||||
|
}
|
||||||
|
|
||||||
|
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
|
||||||
|
sessionComponent.inject(this)
|
||||||
|
|
||||||
|
// Create a work for every event
|
||||||
|
params.events.forEach { event ->
|
||||||
|
if (params.isEncrypted) {
|
||||||
|
Timber.v("Send event in encrypted room")
|
||||||
|
val encryptWork = createEncryptEventWork(params.sessionId, event, true)
|
||||||
|
// Note that event will be replaced by the result of the previous work
|
||||||
|
val sendWork = createSendEventWork(params.sessionId, event, false)
|
||||||
|
timelineSendEventWorkCommon.postSequentialWorks(event.roomId!!, encryptWork, sendWork)
|
||||||
|
} else {
|
||||||
|
val sendWork = createSendEventWork(params.sessionId, event, true)
|
||||||
|
timelineSendEventWorkCommon.postWork(event.roomId!!, sendWork)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createEncryptEventWork(sessionId: String, event: Event, startChain: Boolean): OneTimeWorkRequest {
|
||||||
|
val params = EncryptEventWorker.Params(sessionId, event)
|
||||||
|
val sendWorkData = WorkerParamsFactory.toData(params)
|
||||||
|
|
||||||
|
return workManagerProvider.matrixOneTimeWorkRequestBuilder<EncryptEventWorker>()
|
||||||
|
.setConstraints(WorkManagerProvider.workConstraints)
|
||||||
|
.setInputData(sendWorkData)
|
||||||
|
.startChain(startChain)
|
||||||
|
.setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createSendEventWork(sessionId: String, event: Event, startChain: Boolean): OneTimeWorkRequest {
|
||||||
|
val sendContentWorkerParams = SendEventWorker.Params(sessionId, event)
|
||||||
|
val sendWorkData = WorkerParamsFactory.toData(sendContentWorkerParams)
|
||||||
|
|
||||||
|
return timelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,7 @@ import im.vector.matrix.android.internal.worker.SessionWorkerParams
|
||||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||||
import im.vector.matrix.android.internal.worker.getSessionComponent
|
import im.vector.matrix.android.internal.worker.getSessionComponent
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class SendEventWorker(context: Context,
|
internal class SendEventWorker(context: Context,
|
||||||
|
@ -39,7 +40,6 @@ internal class SendEventWorker(context: Context,
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class Params(
|
internal data class Params(
|
||||||
override val sessionId: String,
|
override val sessionId: String,
|
||||||
val roomId: String,
|
|
||||||
val event: Event,
|
val event: Event,
|
||||||
override val lastFailureMessage: String? = null
|
override val lastFailureMessage: String? = null
|
||||||
) : SessionWorkerParams
|
) : SessionWorkerParams
|
||||||
|
@ -50,7 +50,9 @@ internal class SendEventWorker(context: Context,
|
||||||
|
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
val params = WorkerParamsFactory.fromData<Params>(inputData)
|
val params = WorkerParamsFactory.fromData<Params>(inputData)
|
||||||
?: return Result.success()
|
?: return Result.success().also {
|
||||||
|
Timber.e("Work cancelled due to input error from parent")
|
||||||
|
}
|
||||||
|
|
||||||
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
|
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
|
||||||
sessionComponent.inject(this)
|
sessionComponent.inject(this)
|
||||||
|
@ -66,7 +68,7 @@ internal class SendEventWorker(context: Context,
|
||||||
return Result.success(inputData)
|
return Result.success(inputData)
|
||||||
}
|
}
|
||||||
return try {
|
return try {
|
||||||
sendEvent(event.eventId, event.type, event.content, params.roomId)
|
sendEvent(event)
|
||||||
Result.success()
|
Result.success()
|
||||||
} catch (exception: Throwable) {
|
} catch (exception: Throwable) {
|
||||||
if (exception.shouldBeRetried()) {
|
if (exception.shouldBeRetried()) {
|
||||||
|
@ -79,16 +81,16 @@ internal class SendEventWorker(context: Context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun sendEvent(eventId: String, eventType: String, content: Content?, roomId: String) {
|
private suspend fun sendEvent(event: Event) {
|
||||||
localEchoUpdater.updateSendState(eventId, SendState.SENDING)
|
localEchoUpdater.updateSendState(event.eventId!!, SendState.SENDING)
|
||||||
executeRequest<SendResponse>(eventBus) {
|
executeRequest<SendResponse>(eventBus) {
|
||||||
apiCall = roomAPI.send(
|
apiCall = roomAPI.send(
|
||||||
eventId,
|
event.eventId,
|
||||||
roomId,
|
event.roomId!!,
|
||||||
eventType,
|
event.type,
|
||||||
content
|
event.content
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
localEchoUpdater.updateSendState(eventId, SendState.SENT)
|
localEchoUpdater.updateSendState(event.eventId, SendState.SENT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -579,10 +579,10 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||||
|
|
||||||
if (maxUploadFileSize == HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN) {
|
if (maxUploadFileSize == HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN) {
|
||||||
// Unknown limitation
|
// Unknown limitation
|
||||||
room.sendMedias(attachments, action.compressBeforeSending)
|
room.sendMedias(attachments, action.compressBeforeSending, emptySet())
|
||||||
} else {
|
} else {
|
||||||
when (val tooBigFile = attachments.find { it.size > maxUploadFileSize }) {
|
when (val tooBigFile = attachments.find { it.size > maxUploadFileSize }) {
|
||||||
null -> room.sendMedias(attachments, action.compressBeforeSending)
|
null -> room.sendMedias(attachments, action.compressBeforeSending, emptySet())
|
||||||
else -> _viewEvents.post(RoomDetailViewEvents.FileTooBigError(
|
else -> _viewEvents.post(RoomDetailViewEvents.FileTooBigError(
|
||||||
tooBigFile.name ?: tooBigFile.path,
|
tooBigFile.name ?: tooBigFile.path,
|
||||||
tooBigFile.size,
|
tooBigFile.size,
|
||||||
|
|
|
@ -135,19 +135,19 @@ class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
proposeMediaEdition: Boolean,
|
proposeMediaEdition: Boolean,
|
||||||
compressMediaBeforeSending: Boolean) {
|
compressMediaBeforeSending: Boolean) {
|
||||||
if (!proposeMediaEdition) {
|
if (!proposeMediaEdition) {
|
||||||
selectedRoomIds.forEach { roomId ->
|
// Pick the first room to send the media
|
||||||
val room = session.getRoom(roomId)
|
selectedRoomIds.firstOrNull()
|
||||||
room?.sendMedias(attachmentData, compressMediaBeforeSending)
|
?.let { roomId -> session.getRoom(roomId) }
|
||||||
}
|
?.sendMedias(attachmentData, compressMediaBeforeSending, selectedRoomIds)
|
||||||
} else {
|
} else {
|
||||||
val previewable = attachmentData.filterPreviewables()
|
val previewable = attachmentData.filterPreviewables()
|
||||||
val nonPreviewable = attachmentData.filterNonPreviewables()
|
val nonPreviewable = attachmentData.filterNonPreviewables()
|
||||||
if (nonPreviewable.isNotEmpty()) {
|
if (nonPreviewable.isNotEmpty()) {
|
||||||
// Send the non previewable attachment right now (?)
|
// Send the non previewable attachment right now (?)
|
||||||
selectedRoomIds.forEach { roomId ->
|
// Pick the first room to send the media
|
||||||
val room = session.getRoom(roomId)
|
selectedRoomIds.firstOrNull()
|
||||||
room?.sendMedias(nonPreviewable, compressMediaBeforeSending)
|
?.let { roomId -> session.getRoom(roomId) }
|
||||||
}
|
?.sendMedias(nonPreviewable, compressMediaBeforeSending, selectedRoomIds)
|
||||||
}
|
}
|
||||||
if (previewable.isNotEmpty()) {
|
if (previewable.isNotEmpty()) {
|
||||||
// In case of multiple share of media, edit them first
|
// In case of multiple share of media, edit them first
|
||||||
|
|
Loading…
Reference in New Issue