Skip to content
This repository has been archived by the owner on May 14, 2024. It is now read-only.

Commit

Permalink
Merge branch 'prerelease' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
Zorica Stojchevska committed Dec 3, 2021
2 parents 2fced11 + 38855a2 commit cc26209
Show file tree
Hide file tree
Showing 17 changed files with 419 additions and 125 deletions.
23 changes: 5 additions & 18 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import androidx.room.Index
import androidx.room.PrimaryKey
import ch.protonmail.android.api.models.AttachmentHeaders
import ch.protonmail.android.utils.AppUtil
import ch.protonmail.android.utils.MessageUtils.isLocalAttachmentId
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName
import java.io.ByteArrayOutputStream
Expand Down Expand Up @@ -79,7 +80,7 @@ data class Attachment @JvmOverloads constructor(
@ColumnInfo(name = COLUMN_ATTACHMENT_MESSAGE_ID)
var messageId: String = "",
@ColumnInfo(name = COLUMN_ATTACHMENT_UPLOADED)
var isUploaded: Boolean = false,
var isUploaded: Boolean = !isLocalAttachmentId(attachmentId),
@ColumnInfo(name = COLUMN_ATTACHMENT_UPLOADING)
var isUploading: Boolean = false,
@ColumnInfo(name = COLUMN_ATTACHMENT_SIGNATURE)
Expand Down Expand Up @@ -203,17 +204,16 @@ data class Attachment @JvmOverloads constructor(
keyPackets = localAttachment.keyPackets,
headers = localAttachment.headers
)

}

