Merge remote-tracking branch 'upstream/dev' into libsession-integration

# Conflicts:
#	app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java
#	libsession/src/main/java/org/session/libsession/database/StorageProtocol.kt
#	libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt
#	libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentUploadJob.kt
#	libsession/src/main/java/org/session/libsession/messaging/jobs/BackgroundGroupAddJob.kt
#	libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt
#	libsession/src/main/java/org/session/libsession/messaging/jobs/GroupAvatarDownloadJob.kt
#	libsession/src/main/java/org/session/libsession/messaging/jobs/Job.kt
#	libsession/src/main/java/org/session/libsession/messaging/jobs/JobQueue.kt
#	libsession/src/main/java/org/session/libsession/messaging/jobs/MessageReceiveJob.kt
#	libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt
#	libsession/src/main/java/org/session/libsession/messaging/jobs/NotifyPNServerJob.kt
#	libsession/src/main/java/org/session/libsession/messaging/jobs/OpenGroupDeleteJob.kt
#	libsession/src/main/java/org/session/libsession/messaging/jobs/TrimThreadJob.kt
This commit is contained in:
0x330a
2023-02-20 11:52:25 +11:00
50 changed files with 874 additions and 729 deletions

View File

@@ -52,7 +52,7 @@ interface StorageProtocol {
fun getAttachmentUploadJob(attachmentID: Long): AttachmentUploadJob?
fun getMessageSendJob(messageSendJobID: String): MessageSendJob?
fun getMessageReceiveJob(messageReceiveJobID: String): Job?
fun getGroupAvatarDownloadJob(server: String, room: String): Job?
fun getGroupAvatarDownloadJob(server: String, room: String, imageId: String?): Job?
fun getConfigSyncJob(destination: Destination): Job?
fun resumeMessageSendJobIfNeeded(messageSendJobID: String)
fun isJobCanceled(job: Job): Boolean
@@ -84,6 +84,7 @@ interface StorageProtocol {
// Open Group Metadata
fun updateTitle(groupID: String, newValue: String)
fun updateProfilePicture(groupID: String, newValue: ByteArray)
fun removeProfilePicture(groupID: String)
fun hasDownloadedProfilePicture(groupID: String): Boolean
fun setUserCount(room: String, server: String, newValue: Int)

View File

@@ -16,15 +16,6 @@ object FileServerApi {
private const val serverPublicKey = "da21e1d886c6fbaea313f75298bd64aab03a97ce985b46bb2dad9f2089c8ee59"
const val server = "http://filev2.getsession.org"
const val maxFileSize = 10_000_000 // 10 MB
/**
* The file server has a file size limit of `maxFileSize`, which the Service Nodes try to enforce as well. However, the limit applied by the Service Nodes
* is on the **HTTP request** and not the actual file size. Because the file server expects the file data to be base 64 encoded, the size of the HTTP
* request for a given file will be at least `ceil(n / 3) * 4` bytes, where n is the file size in bytes. This is the minimum size because there might also
* be other parameters in the request. On average the multiplier appears to be about 1.5, so when checking whether the file will exceed the file size limit when
* uploading a file we just divide the size of the file by this number. The alternative would be to actually check the size of the HTTP request but that's only
* possible after proof of work has been calculated and the onion request encryption has happened, which takes several seconds.
*/
const val fileSizeORMultiplier = 2 // TODO: It should be possible to set this to 1.5?
sealed class Error(message: String) : Exception(message) {
object ParsingFailed : Error("Invalid response.")

View File

@@ -42,7 +42,7 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
private val TS_INCOMING_MESSAGE_ID_KEY = "tsIncoming_message_id"
}
override suspend fun execute() {
override suspend fun execute(dispatcherName: String) {
val storage = MessagingModuleConfiguration.shared.storage
val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider
val threadID = storage.getThreadIdForMms(databaseMessageID)
@@ -59,7 +59,7 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
Log.d("AttachmentDownloadJob", "Setting attachment state = failed, don't have attachment")
messageDataProvider.setAttachmentState(AttachmentState.FAILED, AttachmentId(attachmentID,0), databaseMessageID)
}
this.handlePermanentFailure(exception)
this.handlePermanentFailure(dispatcherName, exception)
} else if (exception == Error.DuplicateData) {
attachment?.let { id ->
Log.d("AttachmentDownloadJob", "Setting attachment state = done from duplicate data")
@@ -68,7 +68,7 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
Log.d("AttachmentDownloadJob", "Setting attachment state = done from duplicate data")
messageDataProvider.setAttachmentState(AttachmentState.DONE, AttachmentId(attachmentID,0), databaseMessageID)
}
this.handleSuccess()
this.handleSuccess(dispatcherName)
} else {
if (failureCount + 1 >= maxFailureCount) {
attachment?.let { id ->
@@ -79,7 +79,7 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
messageDataProvider.setAttachmentState(AttachmentState.FAILED, AttachmentId(attachmentID,0), databaseMessageID)
}
}
this.handleFailure(exception)
this.handleFailure(dispatcherName, exception)
}
}
@@ -150,7 +150,7 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
Log.d("AttachmentDownloadJob", "deleting tempfile")
tempFile.delete()
Log.d("AttachmentDownloadJob", "succeeding job")
handleSuccess()
handleSuccess(dispatcherName)
} catch (e: Exception) {
Log.e("AttachmentDownloadJob", "Error processing attachment download", e)
tempFile?.delete()
@@ -169,17 +169,17 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
}
}
private fun handleSuccess() {
private fun handleSuccess(dispatcherName: String) {
Log.w("AttachmentDownloadJob", "Attachment downloaded successfully.")
delegate?.handleJobSucceeded(this)
delegate?.handleJobSucceeded(this, dispatcherName)
}
private fun handlePermanentFailure(e: Exception) {
delegate?.handleJobFailedPermanently(this, e)
private fun handlePermanentFailure(dispatcherName: String, e: Exception) {
delegate?.handleJobFailedPermanently(this, dispatcherName, e)
}
private fun handleFailure(e: Exception) {
delegate?.handleJobFailed(this, e)
private fun handleFailure(dispatcherName: String, e: Exception) {
delegate?.handleJobFailed(this, dispatcherName, e)
}
private fun createTempFile(): File {

View File

@@ -49,29 +49,29 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
private val MESSAGE_SEND_JOB_ID_KEY = "message_send_job_id"
}
override suspend fun execute() {
override suspend fun execute(dispatcherName: String) {
try {
val storage = MessagingModuleConfiguration.shared.storage
val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider
val attachment = messageDataProvider.getScaledSignalAttachmentStream(attachmentID)
?: return handleFailure(Error.NoAttachment)
?: return handleFailure(dispatcherName, Error.NoAttachment)
val openGroup = storage.getOpenGroup(threadID.toLong())
if (openGroup != null) {
val keyAndResult = upload(attachment, openGroup.server, false) {
OpenGroupApi.upload(it, openGroup.room, openGroup.server)
}
handleSuccess(attachment, keyAndResult.first, keyAndResult.second)
handleSuccess(dispatcherName, attachment, keyAndResult.first, keyAndResult.second)
} else {
val keyAndResult = upload(attachment, FileServerApi.server, true) {
FileServerApi.upload(it)
}
handleSuccess(attachment, keyAndResult.first, keyAndResult.second)
handleSuccess(dispatcherName, attachment, keyAndResult.first, keyAndResult.second)
}
} catch (e: java.lang.Exception) {
if (e == Error.NoAttachment) {
this.handlePermanentFailure(e)
this.handlePermanentFailure(dispatcherName, e)
} else {
this.handleFailure(e)
this.handleFailure(dispatcherName, e)
}
}
}
@@ -108,9 +108,9 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
return Pair(key, UploadResult(id, "${server}/file/$id", digest))
}
private fun handleSuccess(attachment: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: UploadResult) {
private fun handleSuccess(dispatcherName: String, attachment: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: UploadResult) {
Log.d(TAG, "Attachment uploaded successfully.")
delegate?.handleJobSucceeded(this)
delegate?.handleJobSucceeded(this, dispatcherName)
val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider
messageDataProvider.handleSuccessfulAttachmentUpload(attachmentID, attachment, attachmentKey, uploadResult)
if (attachment.contentType.startsWith("audio/")) {
@@ -148,16 +148,16 @@ class AttachmentUploadJob(val attachmentID: Long, val threadID: String, val mess
storage.resumeMessageSendJobIfNeeded(messageSendJobID)
}
private fun handlePermanentFailure(e: Exception) {
private fun handlePermanentFailure(dispatcherName: String, e: Exception) {
Log.w(TAG, "Attachment upload failed permanently due to error: $this.")
delegate?.handleJobFailedPermanently(this, e)
delegate?.handleJobFailedPermanently(this, dispatcherName, e)
MessagingModuleConfiguration.shared.messageDataProvider.handleFailedAttachmentUpload(attachmentID)
failAssociatedMessageSendJob(e)
}
private fun handleFailure(e: Exception) {
private fun handleFailure(dispatcherName: String, e: Exception) {
Log.w(TAG, "Attachment upload failed due to error: $this.")
delegate?.handleJobFailed(this, e)
delegate?.handleJobFailed(this, dispatcherName, e)
if (failureCount + 1 >= maxFailureCount) {
failAssociatedMessageSendJob(e)
}

View File

@@ -27,32 +27,32 @@ class BackgroundGroupAddJob(val joinUrl: String): Job {
return "$server.$room"
}
override suspend fun execute() {
override suspend fun execute(dispatcherName: String) {
try {
val openGroup = OpenGroupUrlParser.parseUrl(joinUrl)
val storage = MessagingModuleConfiguration.shared.storage
val allOpenGroups = storage.getAllOpenGroups().map { it.value.joinURL }
if (allOpenGroups.contains(openGroup.joinUrl())) {
Log.e("OpenGroupDispatcher", "Failed to add group because", DuplicateGroupException())
delegate?.handleJobFailed(this, DuplicateGroupException())
delegate?.handleJobFailed(this, dispatcherName, DuplicateGroupException())
return
}
// get image
storage.setOpenGroupPublicKey(openGroup.server, openGroup.serverPublicKey)
val info = storage.addOpenGroup(openGroup.joinUrl())
val imageId = info?.imageId
if (imageId != null) {
JobQueue.shared.add(GroupAvatarDownloadJob(openGroup.room, openGroup.server))
if (imageId != null && storage.getGroupAvatarDownloadJob(openGroup.server, openGroup.room, imageId) == null) {
JobQueue.shared.add(GroupAvatarDownloadJob(openGroup.server, openGroup.room, imageId))
}
Log.d(KEY, "onOpenGroupAdded(${openGroup.server})")
storage.onOpenGroupAdded(openGroup.server)
} catch (e: Exception) {
Log.e("OpenGroupDispatcher", "Failed to add group because",e)
delegate?.handleJobFailed(this, e)
delegate?.handleJobFailed(this, dispatcherName, e)
return
}
Log.d("Loki", "Group added successfully")
delegate?.handleJobSucceeded(this)
delegate?.handleJobSucceeded(this, dispatcherName)
}
override fun serialize(): Data = Data.Builder()

View File

@@ -70,11 +70,11 @@ class BatchMessageReceiveJob(
return storage.getOrCreateThreadIdFor(senderOrSync, message.groupPublicKey, openGroupID)
}
override suspend fun execute() {
executeAsync().get()
override suspend fun execute(dispatcherName: String) {
executeAsync(dispatcherName).get()
}
fun executeAsync(): Promise<Unit, Exception> {
fun executeAsync(dispatcherName: String): Promise<Unit, Exception> {
return task {
val threadMap = mutableMapOf<Long, MutableList<ParsedMessage>>()
val storage = MessagingModuleConfiguration.shared.storage
@@ -185,19 +185,21 @@ class BatchMessageReceiveJob(
deferredThreadMap.awaitAll()
}
if (failures.isEmpty()) {
handleSuccess()
handleSuccess(dispatcherName)
} else {
handleFailure()
handleFailure(dispatcherName)
}
}
}
private fun handleSuccess() {
this.delegate?.handleJobSucceeded(this)
private fun handleSuccess(dispatcherName: String) {
Log.i(TAG, "Completed processing of ${messages.size} messages")
this.delegate?.handleJobSucceeded(this, dispatcherName)
}
private fun handleFailure() {
this.delegate?.handleJobFailed(this, Exception("One or more jobs resulted in failure"))
private fun handleFailure(dispatcherName: String) {
Log.i(TAG, "Handling failure of ${failures.size} messages (${messages.size - failures.size} processed successfully)")
this.delegate?.handleJobFailed(this, dispatcherName, Exception("One or more jobs resulted in failure"))
}
override fun serialize(): Data {

View File

@@ -5,24 +5,43 @@ import org.session.libsession.messaging.open_groups.OpenGroupApi
import org.session.libsession.messaging.utilities.Data
import org.session.libsession.utilities.GroupUtil
class GroupAvatarDownloadJob(val room: String, val server: String) : Job {
class GroupAvatarDownloadJob(val server: String, val room: String, val imageId: String?) : Job {
override var delegate: JobDelegate? = null
override var id: String? = null
override var failureCount: Int = 0
override val maxFailureCount: Int = 10
override suspend fun execute() {
override suspend fun execute(dispatcherName: String) {
if (imageId == null) {
delegate?.handleJobFailedPermanently(this, dispatcherName, Exception("GroupAvatarDownloadJob now requires imageId"))
return
}
val storage = MessagingModuleConfiguration.shared.storage
val imageId = storage.getOpenGroup(room, server)?.imageId ?: return
val storedImageId = storage.getOpenGroup(room, server)?.imageId
if (storedImageId == null || storedImageId != imageId) {
delegate?.handleJobFailedPermanently(this, dispatcherName, Exception("GroupAvatarDownloadJob imageId does not match the OpenGroup"))
return
}
try {
val bytes = OpenGroupApi.downloadOpenGroupProfilePicture(server, room, imageId).get()
// Once the download is complete the imageId might no longer match, so we need to fetch it again just in case
val postDownloadStoredImageId = storage.getOpenGroup(room, server)?.imageId
if (postDownloadStoredImageId == null || postDownloadStoredImageId != imageId) {
delegate?.handleJobFailedPermanently(this, dispatcherName, Exception("GroupAvatarDownloadJob imageId no longer matches the OpenGroup"))
return
}
val groupId = GroupUtil.getEncodedOpenGroupID("$server.$room".toByteArray())
storage.updateProfilePicture(groupId, bytes)
storage.updateTimestampUpdated(groupId, System.currentTimeMillis())
delegate?.handleJobSucceeded(this)
delegate?.handleJobSucceeded(this, dispatcherName)
} catch (e: Exception) {
delegate?.handleJobFailed(this, e)
delegate?.handleJobFailed(this, dispatcherName, e)
}
}
@@ -30,6 +49,7 @@ class GroupAvatarDownloadJob(val room: String, val server: String) : Job {
return Data.Builder()
.putString(ROOM, room)
.putString(SERVER, server)
.putString(IMAGE_ID, imageId)
.build()
}
@@ -40,14 +60,16 @@ class GroupAvatarDownloadJob(val room: String, val server: String) : Job {
private const val ROOM = "room"
private const val SERVER = "server"
private const val IMAGE_ID = "imageId"
}
class Factory : Job.Factory<GroupAvatarDownloadJob> {
override fun create(data: Data): GroupAvatarDownloadJob {
return GroupAvatarDownloadJob(
data.getString(SERVER),
data.getString(ROOM),
data.getString(SERVER)
if (data.hasString(IMAGE_ID)) { data.getString(IMAGE_ID) } else { null }
)
}
}

View File

@@ -17,7 +17,7 @@ interface Job {
internal const val MAX_BUFFER_SIZE = 1_000_000 // bytes
}
suspend fun execute()
suspend fun execute(dispatcherName: String)
fun serialize(): Data

View File

@@ -2,7 +2,7 @@ package org.session.libsession.messaging.jobs
interface JobDelegate {
fun handleJobSucceeded(job: Job)
fun handleJobFailed(job: Job, error: Exception)
fun handleJobFailedPermanently(job: Job, error: Exception)
fun handleJobSucceeded(job: Job, dispatcherName: String)
fun handleJobFailed(job: Job, dispatcherName: String, error: Exception)
fun handleJobFailedPermanently(job: Job, dispatcherName: String, error: Exception)
}

View File

@@ -53,7 +53,7 @@ class JobQueue : JobDelegate {
}
if (openGroupId.isNullOrEmpty()) {
Log.e("OpenGroupDispatcher", "Open Group ID was null on ${job.javaClass.simpleName}")
handleJobFailedPermanently(job, NullPointerException("Open Group ID was null"))
handleJobFailedPermanently(job, name, NullPointerException("Open Group ID was null"))
} else {
val groupChannel = if (!openGroupChannels.containsKey(openGroupId)) {
Log.d("OpenGroupDispatcher", "Creating ${openGroupId.hashCode()} channel")
@@ -95,9 +95,16 @@ class JobQueue : JobDelegate {
}
private suspend fun Job.process(dispatcherName: String) {
Log.d(dispatcherName,"processJob: ${javaClass.simpleName}")
Log.d(dispatcherName,"processJob: ${javaClass.simpleName} (id: $id)")
delegate = this@JobQueue
execute()
try {
execute(dispatcherName)
}
catch (e: Exception) {
Log.d(dispatcherName, "unhandledJobException: ${javaClass.simpleName} (id: $id)")
this@JobQueue.handleJobFailed(this, dispatcherName, e)
}
}
init {
@@ -177,7 +184,7 @@ class JobQueue : JobDelegate {
return
}
if (!pendingJobIds.add(id)) {
Log.e("Loki","tried to re-queue pending/in-progress job")
Log.e("Loki","tried to re-queue pending/in-progress job (id: $id)")
return
}
queue.trySend(job)
@@ -196,7 +203,7 @@ class JobQueue : JobDelegate {
}
}
pendingJobs.sortedBy { it.id }.forEach { job ->
Log.i("Loki", "Resuming pending job of type: ${job::class.simpleName}.")
Log.i("Loki", "Resuming pending job of type: ${job::class.simpleName} (id: ${job.id}).")
queue.trySend(job) // Offer always called on unlimited capacity
}
}
@@ -224,21 +231,21 @@ class JobQueue : JobDelegate {
}
}
override fun handleJobSucceeded(job: Job) {
override fun handleJobSucceeded(job: Job, dispatcherName: String) {
val jobId = job.id ?: return
MessagingModuleConfiguration.shared.storage.markJobAsSucceeded(jobId)
pendingJobIds.remove(jobId)
}
override fun handleJobFailed(job: Job, error: Exception) {
override fun handleJobFailed(job: Job, dispatcherName: String, error: Exception) {
// Canceled
val storage = MessagingModuleConfiguration.shared.storage
if (storage.isJobCanceled(job)) {
return Log.i("Loki", "${job::class.simpleName} canceled.")
return Log.i("Loki", "${job::class.simpleName} canceled (id: ${job.id}).")
}
// Message send jobs waiting for the attachment to upload
if (job is MessageSendJob && error is MessageSendJob.AwaitingAttachmentUploadException) {
Log.i("Loki", "Message send job waiting for attachment upload to finish.")
Log.i("Loki", "Message send job waiting for attachment upload to finish (id: ${job.id}).")
return
}
@@ -256,21 +263,22 @@ class JobQueue : JobDelegate {
job.failureCount += 1
if (job.failureCount >= job.maxFailureCount) {
handleJobFailedPermanently(job, error)
handleJobFailedPermanently(job, dispatcherName, error)
} else {
storage.persistJob(job)
val retryInterval = getRetryInterval(job)
Log.i("Loki", "${job::class.simpleName} failed; scheduling retry (failure count is ${job.failureCount}).")
Log.i("Loki", "${job::class.simpleName} failed (id: ${job.id}); scheduling retry (failure count is ${job.failureCount}).")
timer.schedule(delay = retryInterval) {
Log.i("Loki", "Retrying ${job::class.simpleName}.")
Log.i("Loki", "Retrying ${job::class.simpleName} (id: ${job.id}).")
queue.trySend(job)
}
}
}
override fun handleJobFailedPermanently(job: Job, error: Exception) {
override fun handleJobFailedPermanently(job: Job, dispatcherName: String, error: Exception) {
val jobId = job.id ?: return
handleJobFailedPermanently(jobId)
Log.d(dispatcherName, "permanentlyFailedJob: ${javaClass.simpleName} (id: ${job.id})")
}
private fun handleJobFailedPermanently(jobId: String) {

View File

@@ -25,11 +25,11 @@ class MessageReceiveJob(val data: ByteArray, val serverHash: String? = null, val
private val OPEN_GROUP_ID_KEY = "open_group_id"
}
override suspend fun execute() {
executeAsync().get()
override suspend fun execute(dispatcherName: String) {
executeAsync(dispatcherName).get()
}
fun executeAsync(): Promise<Unit, Exception> {
fun executeAsync(dispatcherName: String): Promise<Unit, Exception> {
val deferred = deferred<Unit, Exception>()
try {
val isRetry: Boolean = failureCount != 0
@@ -39,32 +39,32 @@ class MessageReceiveJob(val data: ByteArray, val serverHash: String? = null, val
val (message, proto) = MessageReceiver.parse(this.data, this.openGroupMessageServerID, openGroupPublicKey = serverPublicKey)
message.serverHash = serverHash
MessageReceiver.handle(message, proto, this.openGroupID)
this.handleSuccess()
this.handleSuccess(dispatcherName)
deferred.resolve(Unit)
} catch (e: Exception) {
Log.e(TAG, "Couldn't receive message.", e)
if (e is MessageReceiver.Error && !e.isRetryable) {
Log.e("Loki", "Message receive job permanently failed.", e)
this.handlePermanentFailure(e)
this.handlePermanentFailure(dispatcherName, e)
} else {
Log.e("Loki", "Couldn't receive message.", e)
this.handleFailure(e)
this.handleFailure(dispatcherName, e)
}
deferred.resolve(Unit) // The promise is just used to keep track of when we're done
}
return deferred.promise
}
private fun handleSuccess() {
delegate?.handleJobSucceeded(this)
private fun handleSuccess(dispatcherName: String) {
delegate?.handleJobSucceeded(this, dispatcherName)
}
private fun handlePermanentFailure(e: Exception) {
delegate?.handleJobFailedPermanently(this, e)
private fun handlePermanentFailure(dispatcherName: String, e: Exception) {
delegate?.handleJobFailedPermanently(this, dispatcherName, e)
}
private fun handleFailure(e: Exception) {
delegate?.handleJobFailed(this, e)
private fun handleFailure(dispatcherName: String, e: Exception) {
delegate?.handleJobFailed(this, dispatcherName, e)
}
override fun serialize(): Data {

View File

@@ -32,7 +32,7 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
private val DESTINATION_KEY = "destination"
}
override suspend fun execute() {
override suspend fun execute(dispatcherName: String) {
val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider
val message = message as? VisibleMessage
val storage = MessagingModuleConfiguration.shared.storage
@@ -60,12 +60,12 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
}
}
if (attachmentsToUpload.isNotEmpty()) {
this.handleFailure(AwaitingAttachmentUploadException)
this.handleFailure(dispatcherName, AwaitingAttachmentUploadException)
return
} // Wait for all attachments to upload before continuing
}
val promise = MessageSender.send(this.message, this.destination).success {
this.handleSuccess()
this.handleSuccess(dispatcherName)
}.fail { exception ->
var logStacktrace = true
@@ -74,14 +74,14 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
is HTTP.HTTPRequestFailedException -> {
logStacktrace = false
if (exception.statusCode == 429) { this.handlePermanentFailure(exception) }
else { this.handleFailure(exception) }
if (exception.statusCode == 429) { this.handlePermanentFailure(dispatcherName, exception) }
else { this.handleFailure(dispatcherName, exception) }
}
is MessageSender.Error -> {
if (!exception.isRetryable) { this.handlePermanentFailure(exception) }
else { this.handleFailure(exception) }
if (!exception.isRetryable) { this.handlePermanentFailure(dispatcherName, exception) }
else { this.handleFailure(dispatcherName, exception) }
}
else -> this.handleFailure(exception)
else -> this.handleFailure(dispatcherName, exception)
}
if (logStacktrace) { Log.e(TAG, "Couldn't send message due to error", exception) }
@@ -94,15 +94,15 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
}
}
private fun handleSuccess() {
delegate?.handleJobSucceeded(this)
private fun handleSuccess(dispatcherName: String) {
delegate?.handleJobSucceeded(this, dispatcherName)
}
private fun handlePermanentFailure(error: Exception) {
delegate?.handleJobFailedPermanently(this, error)
private fun handlePermanentFailure(dispatcherName: String, error: Exception) {
delegate?.handleJobFailedPermanently(this, dispatcherName, error)
}
private fun handleFailure(error: Exception) {
private fun handleFailure(dispatcherName: String, error: Exception) {
Log.w(TAG, "Failed to send $message::class.simpleName.")
val message = message as? VisibleMessage
if (message != null) {
@@ -110,7 +110,7 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
return // The message has been deleted
}
}
delegate?.handleJobFailed(this, error)
delegate?.handleJobFailed(this, dispatcherName, error)
}
override fun serialize(): Data {

View File

@@ -30,7 +30,7 @@ class NotifyPNServerJob(val message: SnodeMessage) : Job {
private val MESSAGE_KEY = "message"
}
override suspend fun execute() {
override suspend fun execute(dispatcherName: String) {
val server = PushNotificationAPI.server
val parameters = mapOf( "data" to message.data, "send_to" to message.recipient )
val url = "${server}/notify"
@@ -46,18 +46,18 @@ class NotifyPNServerJob(val message: SnodeMessage) : Job {
Log.d("Loki", "Couldn't notify PN server due to error: $exception.")
}
}.success {
handleSuccess()
handleSuccess(dispatcherName)
}. fail {
handleFailure(it)
handleFailure(dispatcherName, it)
}
}
private fun handleSuccess() {
delegate?.handleJobSucceeded(this)
private fun handleSuccess(dispatcherName: String) {
delegate?.handleJobSucceeded(this, dispatcherName)
}
private fun handleFailure(error: Exception) {
delegate?.handleJobFailed(this, error)
private fun handleFailure(dispatcherName: String, error: Exception) {
delegate?.handleJobFailed(this, dispatcherName, error)
}
override fun serialize(): Data {

View File

@@ -19,7 +19,7 @@ class OpenGroupDeleteJob(private val messageServerIds: LongArray, private val th
override var failureCount: Int = 0
override val maxFailureCount: Int = 1
override suspend fun execute() {
override suspend fun execute(dispatcherName: String) {
val dataProvider = MessagingModuleConfiguration.shared.messageDataProvider
val numberToDelete = messageServerIds.size
Log.d(TAG, "Deleting $numberToDelete messages")
@@ -39,10 +39,10 @@ class OpenGroupDeleteJob(private val messageServerIds: LongArray, private val th
}
Log.d(TAG, "Deleted ${messageIds.first.size + messageIds.second.size} messages successfully")
delegate?.handleJobSucceeded(this)
delegate?.handleJobSucceeded(this, dispatcherName)
}
catch (e: Exception) {
delegate?.handleJobFailed(this, e)
delegate?.handleJobFailed(this, dispatcherName, e)
}
}

View File

@@ -20,7 +20,7 @@ class TrimThreadJob(val threadId: Long, val openGroupId: String?) : Job {
const val THREAD_LENGTH_TRIGGER_SIZE = 2000
}
override suspend fun execute() {
override suspend fun execute(dispatcherName: String) {
val context = MessagingModuleConfiguration.shared.context
val trimmingEnabled = TextSecurePreferences.isThreadLengthTrimmingEnabled(context)
val storage = MessagingModuleConfiguration.shared.storage
@@ -29,7 +29,7 @@ class TrimThreadJob(val threadId: Long, val openGroupId: String?) : Job {
val oldestMessageTime = System.currentTimeMillis() - TRIM_TIME_LIMIT
storage.trimThreadBefore(threadId, oldestMessageTime)
}
delegate?.handleJobSucceeded(this)
delegate?.handleJobSucceeded(this, dispatcherName)
}
override fun serialize(): Data {

View File

@@ -36,7 +36,7 @@ data class OpenGroup(
val server = json.get("server").asText().lowercase(Locale.US)
val displayName = json.get("displayName").asText()
val publicKey = json.get("publicKey").asText()
val imageId = json.get("imageId")?.asText()
val imageId = if (json.hasNonNull("imageId")) { json.get("imageId")?.asText() } else { null }
val canWrite = json.get("canWrite")?.asText()?.toBoolean() ?: true
val infoUpdates = json.get("infoUpdates")?.asText()?.toIntOrNull() ?: 0
OpenGroup(server = server, room = room, name = displayName, publicKey = publicKey, imageId = imageId, canWrite = canWrite, infoUpdates = infoUpdates)

View File

@@ -159,20 +159,30 @@ class OpenGroupPoller(private val server: String, private val executorService: S
})
}
// Update the group avatar
if (
(
pollInfo.details != null &&
pollInfo.details.imageId != null && (
pollInfo.details.imageId != existingOpenGroup.imageId ||
!storage.hasDownloadedProfilePicture(dbGroupId)
)
) &&
storage.getGroupAvatarDownloadJob(openGroup.server, openGroup.room, pollInfo.details.imageId) == null
) || (
pollInfo.details == null &&
existingOpenGroup.imageId != null &&
!storage.hasDownloadedProfilePicture(dbGroupId)
!storage.hasDownloadedProfilePicture(dbGroupId) &&
storage.getGroupAvatarDownloadJob(openGroup.server, openGroup.room, existingOpenGroup.imageId) == null
)
) {
JobQueue.shared.add(GroupAvatarDownloadJob(roomToken, server))
JobQueue.shared.add(GroupAvatarDownloadJob(server, roomToken, existingOpenGroup.imageId))
}
else if (
pollInfo.details != null &&
pollInfo.details.imageId == null &&
existingOpenGroup.imageId != null
) {
storage.removeProfilePicture(dbGroupId)
}
}

View File

@@ -20,6 +20,7 @@ data class SnodeMessage(
*/
val timestamp: Long
) {
internal constructor(): this("", "", -1, -1)
internal fun toJSON(): Map<String, String> {
return mapOf(

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- MessageRecord -->
<string name="MessageRecord_left_group">Vous avez quitté le groupe.</string>
<string name="MessageRecord_you_created_a_new_group">Vous avez créé un nouveau groupe.</string>
<string name="MessageRecord_s_added_you_to_the_group">%1$s vous a ajouté·e dans le groupe.</string>
<string name="MessageRecord_you_renamed_the_group_to_s">Vous avez renommé le groupe en %1$s</string>
<string name="MessageRecord_s_renamed_the_group_to_s">%1$s a renommé le groupe en : %2$s</string>
<string name="MessageRecord_you_added_s_to_the_group">Vous avez ajouté %1$s au groupe.</string>
<string name="MessageRecord_s_added_s_to_the_group">%1$s a ajouté %2$s au groupe.</string>
<string name="MessageRecord_you_removed_s_from_the_group">Vous avez retiré %1$s du groupe.</string>
<string name="MessageRecord_s_removed_s_from_the_group">%1$s a supprimé %2$s du groupe.</string>
<string name="MessageRecord_you_were_removed_from_the_group">Vous avez été retiré·e du groupe.</string>
<string name="MessageRecord_you">Vous</string>
<string name="MessageRecord_s_called_you">%s vous a appelé·e</string>
<string name="MessageRecord_called_s">Vous avez appelé %s</string>
<string name="MessageRecord_missed_call_from">Appel manqué de %s</string>
<string name="MessageRecord_you_disabled_disappearing_messages">Vous avez désactivé les messages éphémères.</string>
<string name="MessageRecord_s_disabled_disappearing_messages">%1$s a désactivé les messages éphémères.</string>
<string name="MessageRecord_you_set_disappearing_message_time_to_s">Vous avez défini lexpiration des messages éphémères à %1$s</string>
<string name="MessageRecord_s_set_disappearing_message_time_to_s">%1$s a défini lexpiration des messages éphémères à %2$s</string>
<string name="MessageRecord_s_took_a_screenshot">%1$s a pris une capture d\'écran.</string>
<string name="MessageRecord_media_saved_by_s">%1$s a enregistré le média.</string>
<!-- expiration -->
<string name="expiration_off">Désactivé</string>
<plurals name="expiration_seconds">
<item quantity="one">%d seconde</item>
<item quantity="other">%d secondes</item>
</plurals>
<string name="expiration_seconds_abbreviated">%d s</string>
<plurals name="expiration_minutes">
<item quantity="one">%d minute</item>
<item quantity="other">%d minutes</item>
</plurals>
<string name="expiration_minutes_abbreviated">%d min</string>
<plurals name="expiration_hours">
<item quantity="one">%d heure</item>
<item quantity="other">%d heures</item>
</plurals>
<string name="expiration_hours_abbreviated">%d h</string>
<plurals name="expiration_days">
<item quantity="one">%d jour</item>
<item quantity="other">%d jours</item>
</plurals>
<string name="expiration_days_abbreviated">%d j</string>
<plurals name="expiration_weeks">
<item quantity="one">%d semaine</item>
<item quantity="other">%d semaines</item>
</plurals>
<string name="expiration_weeks_abbreviated">%d sem</string>
<string name="ConversationItem_group_action_left">%1$s a quitté le groupe.</string>
<!-- RecipientProvider -->
<string name="RecipientProvider_unnamed_group">Groupe sans nom</string>
</resources>

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- MessageRecord -->
<string name="MessageRecord_left_group">Vous avez quitté le groupe.</string>
<string name="MessageRecord_you_created_a_new_group">Vous avez créé un nouveau groupe.</string>
<string name="MessageRecord_s_added_you_to_the_group">%1$s vous a ajouté·e dans le groupe.</string>
<string name="MessageRecord_you_renamed_the_group_to_s">Vous avez renommé le groupe en %1$s</string>
<string name="MessageRecord_s_renamed_the_group_to_s">%1$s a renommé le groupe en : %2$s</string>
<string name="MessageRecord_you_added_s_to_the_group">Vous avez ajouté %1$s au groupe.</string>
<string name="MessageRecord_s_added_s_to_the_group">%1$s a ajouté %2$s au groupe.</string>
<string name="MessageRecord_you_removed_s_from_the_group">Vous avez retiré %1$s du groupe.</string>
<string name="MessageRecord_s_removed_s_from_the_group">%1$s a supprimé %2$s du groupe.</string>
<string name="MessageRecord_you_were_removed_from_the_group">Vous avez été retiré·e du groupe.</string>
<string name="MessageRecord_you">Vous</string>
<string name="MessageRecord_s_called_you">%s vous a appelé·e</string>
<string name="MessageRecord_called_s">Vous avez appelé %s</string>
<string name="MessageRecord_missed_call_from">Appel manqué de %s</string>
<string name="MessageRecord_you_disabled_disappearing_messages">Vous avez désactivé les messages éphémères.</string>
<string name="MessageRecord_s_disabled_disappearing_messages">%1$s a désactivé les messages éphémères.</string>
<string name="MessageRecord_you_set_disappearing_message_time_to_s">Vous avez défini lexpiration des messages éphémères à %1$s</string>
<string name="MessageRecord_s_set_disappearing_message_time_to_s">%1$s a défini lexpiration des messages éphémères à %2$s</string>
<string name="MessageRecord_s_took_a_screenshot">%1$s a pris une capture d\'écran.</string>
<string name="MessageRecord_media_saved_by_s">%1$s a enregistré le média.</string>
<!-- expiration -->
<string name="expiration_off">Désactivé</string>
<plurals name="expiration_seconds">
<item quantity="one">%d seconde</item>
<item quantity="other">%d secondes</item>
</plurals>
<string name="expiration_seconds_abbreviated">%d s</string>
<plurals name="expiration_minutes">
<item quantity="one">%d minute</item>
<item quantity="other">%d minutes</item>
</plurals>
<string name="expiration_minutes_abbreviated">%d min</string>
<plurals name="expiration_hours">
<item quantity="one">%d heure</item>
<item quantity="other">%d heures</item>
</plurals>
<string name="expiration_hours_abbreviated">%d h</string>
<plurals name="expiration_days">
<item quantity="one">%d jour</item>
<item quantity="other">%d jours</item>
</plurals>
<string name="expiration_days_abbreviated">%d j</string>
<plurals name="expiration_weeks">
<item quantity="one">%d semaine</item>
<item quantity="other">%d semaines</item>
</plurals>
<string name="expiration_weeks_abbreviated">%d sem</string>
<string name="ConversationItem_group_action_left">%1$s a quitté le groupe.</string>
<!-- RecipientProvider -->
<string name="RecipientProvider_unnamed_group">Groupe sans nom</string>
</resources>