mirror of
https://github.com/oxen-io/session-android.git
synced 2025-12-12 08:13:14 +00:00
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:
@@ -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)
|
||||
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ data class SnodeMessage(
|
||||
*/
|
||||
val timestamp: Long
|
||||
) {
|
||||
internal constructor(): this("", "", -1, -1)
|
||||
|
||||
internal fun toJSON(): Map<String, String> {
|
||||
return mapOf(
|
||||
|
||||
54
libsession/src/main/res/values-fr-rFR/strings.xml
Normal file
54
libsession/src/main/res/values-fr-rFR/strings.xml
Normal 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 l’expiration des messages éphémères à %1$s</string>
|
||||
<string name="MessageRecord_s_set_disappearing_message_time_to_s">%1$s a défini l’expiration 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>
|
||||
54
libsession/src/main/res/values-fr/strings.xml
Normal file
54
libsession/src/main/res/values-fr/strings.xml
Normal 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 l’expiration des messages éphémères à %1$s</string>
|
||||
<string name="MessageRecord_s_set_disappearing_message_time_to_s">%1$s a défini l’expiration 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>
|
||||
Reference in New Issue
Block a user