@Throws(MessagingException::class, NoSuchAlgorithmException::class, IOException::class)
fun fromMimeAttachment(part: Part, message_id: String, count: Int): Attachment {
fun fromMimeAttachment(part: Part, messageId: String, count: Int): Attachment {
val data = getBytesFromInputStream(part.inputStream)
return fromMimeAttachment(part, data, message_id, count)
return fromMimeAttachment(part, data, messageId, count)
}

@Throws(MessagingException::class, NoSuchAlgorithmException::class, IOException::class)
fun fromMimeAttachment(part: Part, data: ByteArray, message_id: String, count: Int): Attachment {
fun fromMimeAttachment(part: Part, data: ByteArray, messageId: String, count: Int): Attachment {
val attachment = Attachment()

attachment.fileName = part.fileName ?: "default"
Expand All @@ -237,18 +237,18 @@ data class Attachment @JvmOverloads constructor(
md5.digest().forEach { b ->
format.format("%02x", b)
}
attachment.attachmentId = "PGPAttachment/$message_id/$format/$count"
attachment.attachmentId = "PGPAttachment/$messageId/$format/$count"
}
attachment.fileSize = data.size.toLong()
attachment.mimeType = part.contentType.replace("(\\s*)?[\\r\\n]+(\\s*)".toRegex(), "")
attachment.mimeData = data
attachment.messageId = message_id
attachment.messageId = messageId
return attachment
}

@Throws(MessagingException::class, NoSuchAlgorithmException::class, IOException::class)
fun fromMimeAttachment(data: ByteArray, headers: InternetHeaders, message_id: String, count: Int): Attachment {
return fromMimeAttachment(MimeBodyPart(headers, ByteArray(0)), data, message_id, count)
fun fromMimeAttachment(data: ByteArray, headers: InternetHeaders, messageId: String, count: Int): Attachment {
return fromMimeAttachment(MimeBodyPart(headers, ByteArray(0)), data, messageId, count)
}

@Throws(IOException::class)
Expand All @@ -273,7 +273,12 @@ data class Attachment @JvmOverloads constructor(
useRandomIds: Boolean = true
): List<Attachment> {
return localAttachmentList.map { localAttachment ->
fromLocalAttachment(messagesDatabase, localAttachment, localAttachmentList.indexOf(localAttachment).toLong(), useRandomIds)
fromLocalAttachment(
messagesDatabase,
localAttachment,
localAttachmentList.indexOf(localAttachment).toLong(),
useRandomIds
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import androidx.work.WorkInfo
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import ch.protonmail.android.R
import ch.protonmail.android.activities.messageDetails.repository.MessageDetailsRepository
import ch.protonmail.android.api.models.room.messages.Message
import ch.protonmail.android.api.models.room.pendingActions.PendingActionsDao
Expand All @@ -57,8 +58,8 @@ internal const val KEY_OUTPUT_RESULT_UPLOAD_ATTACHMENTS_ERROR = "keyUploadAttach
private const val UPLOAD_ATTACHMENTS_WORK_NAME_PREFIX = "uploadAttachmentUniqueWorkName"
private const val UPLOAD_ATTACHMENTS_MAX_RETRIES = 1

class UploadAttachments @WorkerInject constructor(
@Assisted context: Context,
class UploadAttachmentsWorker @WorkerInject constructor(
@Assisted val context: Context,
@Assisted params: WorkerParameters,
private val dispatchers: DispatcherProvider,
private val attachmentsRepository: AttachmentsRepository,
Expand All @@ -81,7 +82,14 @@ class UploadAttachments @WorkerInject constructor(
Timber.w("Calling upload attachments from inside worker for $messageId.")
return@withContext when (val result = upload(newAttachments, message, addressCrypto, isMessageSending)) {
is Result.Success -> ListenableWorker.Result.success()
is Result.Failure -> retryOrFail(result.error, messageId = message.messageId)
is Result.Failure.UploadAttachment -> retryOrFail(
result.error,
messageId = message.messageId
)
is Result.Failure.InvalidAttachment -> failureWithError(
result.error,
messageId = message.messageId
)
}
}

Expand Down Expand Up @@ -111,7 +119,7 @@ class UploadAttachments @WorkerInject constructor(

if (result is AttachmentsRepository.Result.Failure) {
pendingActionsDao.deletePendingUploadByMessageId(messageId)
return@withContext Result.Failure(result.error)
return@withContext Result.Failure.UploadAttachment(result.error)
}
}

Expand All @@ -126,12 +134,20 @@ class UploadAttachments @WorkerInject constructor(
messageId: String
): Result.Failure? {
attachmentIds.forEach { attachmentId ->
val attachment = messageDetailsRepository.findAttachmentById(attachmentId)
val attachment = messageDetailsRepository.findAttachmentById(attachmentId) ?: return@forEach

if (!attachment.isUploaded && (attachment.filePath == null || attachment.doesFileExist.not())) {
return Result.Failure.InvalidAttachment(
String.format(context.getString(R.string.attachment_failed_message_drafted),
attachment.fileName
)
)
}

if (attachment?.filePath == null || attachment.isUploaded || attachment.doesFileExist.not()) {
if (attachment.isUploaded) {
Timber.d(
"Skipping attachment ${attachment?.attachmentId}: " +
"not found, invalid or was already uploaded = ${attachment?.isUploaded}"
"Skipping attachment ${attachment.attachmentId}: " +
"was already uploaded = ${attachment.isUploaded}"
)
return@forEach
}
Expand All @@ -149,7 +165,7 @@ class UploadAttachments @WorkerInject constructor(
is AttachmentsRepository.Result.Failure -> {
Timber.e("UploadAttachment $attachmentId to API for messageId $messageId FAILED.")
pendingActionsDao.deletePendingUploadByMessageId(messageId)
return Result.Failure(result.error)
return Result.Failure.UploadAttachment(result.error)
}
}

Expand Down Expand Up @@ -188,15 +204,28 @@ class UploadAttachments @WorkerInject constructor(
return failureWithError(error, exception, messageId)
}

private fun failureWithError(error: String, exception: Throwable? = null, messageId: String?): ListenableWorker.Result {
Timber.e("UploadAttachments Worker failed permanently for $messageId. error = $error, exception = $exception. FAILING")
val errorData = workDataOf(KEY_OUTPUT_RESULT_UPLOAD_ATTACHMENTS_ERROR to error)
private fun failureWithError(
error: String,
exception: Throwable? = null,
messageId: String?
): ListenableWorker.Result {
Timber.e("UploadAttachments Worker failed permanently for " +
"$messageId. error = $error, exception = $exception. FAILING")
val errorData = workDataOf(
KEY_OUTPUT_RESULT_UPLOAD_ATTACHMENTS_ERROR to error,
)
messageId?.let { pendingActionsDao.deletePendingUploadByMessageId(it) }
return ListenableWorker.Result.failure(errorData)
}

sealed class Result {
object Success : Result()
data class Failure(val error: String) : Result()
sealed class Failure : Result() {
abstract val error: String

class UploadAttachment(override val error: String) : Failure()
class InvalidAttachment(override val error: String) : Failure()
}
}

class Enqueuer @Inject constructor(private val workManager: WorkManager) {
Expand All @@ -209,7 +238,7 @@ class UploadAttachments @WorkerInject constructor(
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val uploadAttachmentsRequest = OneTimeWorkRequestBuilder<UploadAttachments>()
val uploadAttachmentsRequest = OneTimeWorkRequestBuilder<UploadAttachmentsWorker>()
.setConstraints(constraints)
.setInputData(
workDataOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import ch.protonmail.android.compose.send.SendMessageWorkerError.FetchSendPrefer
import ch.protonmail.android.compose.send.SendMessageWorkerError.MessageAlreadySent
import ch.protonmail.android.compose.send.SendMessageWorkerError.MessageNotFound
import ch.protonmail.android.compose.send.SendMessageWorkerError.SavedDraftMessageNotFound
import ch.protonmail.android.compose.send.SendMessageWorkerError.UploadAttachmentsFailed
import ch.protonmail.android.compose.send.SendMessageWorkerError.UserVerificationNeeded
import ch.protonmail.android.core.Constants
import ch.protonmail.android.core.Constants.MessageLocationType.ALL_MAIL
Expand Down Expand Up @@ -172,7 +173,10 @@ class SendMessageWorker @WorkerInject constructor(
saveMessageAsSent(message)
failureWithError(MessageAlreadySent)
}

is SaveDraftResult.UploadDraftAttachmentsFailed -> {
pendingActionsDao.deletePendingSendByMessageId(message.messageId ?: "")
failureWithError(UploadAttachmentsFailed)
}
else -> {
pendingActionsDao.deletePendingSendByMessageId(message.messageId ?: "")
showSendMessageError(message.subject)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ enum class SendMessageWorkerError {
ErrorPerformingApiRequest,
UserVerificationNeeded,
ApiRequestReturnedBadBodyCode,
MessageAlreadySent
MessageAlreadySent,
UploadAttachmentsFailed
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,10 @@ interface INotificationServer {
)

fun notifySaveDraftError(errorMessage: String, messageSubject: String?, username: String)

fun notifyAttachmentUploadError(
errorMessage: String,
messageSubject: String?,
username: String
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ private const val CHANNEL_ID_ONGOING_OPS = "ongoingOperations"
private const val CHANNEL_ID_ACCOUNT = "account"
private const val CHANNEL_ID_ATTACHMENTS = "attachments"
private const val NOTIFICATION_ID_SAVE_DRAFT_ERROR = 6812
private const val NOTIFICATION_ID_ATTACHMENT_ERROR = 6813
private const val NOTIFICATION_GROUP_ID_EMAIL = 99
private const val NOTIFICATION_ID_VERIFICATION = 2
private const val NOTIFICATION_ID_LOGGED_OUT = 3
Expand Down Expand Up @@ -347,7 +348,7 @@ class NotificationServer @Inject constructor(
"com.android.systemui", ringtoneUri, Intent.FLAG_GRANT_READ_URI_PERMISSION
)
}
} ?: RingtoneManager.getDefaultUri(TYPE_NOTIFICATION)
} ?: RingtoneManager.getDefaultUri(TYPE_NOTIFICATION)
} catch (e: Exception) {
Timber.i(e, "Unable to set notification ringtone")
RingtoneManager.getDefaultUri(TYPE_NOTIFICATION)
Expand Down Expand Up @@ -557,41 +558,67 @@ class NotificationServer @Inject constructor(
notificationManager.notify(username.hashCode() + NOTIFICATION_ID_SENDING_FAILED, notification)
}

override fun notifyMultipleErrorSendingMessage(
unreadSendingFailedNotifications: List<SendingFailedNotification>,
user: User
private fun notifyErrorWithTitle(
username: String,
title: String,
bigText: String,
summaryText: String = username,
notificationID: Int
) {

val notificationTitle = context.getString(R.string.message_sending_failures, unreadSendingFailedNotifications.size)

// Create Notification Style
val bigTextStyle = NotificationCompat.BigTextStyle()
.setBigContentTitle(notificationTitle)
.setSummaryText(user.defaultEmail ?: user.username ?: context.getString(R.string.app_name))
.bigText(createSpannableBigText(unreadSendingFailedNotifications))
.setBigContentTitle(title)
.setSummaryText(summaryText)
.bigText(bigText)

// Create notification builder
val notificationBuilder = createGenericErrorSendingMessageNotification(user.username)
val notificationBuilder = createGenericErrorSendingMessageNotification(username)
.setStyle(bigTextStyle)

// Build and show notification
val notification = notificationBuilder.build()
notificationManager.notify(user.username.hashCode() + NOTIFICATION_ID_SENDING_FAILED, notification)
notificationManager.notify(username.hashCode() + notificationID, notification)
}

override fun notifySaveDraftError(errorMessage: String, messageSubject: String?, username: String) {
val title = context.getString(R.string.failed_saving_draft_online, messageSubject)

val bigTextStyle = NotificationCompat.BigTextStyle()
.setBigContentTitle(title)
.setSummaryText(username)
.bigText(errorMessage)
override fun notifyMultipleErrorSendingMessage(
unreadSendingFailedNotifications: List<SendingFailedNotification>,
user: User
) {
val notificationTitle = context.getString(
R.string.message_sending_failures, unreadSendingFailedNotifications.size
)

val notificationBuilder = createGenericErrorSendingMessageNotification(username)
.setStyle(bigTextStyle)
notifyErrorWithTitle(
user.username,
notificationTitle,
createSpannableBigText(unreadSendingFailedNotifications).toString(),
user.defaultEmail ?: user.username ?: context.getString(R.string.app_name),
NOTIFICATION_ID_SENDING_FAILED
)
}

val notification = notificationBuilder.build()
notificationManager.notify(username.hashCode() + NOTIFICATION_ID_SAVE_DRAFT_ERROR, notification)
override fun notifySaveDraftError(
errorMessage: String,
messageSubject: String?,
username: String
) {
val title = context.getString(R.string.failed_saving_draft_online, messageSubject)
notifyErrorWithTitle(
username,
title,
errorMessage,
notificationID = NOTIFICATION_ID_SAVE_DRAFT_ERROR
)
}

override fun notifyAttachmentUploadError(
errorMessage: String,
messageSubject: String?,
username: String
) {
val title = context.getString(R.string.failed_uploading_attachment_online, messageSubject)
notifyErrorWithTitle(
username,
title,
errorMessage,
notificationID = NOTIFICATION_ID_ATTACHMENT_ERROR
)
}
}
Loading

0 comments on commit cc26209

Please sign in to comment.