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.
|
||||
* @param attachment the 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]
|
||||
*/
|
||||
fun sendMedia(attachment: ContentAttachmentData,
|
||||
// TODO Change to a Compression Level Enum
|
||||
compressBeforeSending: Boolean): Cancelable
|
||||
compressBeforeSending: Boolean,
|
||||
roomIds: Set<String>): Cancelable
|
||||
|
||||
/**
|
||||
* Method to send a list of media asynchronously.
|
||||
* @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]
|
||||
*/
|
||||
fun sendMedias(attachments: List<ContentAttachmentData>,
|
||||
// TODO Change to a Compression Level Enum
|
||||
compressBeforeSending: Boolean): Cancelable
|
||||
compressBeforeSending: Boolean,
|
||||
roomIds: Set<String>): Cancelable
|
||||
|
||||
/**
|
||||
* Send a poll to the room.
|
||||
|
@ -17,7 +17,11 @@
|
||||
package im.vector.matrix.android.internal.di
|
||||
|
||||
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
|
||||
|
||||
internal class WorkManagerProvider @Inject constructor(
|
||||
@ -54,5 +58,7 @@ internal class WorkManagerProvider @Inject constructor(
|
||||
val workConstraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.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.relation.SendRelationWorker
|
||||
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.SendEventWorker
|
||||
import im.vector.matrix.android.internal.session.signout.SignOutModule
|
||||
@ -85,23 +86,25 @@ internal interface SessionComponent {
|
||||
|
||||
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
|
||||
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.toContent
|
||||
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.model.rest.EncryptedFileInfo
|
||||
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.WorkerParamsFactory
|
||||
import im.vector.matrix.android.internal.worker.getSessionComponent
|
||||
@ -43,8 +47,7 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class Params(
|
||||
override val sessionId: String,
|
||||
val roomId: String,
|
||||
val event: Event,
|
||||
val events: List<Event>,
|
||||
val attachment: ContentAttachmentData,
|
||||
val isRoomEncrypted: Boolean,
|
||||
val compressBeforeSending: Boolean,
|
||||
@ -68,14 +71,17 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
||||
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
|
||||
sessionComponent.inject(this)
|
||||
|
||||
val eventId = params.event.eventId ?: return Result.success()
|
||||
val attachment = params.attachment
|
||||
|
||||
val attachmentFile = try {
|
||||
File(attachment.path)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
contentUploadStateTracker.setFailure(params.event.eventId, e)
|
||||
params.events
|
||||
.mapNotNull { it.eventId }
|
||||
.forEach {
|
||||
contentUploadStateTracker.setFailure(it, e)
|
||||
}
|
||||
return Result.success(
|
||||
WorkerParamsFactory.toData(params.copy(
|
||||
lastFailureMessage = e.localizedMessage
|
||||
@ -91,14 +97,22 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
||||
ThumbnailExtractor.extractThumbnail(params.attachment)?.let { thumbnailData ->
|
||||
val thumbnailProgressListener = object : ProgressRequestBody.Listener {
|
||||
override fun onProgress(current: Long, total: Long) {
|
||||
contentUploadStateTracker.setProgressThumbnail(eventId, current, total)
|
||||
params.events
|
||||
.mapNotNull { it.eventId }
|
||||
.forEach {
|
||||
contentUploadStateTracker.setProgressThumbnail(it, current, total)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
val contentUploadResponse = if (params.isRoomEncrypted) {
|
||||
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)
|
||||
uploadedThumbnailEncryptedFileInfo = encryptionResult.encryptedFileInfo
|
||||
fileUploader.uploadByteArray(encryptionResult.encryptedByteArray,
|
||||
@ -121,11 +135,15 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
||||
|
||||
val progressListener = object : ProgressRequestBody.Listener {
|
||||
override fun onProgress(current: Long, total: Long) {
|
||||
if (isStopped) {
|
||||
contentUploadStateTracker.setFailure(eventId, Throwable("Cancelled"))
|
||||
} else {
|
||||
contentUploadStateTracker.setProgress(eventId, current, total)
|
||||
}
|
||||
params.events
|
||||
.mapNotNull { it.eventId }
|
||||
.forEach {
|
||||
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 {
|
||||
val contentUploadResponse = if (params.isRoomEncrypted) {
|
||||
Timber.v("Encrypt file")
|
||||
contentUploadStateTracker.setEncrypting(eventId)
|
||||
params.events
|
||||
.mapNotNull { it.eventId }
|
||||
.forEach {
|
||||
contentUploadStateTracker.setEncrypting(it)
|
||||
}
|
||||
|
||||
val encryptionResult = MXEncryptedAttachments.encryptAttachment(FileInputStream(attachmentFile), attachment.mimeType)
|
||||
uploadedFileEncryptedFileInfo = encryptionResult.encryptedFileInfo
|
||||
@ -154,7 +176,12 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
||||
}
|
||||
|
||||
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(
|
||||
WorkerParamsFactory.toData(
|
||||
params.copy(
|
||||
@ -170,9 +197,18 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
|
||||
thumbnailUrl: String?,
|
||||
thumbnailEncryptedFileInfo: EncryptedFileInfo?): Result {
|
||||
Timber.v("handleSuccess $attachmentUrl, work is stopped $isStopped")
|
||||
contentUploadStateTracker.setSuccess(params.event.eventId!!)
|
||||
val event = updateEvent(params.event, attachmentUrl, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo)
|
||||
val sendParams = SendEventWorker.Params(params.sessionId, params.roomId, event)
|
||||
params.events
|
||||
.mapNotNull { it.eventId }
|
||||
.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))
|
||||
}
|
||||
|
||||
|
@ -196,13 +196,13 @@ internal class DefaultRelationService @AssistedInject constructor(
|
||||
|
||||
private fun createEncryptEventWork(event: Event, keepKeys: List<String>?): OneTimeWorkRequest {
|
||||
// Same parameter
|
||||
val params = EncryptEventWorker.Params(sessionId, roomId, event, keepKeys)
|
||||
val params = EncryptEventWorker.Params(sessionId, event, keepKeys)
|
||||
val sendWorkData = WorkerParamsFactory.toData(params)
|
||||
return timeLineSendEventWorkCommon.createWork<EncryptEventWorker>(sendWorkData, true)
|
||||
}
|
||||
|
||||
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)
|
||||
return timeLineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.Operation
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
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.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
@ -49,7 +48,6 @@ import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private const val UPLOAD_WORK = "UPLOAD_WORK"
|
||||
private const val BACKOFF_DELAY = 10_000L
|
||||
|
||||
internal class DefaultSendService @AssistedInject constructor(
|
||||
@Assisted private val roomId: String,
|
||||
@ -58,7 +56,6 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||
@SessionId private val sessionId: String,
|
||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||
private val cryptoService: CryptoService,
|
||||
private val monarchy: Monarchy,
|
||||
private val taskExecutor: TaskExecutor,
|
||||
private val localEchoRepository: LocalEchoRepository
|
||||
) : SendService {
|
||||
@ -103,6 +100,7 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||
return if (cryptoService.isRoomEncrypted(roomId)) {
|
||||
Timber.v("Send event in encrypted room")
|
||||
val encryptWork = createEncryptEventWork(event, true)
|
||||
// Note that event will be replaced by the result of the previous work
|
||||
val sendWork = createSendEventWork(event, false)
|
||||
timelineSendEventWorkCommon.postSequentialWorks(roomId, encryptWork, sendWork)
|
||||
} 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()) {
|
||||
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
|
||||
val event = localEchoEventFactory.createMediaEvent(roomId, attachment).also {
|
||||
createLocalEcho(it)
|
||||
// Ensure current roomId is included in the set
|
||||
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 sendWork = createSendEventWork(localEcho, false)
|
||||
val encryptedLocalEchoes = splitLocalEchoes[true].orEmpty()
|
||||
val clearLocalEchoes = splitLocalEchoes[false].orEmpty()
|
||||
|
||||
if (isRoomEncrypted) {
|
||||
val encryptWork = createEncryptEventWork(localEcho, false /*not start of chain, take input error*/)
|
||||
val cancelableBag = CancelableBag()
|
||||
|
||||
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)
|
||||
.then(encryptWork)
|
||||
.then(sendWork)
|
||||
.then(dispatcherWork)
|
||||
.enqueue()
|
||||
op.result.addListener(Runnable {
|
||||
if (op.result.isCancelled) {
|
||||
operation.result.addListener(Runnable {
|
||||
if (operation.result.isCancelled) {
|
||||
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")
|
||||
}
|
||||
}, workerFutureListenerExecutor)
|
||||
} else {
|
||||
workManagerProvider.workManager
|
||||
.beginUniqueWork(buildWorkName(UPLOAD_WORK), ExistingWorkPolicy.APPEND, uploadWork)
|
||||
.then(sendWork)
|
||||
.enqueue()
|
||||
|
||||
cancelableBag.add(CancelableWork(workManagerProvider.workManager, dispatcherWork.id))
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -250,19 +273,19 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||
|
||||
private fun createEncryptEventWork(event: Event, startChain: Boolean): OneTimeWorkRequest {
|
||||
// Same parameter
|
||||
val params = EncryptEventWorker.Params(sessionId, roomId, event)
|
||||
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, BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
return timelineSendEventWorkCommon.createWork<SendEventWorker>(sendWorkData, startChain)
|
||||
@ -277,19 +300,33 @@ internal class DefaultSendService @AssistedInject constructor(
|
||||
return timelineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData, true)
|
||||
}
|
||||
|
||||
private fun createUploadMediaWork(event: Event,
|
||||
private fun createUploadMediaWork(allLocalEchos: List<Event>,
|
||||
attachment: ContentAttachmentData,
|
||||
isRoomEncrypted: Boolean,
|
||||
compressBeforeSending: Boolean,
|
||||
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)
|
||||
|
||||
return workManagerProvider.matrixOneTimeWorkRequestBuilder<UploadContentWorker>()
|
||||
.setConstraints(WorkManagerProvider.workConstraints)
|
||||
.startChain(startChain)
|
||||
.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()
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import android.content.Context
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.WorkerParameters
|
||||
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.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
@ -39,9 +38,8 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class Params(
|
||||
override val sessionId: String,
|
||||
val roomId: String,
|
||||
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,
|
||||
override val lastFailureMessage: String? = null
|
||||
) : SessionWorkerParams
|
||||
@ -53,7 +51,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||
Timber.v("Start Encrypt work")
|
||||
val params = WorkerParamsFactory.fromData<Params>(inputData)
|
||||
?: 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}")
|
||||
@ -80,7 +78,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||
var result: MXEncryptEventContentResult? = null
|
||||
try {
|
||||
result = awaitCallback {
|
||||
crypto.encryptEventContent(localMutableContent, localEvent.type, params.roomId, it)
|
||||
crypto.encryptEventContent(localMutableContent, localEvent.type, localEvent.roomId!!, it)
|
||||
}
|
||||
} catch (throwable: Throwable) {
|
||||
error = throwable
|
||||
@ -98,7 +96,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||
type = safeResult.eventType,
|
||||
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))
|
||||
} else {
|
||||
val sendState = when (error) {
|
||||
@ -107,7 +105,7 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||
}
|
||||
localEchoUpdater.updateSendState(localEvent.eventId, sendState)
|
||||
// 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")
|
||||
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.getSessionComponent
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class SendEventWorker(context: Context,
|
||||
@ -39,7 +40,6 @@ internal class SendEventWorker(context: Context,
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class Params(
|
||||
override val sessionId: String,
|
||||
val roomId: String,
|
||||
val event: Event,
|
||||
override val lastFailureMessage: String? = null
|
||||
) : SessionWorkerParams
|
||||
@ -50,7 +50,9 @@ internal class SendEventWorker(context: Context,
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
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()
|
||||
sessionComponent.inject(this)
|
||||
@ -66,7 +68,7 @@ internal class SendEventWorker(context: Context,
|
||||
return Result.success(inputData)
|
||||
}
|
||||
return try {
|
||||
sendEvent(event.eventId, event.type, event.content, params.roomId)
|
||||
sendEvent(event)
|
||||
Result.success()
|
||||
} catch (exception: Throwable) {
|
||||
if (exception.shouldBeRetried()) {
|
||||
@ -79,16 +81,16 @@ internal class SendEventWorker(context: Context,
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun sendEvent(eventId: String, eventType: String, content: Content?, roomId: String) {
|
||||
localEchoUpdater.updateSendState(eventId, SendState.SENDING)
|
||||
private suspend fun sendEvent(event: Event) {
|
||||
localEchoUpdater.updateSendState(event.eventId!!, SendState.SENDING)
|
||||
executeRequest<SendResponse>(eventBus) {
|
||||
apiCall = roomAPI.send(
|
||||
eventId,
|
||||
roomId,
|
||||
eventType,
|
||||
content
|
||||
event.eventId,
|
||||
event.roomId!!,
|
||||
event.type,
|
||||
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) {
|
||||
// Unknown limitation
|
||||
room.sendMedias(attachments, action.compressBeforeSending)
|
||||
room.sendMedias(attachments, action.compressBeforeSending, emptySet())
|
||||
} else {
|
||||
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(
|
||||
tooBigFile.name ?: tooBigFile.path,
|
||||
tooBigFile.size,
|
||||
|
@ -135,19 +135,19 @@ class IncomingShareViewModel @AssistedInject constructor(@Assisted initialState:
|
||||
proposeMediaEdition: Boolean,
|
||||
compressMediaBeforeSending: Boolean) {
|
||||
if (!proposeMediaEdition) {
|
||||
selectedRoomIds.forEach { roomId ->
|
||||
val room = session.getRoom(roomId)
|
||||
room?.sendMedias(attachmentData, compressMediaBeforeSending)
|
||||
}
|
||||
// Pick the first room to send the media
|
||||
selectedRoomIds.firstOrNull()
|
||||
?.let { roomId -> session.getRoom(roomId) }
|
||||
?.sendMedias(attachmentData, compressMediaBeforeSending, selectedRoomIds)
|
||||
} else {
|
||||
val previewable = attachmentData.filterPreviewables()
|
||||
val nonPreviewable = attachmentData.filterNonPreviewables()
|
||||
if (nonPreviewable.isNotEmpty()) {
|
||||
// Send the non previewable attachment right now (?)
|
||||
selectedRoomIds.forEach { roomId ->
|
||||
val room = session.getRoom(roomId)
|
||||
room?.sendMedias(nonPreviewable, compressMediaBeforeSending)
|
||||
}
|
||||
// Pick the first room to send the media
|
||||
selectedRoomIds.firstOrNull()
|
||||
?.let { roomId -> session.getRoom(roomId) }
|
||||
?.sendMedias(nonPreviewable, compressMediaBeforeSending, selectedRoomIds)
|
||||
}
|
||||
if (previewable.isNotEmpty()) {
|
||||
// In case of multiple share of media, edit them first
|
||||
|
Loading…
x
Reference in New Issue
Block a user