From 53c4185b9402f47fc6e91c160b38ef7007a9915d Mon Sep 17 00:00:00 2001 From: Jorge Blacio Date: Tue, 17 Sep 2019 18:00:32 -0500 Subject: [PATCH] Added a fix for adding files in the composer which had a couple of issues. --- .../data/UploadAttachmentWorkerTest.kt | 3 +- .../data/DownloadAttachmentWorkerTest.kt | 2 +- .../mailbox/data/SendEmailWorkerTest.kt | 3 + .../scenes/settings/data/LogoutWorkerTest.kt | 14 ++- .../com/criptext/mail/db/dao/SignUpDao.kt | 10 +- .../scenes/composer/ComposerController.kt | 29 +++--- .../composer/data/ComposerAttachment.kt | 7 +- .../composer/data/ComposerDataSource.kt | 2 +- .../scenes/composer/data/ComposerRequest.kt | 2 +- .../scenes/composer/data/ComposerResult.kt | 7 +- .../composer/workers/LoadInitialDataWorker.kt | 6 +- .../composer/workers/SaveEmailWorker.kt | 2 +- .../workers/UploadAttachmentWorker.kt | 9 +- .../settings/cloudbackup/CloudBackupScene.kt | 15 +-- .../settings/data/SettingsDataSource.kt | 7 -- .../scenes/settings/data/SettingsRequest.kt | 1 - .../scenes/settings/data/SettingsResult.kt | 6 -- .../settings/profile/ProfileController.kt | 7 +- .../scenes/settings/workers/LogoutWorker.kt | 94 ------------------- .../signup/data/StoreAccountTransaction.kt | 4 + .../services/jobs/CloudBackupJobService.kt | 36 ++++++- .../com/criptext/mail/utils/AccountUtils.kt | 8 ++ .../mail/utils/file/ActivityMessageUtils.kt | 15 +-- .../com/criptext/mail/utils/file/PathUtil.kt | 20 +++- .../generaldatasource/data/GeneralResult.kt | 2 +- .../generaldatasource/workers/LogoutWorker.kt | 2 +- .../ComposerControllerDataSourceEventsTest.kt | 4 +- 27 files changed, 133 insertions(+), 184 deletions(-) delete mode 100644 src/main/kotlin/com/criptext/mail/scenes/settings/workers/LogoutWorker.kt diff --git a/src/androidTest/kotlin/com/criptext/mail/scenes/composer/data/UploadAttachmentWorkerTest.kt b/src/androidTest/kotlin/com/criptext/mail/scenes/composer/data/UploadAttachmentWorkerTest.kt index c6ccdf852..31a40954d 100644 --- a/src/androidTest/kotlin/com/criptext/mail/scenes/composer/data/UploadAttachmentWorkerTest.kt +++ b/src/androidTest/kotlin/com/criptext/mail/scenes/composer/data/UploadAttachmentWorkerTest.kt @@ -69,7 +69,8 @@ class UploadAttachmentWorkerTest { private fun newWorker(filepath: String): UploadAttachmentWorker = UploadAttachmentWorker(filepath = filepath, activeAccount = activeAccount, httpClient = httpClient, publishFn = {}, fileKey = null, - accountDao = db.accountDao(), filesSize = 0L, storage = storage) + accountDao = db.accountDao(), filesSize = 0L, storage = storage, + uuid = "_UUID_") @Test fun should_upload_file_without_errors() { diff --git a/src/androidTest/kotlin/com/criptext/mail/scenes/emaildetail/data/DownloadAttachmentWorkerTest.kt b/src/androidTest/kotlin/com/criptext/mail/scenes/emaildetail/data/DownloadAttachmentWorkerTest.kt index 57bd7546b..8664f53fd 100644 --- a/src/androidTest/kotlin/com/criptext/mail/scenes/emaildetail/data/DownloadAttachmentWorkerTest.kt +++ b/src/androidTest/kotlin/com/criptext/mail/scenes/emaildetail/data/DownloadAttachmentWorkerTest.kt @@ -95,7 +95,7 @@ class DownloadAttachmentWorkerTest { private fun newWorker(filepath: String): UploadAttachmentWorker = UploadAttachmentWorker(filepath = filepath, activeAccount = activeAccount, httpClient = httpClient, publishFn = {}, fileKey = null, accountDao = db.accountDao(), - storage = storage, filesSize = 0L) + storage = storage, filesSize = 0L, uuid = "_UUID_") private fun newDownloadWorker(filetoken: String): DownloadAttachmentWorker = DownloadAttachmentWorker(fileToken = filetoken, emailId = 0, diff --git a/src/androidTest/kotlin/com/criptext/mail/scenes/mailbox/data/SendEmailWorkerTest.kt b/src/androidTest/kotlin/com/criptext/mail/scenes/mailbox/data/SendEmailWorkerTest.kt index a09b34ef5..324f288df 100644 --- a/src/androidTest/kotlin/com/criptext/mail/scenes/mailbox/data/SendEmailWorkerTest.kt +++ b/src/androidTest/kotlin/com/criptext/mail/scenes/mailbox/data/SendEmailWorkerTest.kt @@ -31,6 +31,8 @@ import org.junit.Test import org.junit.rules.TemporaryFolder import org.junit.runner.RunWith import org.whispersystems.libsignal.SignalProtocolAddress +import java.util.* +import kotlin.collections.ArrayList /** @@ -215,6 +217,7 @@ class SendEmailWorkerTest { val file = folder.newFile(String.format("file_%s.png", i)) attachmentList.add(ComposerAttachment( id=0, + uuid = UUID.randomUUID().toString(), filepath=file.name, uploadProgress=100, filetoken="__FILE_TOKEN__", diff --git a/src/androidTest/kotlin/com/criptext/mail/scenes/settings/data/LogoutWorkerTest.kt b/src/androidTest/kotlin/com/criptext/mail/scenes/settings/data/LogoutWorkerTest.kt index 0ab9033ec..7b114676e 100644 --- a/src/androidTest/kotlin/com/criptext/mail/scenes/settings/data/LogoutWorkerTest.kt +++ b/src/androidTest/kotlin/com/criptext/mail/scenes/settings/data/LogoutWorkerTest.kt @@ -5,6 +5,7 @@ import androidx.test.runner.AndroidJUnit4 import com.criptext.mail.androidtest.TestActivity import com.criptext.mail.androidtest.TestDatabase import com.criptext.mail.api.HttpClient +import com.criptext.mail.db.EventLocalDB import com.criptext.mail.db.KeyValueStorage import com.criptext.mail.db.MailboxLocalDB import com.criptext.mail.db.SettingsLocalDB @@ -13,9 +14,10 @@ import com.criptext.mail.db.models.ActiveAccount import com.criptext.mail.db.models.Contact import com.criptext.mail.db.models.Label import com.criptext.mail.mocks.MockEmailData -import com.criptext.mail.scenes.settings.workers.LogoutWorker import com.criptext.mail.utils.MockedResponse import com.criptext.mail.utils.enqueueResponses +import com.criptext.mail.utils.generaldatasource.data.GeneralResult +import com.criptext.mail.utils.generaldatasource.workers.LogoutWorker import io.mockk.mockk import okhttp3.mockwebserver.MockWebServer import org.amshove.kluent.shouldBe @@ -33,7 +35,7 @@ class LogoutWorkerTest{ private lateinit var db: TestDatabase private lateinit var mailboxLocalDB: MailboxLocalDB - private lateinit var settingsLocalDB: SettingsLocalDB + private lateinit var eventLocalDB: EventLocalDB private lateinit var storage: KeyValueStorage private val activeAccount = ActiveAccount(name = "Tester", recipientId = "tester", deviceId = 1, jwt = "__JWTOKEN__", signature = "", refreshToken = "", id = 1, @@ -59,7 +61,7 @@ class LogoutWorkerTest{ signature = "", refreshToken = "__REFRESH__", isActive = true, domain = "criptext.com", isLoggedIn = true, backupPassword = null, autoBackupFrequency = 0, hasCloudBackup = false, wifiOnly = true, lastTimeBackup = null)) mailboxLocalDB = MailboxLocalDB.Default(db, mActivityRule.activity.filesDir) - settingsLocalDB = SettingsLocalDB.Default(db) + eventLocalDB = EventLocalDB(db, mActivityRule.activity.filesDir, mActivityRule.activity.cacheDir) MockEmailData.insertEmailsNeededForTests(db, listOf(Label.defaultItems.inbox), mActivityRule.activity.filesDir, activeAccount.recipientId, accountId = activeAccount.id, @@ -81,7 +83,7 @@ class LogoutWorkerTest{ val worker = newWorker() - worker.work(mockk()) as SettingsResult.Logout.Success + worker.work(mockk()) as GeneralResult.Logout.Success mailboxLocalDB.getThreadsIdsFromLabel( labelName = Label.defaultItems.inbox.text, @@ -93,10 +95,12 @@ class LogoutWorkerTest{ private fun newWorker(): LogoutWorker = LogoutWorker( - db = settingsLocalDB, + db = eventLocalDB, httpClient = httpClient, activeAccount = activeAccount, storage = storage, + accountDao = db.accountDao(), + shouldDeleteAllData = false, publishFn = {}) @After diff --git a/src/main/kotlin/com/criptext/mail/db/dao/SignUpDao.kt b/src/main/kotlin/com/criptext/mail/db/dao/SignUpDao.kt index 29e052ba7..57a3c0880 100644 --- a/src/main/kotlin/com/criptext/mail/db/dao/SignUpDao.kt +++ b/src/main/kotlin/com/criptext/mail/db/dao/SignUpDao.kt @@ -29,12 +29,15 @@ interface SignUpDao { registrationId=:registrationId, domain=:domain, isActive=:isActive, - isLoggedIn=:isLoggedIn + isLoggedIn=:isLoggedIn, + hasCloudBackup=:hasCloudBackup, + wifiOnly=:wifiOnly, + autoBackupFrequency=:backupFrequency WHERE recipientId=:recipientId """) fun updateAccount(recipientId: String, name: String, jwt: String, refreshJwt: String, deviceId: Int, identityKey: String, registrationId: Int, domain: String, - isActive: Int, isLoggedIn: Int) + isActive: Int, isLoggedIn: Int, hasCloudBackup: Boolean, wifiOnly: Boolean, backupFrequency: Int) @Insert fun insertPreKeys(preKeys : List) @@ -68,7 +71,8 @@ interface SignUpDao { updateAccount(recipientId = account.recipientId, name = account.name, deviceId = account.deviceId, domain = account.domain, isLoggedIn = if(account.isLoggedIn) 1 else 0, isActive = if(account.isActive) 1 else 0, identityKey = account.identityKeyPairB64, jwt = account.jwt, refreshJwt = account.refreshToken, - registrationId = account.registrationId) + registrationId = account.registrationId, backupFrequency = account.autoBackupFrequency, hasCloudBackup = account.hasCloudBackup, + wifiOnly = account.wifiOnly) val savedAccount = accountDao.getLoggedInAccount()!! preKeyList.forEach { it.accountId = savedAccount.id } diff --git a/src/main/kotlin/com/criptext/mail/scenes/composer/ComposerController.kt b/src/main/kotlin/com/criptext/mail/scenes/composer/ComposerController.kt index 5d36b43b8..0e7581365 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/composer/ComposerController.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/composer/ComposerController.kt @@ -2,7 +2,6 @@ package com.criptext.mail.scenes.composer import android.Manifest -import android.content.DialogInterface import android.content.pm.PackageManager import android.view.View import com.criptext.mail.BaseActivity @@ -19,12 +18,10 @@ import com.criptext.mail.scenes.ActivityMessage import com.criptext.mail.scenes.SceneController import com.criptext.mail.scenes.composer.data.* import com.criptext.mail.scenes.composer.ui.ComposerUIObserver -import com.criptext.mail.scenes.mailbox.data.EmailThread import com.criptext.mail.scenes.params.EmailDetailParams import com.criptext.mail.scenes.params.LinkingParams import com.criptext.mail.scenes.params.MailboxParams import com.criptext.mail.scenes.params.SignInParams -import com.criptext.mail.scenes.settings.data.SettingsRequest import com.criptext.mail.utils.* import com.criptext.mail.utils.file.FileUtils import com.criptext.mail.utils.generaldatasource.data.GeneralDataSource @@ -32,7 +29,6 @@ import com.criptext.mail.utils.generaldatasource.data.GeneralRequest import com.criptext.mail.utils.generaldatasource.data.GeneralResult import com.criptext.mail.utils.ui.data.DialogResult import com.criptext.mail.utils.ui.data.DialogType -import com.criptext.mail.websocket.WebSocketSingleton import java.io.File import java.util.* @@ -244,7 +240,7 @@ class ComposerController(private val storage: KeyValueStorage, EmailAddressUtils.extractEmailAddressDomain(email) in checkedData.filter { it.isCriptextDomain } .map { it.name } } - contacts.forEachIndexed { index, contact -> + contacts.forEachIndexed { _, contact -> if(contact.email in isCriptext) contact.isCriptextDomain = true } @@ -272,7 +268,7 @@ class ComposerController(private val storage: KeyValueStorage, when (result) { is GeneralResult.GetRemoteFile.Success -> { scene.dismissPreparingFileDialog() - model.attachments.addAll(result.remoteFiles.map { ComposerAttachment(it.first, it.second, model.fileKey!!) }) + model.attachments.addAll(result.remoteFiles.map { ComposerAttachment(UUID.randomUUID().toString(), it.first, it.second, model.fileKey!!) }) scene.notifyAttachmentSetChanged() handleNextUpload() } @@ -297,15 +293,15 @@ class ComposerController(private val storage: KeyValueStorage, private fun onUploadFile(result: ComposerResult.UploadFile){ when (result) { is ComposerResult.UploadFile.Register -> { - val composerAttachment = getAttachmentByPath(result.filepath) ?: return + val composerAttachment = getAttachmentByUUID(result.uuid) ?: return composerAttachment.filetoken = result.filetoken } is ComposerResult.UploadFile.Progress -> { - val composerAttachment = getAttachmentByPath(result.filepath) ?: return + val composerAttachment = getAttachmentByUUID(result.uuid) ?: return composerAttachment.uploadProgress = result.percentage } is ComposerResult.UploadFile.Success -> { - val composerAttachment = getAttachmentByPath(result.filepath) + val composerAttachment = getAttachmentByUUID(result.uuid) composerAttachment?.uploadProgress = 100 model.isUploadingAttachments = false model.filesSize = result.filesSize @@ -339,8 +335,8 @@ class ComposerController(private val storage: KeyValueStorage, scene.notifyAttachmentSetChanged() } - private fun getAttachmentByPath(filepath: String): ComposerAttachment? { - return model.attachments.firstOrNull{it.filepath == filepath} + private fun getAttachmentByUUID(uuid: String): ComposerAttachment? { + return model.attachments.firstOrNull{it.uuid == uuid} } private fun removeAttachmentByPath(filepath: String) { @@ -469,13 +465,14 @@ class ComposerController(private val storage: KeyValueStorage, private fun isReadyForSending() = (model.to.isNotEmpty() || model.cc.isNotEmpty() || model.bcc.isNotEmpty()) - private fun uploadSelectedFile(filepath: String, fileKey: String){ + private fun uploadSelectedFile(filepath: String, fileKey: String, uuid: String){ model.isUploadingAttachments = true scene.dismissPreparingFileDialog() dataSource.submitRequest(ComposerRequest.UploadAttachment( filepath = filepath, fileKey = fileKey, - filesSize = model.filesSize + filesSize = model.filesSize, + uuid = uuid )) } @@ -540,7 +537,7 @@ class ComposerController(private val storage: KeyValueStorage, val localAttachments = filesMetadata .filter(isNewAttachment) .filter {it.second != -1L} - .map{ComposerAttachment(it.first, it.second, model.fileKey!!)} + .map{ComposerAttachment(UUID.randomUUID().toString(), it.first, it.second, model.fileKey!!)} val remoteAttachments = filesMetadata .filter(isNewAttachment) .filter{ it.second == -1L } @@ -563,13 +560,13 @@ class ComposerController(private val storage: KeyValueStorage, return } val attachmentToUpload = model.attachments.firstOrNull { it.uploadProgress == -1 } ?: return - val composerAttachment = getAttachmentByPath(attachmentToUpload.filepath) + val composerAttachment = getAttachmentByUUID(attachmentToUpload.uuid) if(composerAttachment == null){ scene.showMaxFilesExceedsDialog() return }else { composerAttachment.uploadProgress = 0 - uploadSelectedFile(attachmentToUpload.filepath, composerAttachment.fileKey) + uploadSelectedFile(attachmentToUpload.filepath, composerAttachment.fileKey, composerAttachment.uuid) } } diff --git a/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerAttachment.kt b/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerAttachment.kt index 12efc7482..40ef6ec8d 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerAttachment.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerAttachment.kt @@ -4,11 +4,12 @@ import com.criptext.mail.db.AttachmentTypes import com.criptext.mail.utils.file.FileUtils import org.json.JSONArray import org.json.JSONObject +import java.util.* -data class ComposerAttachment(val id: Long, val filepath: String, var uploadProgress: Int, +data class ComposerAttachment(val id: Long, val uuid: String, val filepath: String, var uploadProgress: Int, var filetoken: String, val type: AttachmentTypes, var size: Long, val fileKey: String, val cid: String?) { - constructor(filepath: String, size: Long, fileKey: String): this (0, filepath, -1, filetoken = "", + constructor(uuid: String, filepath: String, size: Long, fileKey: String): this (0, uuid ,filepath, -1, filetoken = "", type = FileUtils.getAttachmentTypeFromPath(filepath), size = size, fileKey = fileKey, cid = null) companion object{ @@ -16,6 +17,7 @@ data class ComposerAttachment(val id: Long, val filepath: String, var uploadProg val json = JSONObject(jsonString) return ComposerAttachment( filepath = json.getString("filepath"), + uuid = json.getString("uuid"), size = json.getLong("size"), fileKey = json.getString("fileKey") ) @@ -35,6 +37,7 @@ data class ComposerAttachment(val id: Long, val filepath: String, var uploadProg attachments.forEach { val json = JSONObject() json.put("id", it.id) + json.put("uuid", it.uuid) json.put("filepath", it.filepath) json.put("uploadProgress", it.uploadProgress) json.put("filetoken", it.filetoken) diff --git a/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerDataSource.kt b/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerDataSource.kt index 3c93d57f1..a61ef02ad 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerDataSource.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerDataSource.kt @@ -54,7 +54,7 @@ class ComposerDataSource( filepath = params.filepath, httpClient = httpClient, activeAccount = activeAccount, publishFn = { res -> flushResults(res) }, fileKey = params.fileKey, - accountDao = composerLocalDB.accountDao, storage = storage) + accountDao = composerLocalDB.accountDao, storage = storage, uuid = params.uuid) is ComposerRequest.LoadInitialData -> LoadInitialDataWorker( httpClient = HttpClient.Default(), db = composerLocalDB, diff --git a/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerRequest.kt b/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerRequest.kt index 527158756..87bcbeaeb 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerRequest.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerRequest.kt @@ -15,7 +15,7 @@ sealed class ComposerRequest { val onlySave: Boolean, val attachments: List, val fileKey: String?, val originalId: Long?, val senderAccount: ActiveAccount? = null, val currentLabel: Label): ComposerRequest() - class UploadAttachment(val filepath: String, val fileKey: String?, val filesSize: Long): ComposerRequest() + class UploadAttachment(val filepath: String, val fileKey: String?, val filesSize: Long, val uuid: String): ComposerRequest() class LoadInitialData(val composerType: ComposerType, val emailId: Long): ComposerRequest() data class CheckDomain(val emails: List) : ComposerRequest() } \ No newline at end of file diff --git a/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerResult.kt b/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerResult.kt index 3430c50ce..c64c63774 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerResult.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/composer/data/ComposerResult.kt @@ -5,6 +5,7 @@ import com.criptext.mail.db.models.Account import com.criptext.mail.db.models.Contact import com.criptext.mail.email_preview.EmailPreview import com.criptext.mail.utils.UIMessage +import java.util.* /** * Created by gabriel on 2/26/18. @@ -38,9 +39,9 @@ sealed class ComposerResult { } sealed class UploadFile : ComposerResult() { - data class Success(val filepath: String, val filesSize: Long): UploadFile() - data class Register(val filepath: String, val filetoken: String): UploadFile() - data class Progress(val filepath: String, val percentage: Int): UploadFile() + data class Success(val filepath: String, val filesSize: Long, val uuid: String): UploadFile() + data class Register(val filepath: String, val filetoken: String, val uuid: String): UploadFile() + data class Progress(val filepath: String, val percentage: Int, val uuid: String): UploadFile() data class MaxFilesExceeds(val filepath: String): UploadFile() data class PayloadTooLarge(val filepath: String, val headers: ResultHeaders): UploadFile() data class Failure(val filepath: String, val message: UIMessage): UploadFile() diff --git a/src/main/kotlin/com/criptext/mail/scenes/composer/workers/LoadInitialDataWorker.kt b/src/main/kotlin/com/criptext/mail/scenes/composer/workers/LoadInitialDataWorker.kt index ab5c33d44..229d3ac88 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/composer/workers/LoadInitialDataWorker.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/composer/workers/LoadInitialDataWorker.kt @@ -13,6 +13,8 @@ import com.criptext.mail.utils.EmailAddressUtils import com.criptext.mail.utils.UIMessage import com.criptext.mail.utils.file.FileUtils import com.github.kittinunf.result.Result +import java.util.* +import kotlin.collections.ArrayList /** * Created by gabriel on 7/2/18. @@ -38,7 +40,7 @@ class LoadInitialDataWorker( private fun convertDraftToInputData(fullEmail: FullEmail): ComposerInputData { val attachments = ArrayList(fullEmail.files.map { - ComposerAttachment(0, it.name, 100, + ComposerAttachment(0, UUID.randomUUID().toString(), it.name, 100, it.token, FileUtils.getAttachmentTypeFromPath(it.name), it.size, it.fileKey, it.cid) }) @@ -119,7 +121,7 @@ class LoadInitialDataWorker( val subject = (if(fullEmail.email.subject.matches("^(Fw|FW|Fwd|FWD): .*\$".toRegex())) "" else "FW: ") + fullEmail.email.subject val attachments = ArrayList(fullEmail.files.map { - ComposerAttachment(0, it.name, 100, + ComposerAttachment(0, UUID.randomUUID().toString(), it.name, 100, it.token, FileUtils.getAttachmentTypeFromPath(it.name), it.size, it.fileKey, it.cid) }) diff --git a/src/main/kotlin/com/criptext/mail/scenes/composer/workers/SaveEmailWorker.kt b/src/main/kotlin/com/criptext/mail/scenes/composer/workers/SaveEmailWorker.kt index 787f85a23..157fe58d2 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/composer/workers/SaveEmailWorker.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/composer/workers/SaveEmailWorker.kt @@ -54,7 +54,7 @@ class SaveEmailWorker( val (newEmailId, savedMailThreadId) = saveEmail() val attachmentsSaved = dao.findFilesByEmailId(newEmailId).map { ComposerAttachment( - id = it.id, fileKey = it.fileKey, size = it.size, + id = it.id, uuid = UUID.randomUUID().toString(), fileKey = it.fileKey, size = it.size, filepath = attachments.find { file -> it.token == file.filetoken }!!.filepath, filetoken = it.token, type = attachments.find { file -> it.token == file.filetoken }!!.type, uploadProgress = attachments.find { file -> it.token == file.filetoken }!!.uploadProgress, diff --git a/src/main/kotlin/com/criptext/mail/scenes/composer/workers/UploadAttachmentWorker.kt b/src/main/kotlin/com/criptext/mail/scenes/composer/workers/UploadAttachmentWorker.kt index fb0952eae..2f9c584c1 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/composer/workers/UploadAttachmentWorker.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/composer/workers/UploadAttachmentWorker.kt @@ -28,7 +28,8 @@ import java.io.File class UploadAttachmentWorker(private val filesSize: Long, private val filepath: String, - private val httpClient: HttpClient, + private val uuid: String, + httpClient: HttpClient, private val activeAccount: ActiveAccount, private val storage: KeyValueStorage, private val accountDao: AccountDao, @@ -59,13 +60,13 @@ class UploadAttachmentWorker(private val filesSize: Long, private fun uploadFile(file: File, reporter: ProgressReporter): (String) -> Result = { fileToken -> - reporter.report(ComposerResult.UploadFile.Register(file.absolutePath, fileToken)) + reporter.report(ComposerResult.UploadFile.Register(file.absolutePath, fileToken, uuid)) Result.of { val chunks = (file.length() / chunkSize).toInt() + 1 val onNewChunkRead: (ByteArray, Int) -> Unit = { chunk, index -> reporter.report(ComposerResult.UploadFile.Progress(file.absolutePath, - index * 100 / chunks)) + index * 100 / chunks, uuid)) fileServiceAPIClient.uploadChunk(chunk = if(fileKey != null) AESUtil(fileKey).encrypt(chunk) else @@ -111,7 +112,7 @@ class UploadAttachmentWorker(private val filesSize: Long, return when (finalResult) { - is Result.Success -> ComposerResult.UploadFile.Success(filepath, size) + is Result.Success -> ComposerResult.UploadFile.Success(filepath, size, uuid) is Result.Failure -> catchException(finalResult.error) } } diff --git a/src/main/kotlin/com/criptext/mail/scenes/settings/cloudbackup/CloudBackupScene.kt b/src/main/kotlin/com/criptext/mail/scenes/settings/cloudbackup/CloudBackupScene.kt index 1ce942488..7c95f7a23 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/settings/cloudbackup/CloudBackupScene.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/settings/cloudbackup/CloudBackupScene.kt @@ -6,10 +6,7 @@ import android.widget.* import com.criptext.mail.R import com.criptext.mail.services.jobs.CloudBackupJobService import com.criptext.mail.services.jobs.CriptextJobCreator -import com.criptext.mail.utils.DateAndTimeUtils -import com.criptext.mail.utils.UIMessage -import com.criptext.mail.utils.Utility -import com.criptext.mail.utils.getLocalizedUIMessage +import com.criptext.mail.utils.* import com.criptext.mail.utils.ui.AccountSuspendedDialog import com.criptext.mail.utils.ui.MessageAndProgressDialog import com.criptext.mail.utils.ui.data.DialogType @@ -246,7 +243,7 @@ interface CloudBackupScene{ override fun scheduleCloudBackupJob(period: Int, accountId: Long, useWifiOnly: Boolean) { val cloudBackupJobService = CloudBackupJobService() - cloudBackupJobService.schedule(context, getFrequencyPeriod(period), accountId, useWifiOnly) + cloudBackupJobService.schedule(context, AccountUtils.getFrequencyPeriod(period), accountId, useWifiOnly) } override fun removeFromScheduleCloudBackupJob(accountId: Long) { @@ -266,14 +263,6 @@ interface CloudBackupScene{ preparingFileDialog.dismiss() } - private fun getFrequencyPeriod(period: Int): Long { - return when(period){ - 1 -> 86400000L * 7L - 2 -> 86400000L * 30L - else -> 86400000L - } - } - private fun setWifiOnlySwitchState(isChecked: Boolean) { backupOverSwitch.setOnCheckedChangeListener { _,_ -> } backupOverSwitch.isChecked = isChecked diff --git a/src/main/kotlin/com/criptext/mail/scenes/settings/data/SettingsDataSource.kt b/src/main/kotlin/com/criptext/mail/scenes/settings/data/SettingsDataSource.kt index dd17506f1..0f79968c9 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/settings/data/SettingsDataSource.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/settings/data/SettingsDataSource.kt @@ -22,13 +22,6 @@ class SettingsDataSource( flushResults: (SettingsResult) -> Unit): BackgroundWorker<*> { return when(params){ - is SettingsRequest.Logout -> LogoutWorker( - storage = storage, - db = settingsLocalDB, - activeAccount = activeAccount, - httpClient = httpClient, - publishFn = { res -> flushResults(res) } - ) is SettingsRequest.ResetPassword -> ForgotPasswordWorker( storage = storage, accountDao = settingsLocalDB.accountDao, diff --git a/src/main/kotlin/com/criptext/mail/scenes/settings/data/SettingsRequest.kt b/src/main/kotlin/com/criptext/mail/scenes/settings/data/SettingsRequest.kt index 39a06f44b..703a58992 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/settings/data/SettingsRequest.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/settings/data/SettingsRequest.kt @@ -1,7 +1,6 @@ package com.criptext.mail.scenes.settings.data sealed class SettingsRequest{ - class Logout: SettingsRequest() class ResetPassword: SettingsRequest() class SyncBegin: SettingsRequest() } \ No newline at end of file diff --git a/src/main/kotlin/com/criptext/mail/scenes/settings/data/SettingsResult.kt b/src/main/kotlin/com/criptext/mail/scenes/settings/data/SettingsResult.kt index 9b8b1be5e..0e36128e2 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/settings/data/SettingsResult.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/settings/data/SettingsResult.kt @@ -4,12 +4,6 @@ import com.criptext.mail.db.models.Label import com.criptext.mail.utils.UIMessage sealed class SettingsResult{ - - sealed class Logout: SettingsResult() { - class Success: Logout() - data class Failure(val message: UIMessage): Logout() - } - sealed class ResetPassword : SettingsResult() { class Success: ResetPassword() data class Failure(val message: UIMessage): ResetPassword() diff --git a/src/main/kotlin/com/criptext/mail/scenes/settings/profile/ProfileController.kt b/src/main/kotlin/com/criptext/mail/scenes/settings/profile/ProfileController.kt index c7332184e..60b31de9d 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/settings/profile/ProfileController.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/settings/profile/ProfileController.kt @@ -22,10 +22,8 @@ import com.criptext.mail.scenes.params.* import com.criptext.mail.scenes.settings.profile.data.* import com.criptext.mail.scenes.signin.data.LinkStatusData import com.criptext.mail.scenes.signin.data.UserData -import com.criptext.mail.utils.KeyboardManager -import com.criptext.mail.utils.PinLockUtils -import com.criptext.mail.utils.UIMessage -import com.criptext.mail.utils.Utility +import com.criptext.mail.services.jobs.CloudBackupJobService +import com.criptext.mail.utils.* import com.criptext.mail.utils.generaldatasource.data.GeneralDataSource import com.criptext.mail.utils.generaldatasource.data.GeneralRequest import com.criptext.mail.utils.generaldatasource.data.GeneralResult @@ -271,6 +269,7 @@ class ProfileController( private fun onLogout(result: GeneralResult.Logout){ when(result) { is GeneralResult.Logout.Success -> { + CloudBackupJobService.cancelJob(storage, result.oldAccountId) if(result.activeAccount == null) host.exitToScene(SignInParams(), null, false, true) else { diff --git a/src/main/kotlin/com/criptext/mail/scenes/settings/workers/LogoutWorker.kt b/src/main/kotlin/com/criptext/mail/scenes/settings/workers/LogoutWorker.kt deleted file mode 100644 index a72bea811..000000000 --- a/src/main/kotlin/com/criptext/mail/scenes/settings/workers/LogoutWorker.kt +++ /dev/null @@ -1,94 +0,0 @@ -package com.criptext.mail.scenes.settings.workers - -import com.criptext.mail.R -import com.criptext.mail.api.HttpClient -import com.criptext.mail.api.HttpErrorHandlingHelper -import com.criptext.mail.api.ServerErrorException -import com.criptext.mail.bgworker.BackgroundWorker -import com.criptext.mail.bgworker.ProgressReporter -import com.criptext.mail.db.KeyValueStorage -import com.criptext.mail.db.SettingsLocalDB -import com.criptext.mail.db.models.ActiveAccount -import com.criptext.mail.scenes.settings.data.SettingsAPIClient -import com.criptext.mail.scenes.settings.data.SettingsResult -import com.criptext.mail.utils.AccountUtils -import com.criptext.mail.utils.UIMessage -import com.github.kittinunf.result.Result -import com.github.kittinunf.result.flatMap -import com.github.kittinunf.result.mapError - - -/** - * Created by danieltigse on 28/06/18. - */ - -class LogoutWorker( - private val db: SettingsLocalDB, - private val storage: KeyValueStorage, - private val httpClient: HttpClient, - private val activeAccount: ActiveAccount, - override val publishFn: ( - SettingsResult.Logout) -> Unit) - : BackgroundWorker { - - override val canBeParallelized = true - private val apiClient = SettingsAPIClient(httpClient, activeAccount.jwt) - - override fun catchException(ex: Exception): SettingsResult.Logout { - if(ex is ServerErrorException) return SettingsResult.Logout.Failure(UIMessage(R.string.server_bad_status, arrayOf(ex.errorCode))) - return SettingsResult.Logout.Failure(UIMessage(R.string.local_error, arrayOf(ex.toString()))) - } - - override fun work(reporter: ProgressReporter): SettingsResult.Logout? { - val deleteOperation = workOperation() - - val sessionExpired = HttpErrorHandlingHelper.didFailBecauseInvalidSession(deleteOperation) - - val finalResult = if(sessionExpired) - newRetryWithNewSessionOperation() - else - deleteOperation - - return when (finalResult){ - is Result.Success -> { - SettingsResult.Logout.Success() - } - is Result.Failure -> { - catchException(finalResult.error) - } - } - } - - override fun cancel() { - TODO("CANCEL IS NOT IMPLEMENTED") - } - - private fun workOperation() : Result = Result.of {apiClient.postLogout()} - .mapError(HttpErrorHandlingHelper.httpExceptionsToNetworkExceptions) - .flatMap { Result.of { db.logout(activeAccount.id) } } - .flatMap { - Result.of { - val loggedOutAccounts = AccountUtils.getLastLoggedAccounts(storage) - loggedOutAccounts.add(activeAccount.userEmail) - storage.putString(KeyValueStorage.StringKey.LastLoggedUser, loggedOutAccounts.distinct().joinToString()) - } - } - - private fun newRetryWithNewSessionOperation() - : Result { - val refreshOperation = HttpErrorHandlingHelper.newRefreshSessionOperation(apiClient, - activeAccount, storage, db.accountDao) - .mapError(HttpErrorHandlingHelper.httpExceptionsToNetworkExceptions) - return when(refreshOperation){ - is Result.Success -> { - val account = ActiveAccount.loadFromStorage(storage)!! - apiClient.token = account.jwt - workOperation() - } - is Result.Failure -> { - Result.of { throw refreshOperation.error } - } - } - } - -} diff --git a/src/main/kotlin/com/criptext/mail/scenes/signup/data/StoreAccountTransaction.kt b/src/main/kotlin/com/criptext/mail/scenes/signup/data/StoreAccountTransaction.kt index 35866cfcd..74e7d9418 100644 --- a/src/main/kotlin/com/criptext/mail/scenes/signup/data/StoreAccountTransaction.kt +++ b/src/main/kotlin/com/criptext/mail/scenes/signup/data/StoreAccountTransaction.kt @@ -8,6 +8,7 @@ import com.criptext.mail.db.models.ActiveAccount import com.criptext.mail.db.models.Label import com.criptext.mail.db.models.signal.CRPreKey import com.criptext.mail.db.models.signal.CRSignedPreKey +import com.criptext.mail.services.jobs.CloudBackupJobService import com.criptext.mail.signal.SignalKeyGenerator /** @@ -45,6 +46,9 @@ class StoreAccountTransaction(private val dao: SignUpDao, extraSteps?.run() val dbAccount = accountDao.getLoggedInAccount()!! setNewUserAsActiveAccount(dbAccount) + if(dbAccount.hasCloudBackup){ + CloudBackupJobService.scheduleJob(keyValueStorage, dbAccount) + } } if(!keepData) { diff --git a/src/main/kotlin/com/criptext/mail/services/jobs/CloudBackupJobService.kt b/src/main/kotlin/com/criptext/mail/services/jobs/CloudBackupJobService.kt index ce0d73a6b..b7b9ab536 100644 --- a/src/main/kotlin/com/criptext/mail/services/jobs/CloudBackupJobService.kt +++ b/src/main/kotlin/com/criptext/mail/services/jobs/CloudBackupJobService.kt @@ -9,22 +9,22 @@ import android.net.NetworkCapabilities import android.net.NetworkInfo import android.os.Build import android.util.Log -import androidx.core.app.NotificationCompat import com.criptext.mail.R import com.criptext.mail.androidui.CriptextNotification.Companion.JOB_BACKUP_ID import com.criptext.mail.bgworker.ProgressReporter import com.criptext.mail.db.AppDatabase import com.criptext.mail.db.KeyValueStorage import com.criptext.mail.db.dao.AccountDao +import com.criptext.mail.db.models.Account import com.criptext.mail.db.models.ActiveAccount import com.criptext.mail.push.PushData import com.criptext.mail.push.notifiers.JobBackupNotifier -import com.criptext.mail.scenes.settings.cloudbackup.data.CloudBackupDataSource import com.criptext.mail.scenes.settings.cloudbackup.data.CloudBackupResult import com.criptext.mail.scenes.settings.cloudbackup.workers.DataFileCreationWorker import com.criptext.mail.scenes.settings.cloudbackup.workers.DeleteOldBackupWorker import com.criptext.mail.scenes.settings.cloudbackup.workers.UploadBackupToDriveWorker import com.criptext.mail.services.data.JobIdData +import com.criptext.mail.utils.AccountUtils import com.criptext.mail.utils.UIMessage import com.criptext.mail.utils.getLocalizedUIMessage import com.evernote.android.job.Job @@ -113,9 +113,9 @@ class CloudBackupJobService: Job() { val builder = JobRequest.Builder(JOB_TAG) builder.setRequiredNetworkType(JobRequest.NetworkType.ANY) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - builder.setPeriodic(intervalMillis, JobInfo.getMinFlexMillis()) + builder.setPeriodic(900000, JobInfo.getMinFlexMillis()) }else { - builder.setPeriodic(intervalMillis) + builder.setPeriodic(900000) } val id = builder.build() .schedule() @@ -286,13 +286,41 @@ class CloudBackupJobService: Job() { companion object { const val JOB_TAG = "CRIPTEXT_CLOUD_BACKUP_JOB_SERVICE" + fun scheduleJob(storage: KeyValueStorage, account: Account){ + + val builder = JobRequest.Builder(JOB_TAG) + builder.setRequiredNetworkType(JobRequest.NetworkType.ANY) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + builder.setPeriodic(AccountUtils.getFrequencyPeriod(account.autoBackupFrequency), JobInfo.getMinFlexMillis()) + }else { + builder.setPeriodic(AccountUtils.getFrequencyPeriod(account.autoBackupFrequency)) + } + val id = builder.build() + .schedule() + val savedJobsString = storage.getString(KeyValueStorage.StringKey.SavedJobs, "") + val listOfJobs = if(savedJobsString.isEmpty()) mutableListOf() + else JobIdData.fromJson(savedJobsString) + val accountSavedData = listOfJobs.find { it.accountId == account.id} + if(accountSavedData != null) { + listOfJobs.remove(accountSavedData) + } + listOfJobs.add(JobIdData(account.id, id, account.wifiOnly)) + storage.putString(KeyValueStorage.StringKey.SavedJobs, JobIdData.toJSON(listOfJobs).toString()) + } + fun cancelJob(storage: KeyValueStorage, accountId: Long){ val savedJobsString = storage.getString(KeyValueStorage.StringKey.SavedJobs, "") val listOfJobs = if(savedJobsString.isEmpty()) mutableListOf() else JobIdData.fromJson(savedJobsString) val accountSavedData = listOfJobs.find { it.accountId == accountId} if(accountSavedData != null) { + listOfJobs.remove(accountSavedData) + if(listOfJobs.isNotEmpty()) + storage.putString(KeyValueStorage.StringKey.SavedJobs, JobIdData.toJSON(listOfJobs).toString()) + else + storage.remove(listOf(KeyValueStorage.StringKey.SavedJobs)) JobManager.instance().cancel(accountSavedData.jobId) + Log.e("JOBSERVICE:", "Canceled!!!") } } } diff --git a/src/main/kotlin/com/criptext/mail/utils/AccountUtils.kt b/src/main/kotlin/com/criptext/mail/utils/AccountUtils.kt index 6a1f45efe..cc6d8e901 100644 --- a/src/main/kotlin/com/criptext/mail/utils/AccountUtils.kt +++ b/src/main/kotlin/com/criptext/mail/utils/AccountUtils.kt @@ -19,4 +19,12 @@ object AccountUtils { return if(storageLastUser.isEmpty()) mutableListOf() else storageLastUser.split(",").map { it.trim() }.toMutableList() } + + fun getFrequencyPeriod(period: Int): Long { + return when(period){ + 1 -> 86400000L * 7L + 2 -> 86400000L * 30L + else -> 86400000L + } + } } \ No newline at end of file diff --git a/src/main/kotlin/com/criptext/mail/utils/file/ActivityMessageUtils.kt b/src/main/kotlin/com/criptext/mail/utils/file/ActivityMessageUtils.kt index 378d42a59..a9f82d938 100644 --- a/src/main/kotlin/com/criptext/mail/utils/file/ActivityMessageUtils.kt +++ b/src/main/kotlin/com/criptext/mail/utils/file/ActivityMessageUtils.kt @@ -3,18 +3,13 @@ package com.criptext.mail.utils.file import android.content.ContentResolver import android.content.Context import android.content.Intent +import android.os.ParcelFileDescriptor import com.criptext.mail.scenes.ActivityMessage object ActivityMessageUtils { fun getAddAttachmentsActivityMessage(data: Intent, contentResolver: ContentResolver?, ctx: Context): ActivityMessage.AddAttachments? { val clipData = data.clipData - if(clipData == null) { - data.data?.also { uri -> - val attachment = FileUtils.getPathAndSizeFromUri(uri, contentResolver, ctx) - if (attachment != null) - return ActivityMessage.AddAttachments(listOf(attachment)) - } - }else{ + if(clipData != null && data.data == null){ val attachmentList = mutableListOf>() for (i in 0 until clipData.itemCount) { clipData.getItemAt(i).also { item -> @@ -26,6 +21,12 @@ object ActivityMessageUtils { } if (attachmentList.isNotEmpty()) return ActivityMessage.AddAttachments(attachmentList) + } else if(data.data != null) { + data.data?.also { uri -> + val attachment = FileUtils.getPathAndSizeFromUri(uri, contentResolver, ctx) + if (attachment != null) + return ActivityMessage.AddAttachments(listOf(attachment)) + } } return null } diff --git a/src/main/kotlin/com/criptext/mail/utils/file/PathUtil.kt b/src/main/kotlin/com/criptext/mail/utils/file/PathUtil.kt index 102e0cd8e..cf3ac904f 100644 --- a/src/main/kotlin/com/criptext/mail/utils/file/PathUtil.kt +++ b/src/main/kotlin/com/criptext/mail/utils/file/PathUtil.kt @@ -35,11 +35,23 @@ object PathUtil { isExternalStorageDocument(uri) -> { val docId = DocumentsContract.getDocumentId(uri) val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() - return if(split[0] != "0"){ - "storage/${split[0]}/${split[1]}" - } else { - Environment.getExternalStorageDirectory().absolutePath + "/" + split[1] + if (split.size == 2) { + val type = split[0] + val realDocId = split[1] + + return when { + "primary".equals(type, true) -> { + return Environment.getExternalStorageDirectory().absolutePath + "/" + realDocId + } + split[0] != "0" -> { + "storage/${split[0]}/${split[1]}" + } + else -> { + Environment.getExternalStorageDirectory().absolutePath + "/" + realDocId + } + } } + return null } isDownloadsDocument(uri) -> { val id = DocumentsContract.getDocumentId(uri) diff --git a/src/main/kotlin/com/criptext/mail/utils/generaldatasource/data/GeneralResult.kt b/src/main/kotlin/com/criptext/mail/utils/generaldatasource/data/GeneralResult.kt index a975f376e..0cc17024d 100644 --- a/src/main/kotlin/com/criptext/mail/utils/generaldatasource/data/GeneralResult.kt +++ b/src/main/kotlin/com/criptext/mail/utils/generaldatasource/data/GeneralResult.kt @@ -136,7 +136,7 @@ sealed class GeneralResult { } sealed class Logout: GeneralResult() { - data class Success(val activeAccount: ActiveAccount?, val oldAccountEmail: String): Logout() + data class Success(val activeAccount: ActiveAccount?, val oldAccountEmail: String, val oldAccountId: Long): Logout() class Failure: Logout() } diff --git a/src/main/kotlin/com/criptext/mail/utils/generaldatasource/workers/LogoutWorker.kt b/src/main/kotlin/com/criptext/mail/utils/generaldatasource/workers/LogoutWorker.kt index 7fb55ba3d..b85985971 100644 --- a/src/main/kotlin/com/criptext/mail/utils/generaldatasource/workers/LogoutWorker.kt +++ b/src/main/kotlin/com/criptext/mail/utils/generaldatasource/workers/LogoutWorker.kt @@ -52,7 +52,7 @@ class LogoutWorker( return when (finalResult){ is Result.Success -> { - GeneralResult.Logout.Success(newActiveAccount, activeAccount.userEmail) + GeneralResult.Logout.Success(newActiveAccount, activeAccount.userEmail, activeAccount.id) } is Result.Failure -> { GeneralResult.Logout.Failure() diff --git a/src/test/java/com/criptext/mail/scenes/composer/ComposerControllerDataSourceEventsTest.kt b/src/test/java/com/criptext/mail/scenes/composer/ComposerControllerDataSourceEventsTest.kt index dd32d66ad..b5c6fd3bd 100644 --- a/src/test/java/com/criptext/mail/scenes/composer/ComposerControllerDataSourceEventsTest.kt +++ b/src/test/java/com/criptext/mail/scenes/composer/ComposerControllerDataSourceEventsTest.kt @@ -101,7 +101,7 @@ class ComposerControllerDataSourceEventsTest: ComposerControllerTest() { runAfterSelectingAnAttachment { clearMocks(host) simulateAddAttachmentEvent(ComposerResult.UploadFile.Register(filepath = "/test.pdf", - filetoken = mockedFiletoken)) + filetoken = mockedFiletoken, uuid = model.attachments[0].uuid)) model.attachments[0].filetoken `should be` mockedFiletoken verify { scene.notifyAttachmentSetChanged() } @@ -112,7 +112,7 @@ class ComposerControllerDataSourceEventsTest: ComposerControllerTest() { fun `after receiving ack of success file, the file should be at 100% progress`() { runAfterSelectingAnAttachment { clearMocks(host) - simulateAddAttachmentEvent(ComposerResult.UploadFile.Success(filepath = "/test.pdf", filesSize = 0L)) + simulateAddAttachmentEvent(ComposerResult.UploadFile.Success(filepath = "/test.pdf", filesSize = 0L, uuid = model.attachments[0].uuid)) model.attachments[0].uploadProgress `should be` 100 verify { scene.notifyAttachmentSetChanged() }