Signal-Android/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStorySendSyncJob.kt

111 wiersze
4.0 KiB
Kotlin

package org.thoughtcrime.securesms.jobs
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.jobmanager.Data
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
import org.thoughtcrime.securesms.recipients.Recipient
import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessageRecipient
import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage
import org.whispersystems.signalservice.api.push.SignalServiceAddress
import java.lang.Exception
import java.util.Optional
import java.util.concurrent.TimeUnit
/**
* Transmits a sent sync transcript to linked devices containing the story sync manifest for the given sent timestamp.
* The transmitted message is sent as a recipient update, and will only contain affected recipients that still have a
* live story for the given timestamp.
*/
class MultiDeviceStorySendSyncJob private constructor(parameters: Parameters, private val sentTimestamp: Long, private val deletedMessageId: Long) : BaseJob(parameters) {
companion object {
const val KEY = "MultiDeviceStorySendSyncJob"
private val TAG = Log.tag(MultiDeviceStorySendSyncJob::class.java)
private const val DATA_SENT_TIMESTAMP = "sent.timestamp"
private const val DATA_DELETED_MESSAGE_ID = "deleted.message.id"
@JvmStatic
fun create(sentTimestamp: Long, deletedMessageId: Long): MultiDeviceStorySendSyncJob {
return MultiDeviceStorySendSyncJob(
parameters = Parameters.Builder()
.addConstraint(NetworkConstraint.KEY)
.setMaxAttempts(Parameters.UNLIMITED)
.setLifespan(TimeUnit.DAYS.toMillis(1))
.setQueue(KEY)
.build(),
sentTimestamp = sentTimestamp,
deletedMessageId = deletedMessageId
)
}
}
override fun serialize(): Data {
return Data.Builder()
.putLong(DATA_SENT_TIMESTAMP, sentTimestamp)
.putLong(DATA_DELETED_MESSAGE_ID, deletedMessageId)
.build()
}
override fun getFactoryKey(): String = KEY
override fun onRun() {
val recipientIds = SignalDatabase.storySends.getRecipientIdsForManifestUpdate(sentTimestamp, deletedMessageId)
if (recipientIds.isEmpty()) {
Log.i(TAG, "No recipients requiring a manifest update. Dropping.")
return
}
val updateManifest = SignalDatabase.storySends.getSentStorySyncManifestForUpdate(sentTimestamp, recipientIds)
if (updateManifest.entries.isEmpty()) {
Log.i(TAG, "No entries in updated manifest. Dropping.")
return
}
val recipientsSet = updateManifest.toRecipientsSet()
val transcriptMessage: SignalServiceSyncMessage = SignalServiceSyncMessage.forSentTranscript(buildSentTranscript(recipientsSet))
val sendMessageResult = ApplicationDependencies.getSignalServiceMessageSender().sendSyncMessage(transcriptMessage, Optional.empty())
if (!sendMessageResult.isSuccess) {
throw RetryableException()
}
}
override fun onShouldRetry(e: Exception): Boolean {
return e is RetryableException
}
private fun buildSentTranscript(recipientsSet: Set<SignalServiceStoryMessageRecipient>): SentTranscriptMessage {
return SentTranscriptMessage(
Optional.of(SignalServiceAddress(Recipient.self().requireServiceId())),
sentTimestamp,
Optional.empty(),
0,
emptyMap(),
true,
Optional.empty(),
recipientsSet
)
}
override fun onFailure() = Unit
class RetryableException : Exception()
class Factory : Job.Factory<MultiDeviceStorySendSyncJob> {
override fun create(parameters: Parameters, data: Data): MultiDeviceStorySendSyncJob {
return MultiDeviceStorySendSyncJob(
parameters = parameters,
sentTimestamp = data.getLong(DATA_SENT_TIMESTAMP),
deletedMessageId = data.getLong(DATA_DELETED_MESSAGE_ID)
)
}
}
}