Skip to content

Commit

Permalink
Added MessagesMovingJobService. Refactored code.| #504
Browse files Browse the repository at this point in the history
  • Loading branch information
DenBond7 committed Oct 10, 2019
1 parent 76b80aa commit 8487f53
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 3 deletions.
5 changes: 5 additions & 0 deletions FlowCrypt/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,11 @@
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" />

<service
android:name=".jobscheduler.MessagesMovingJobService"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" />

<service
android:name=".jobscheduler.ForwardedAttachmentsDownloaderJobService"
android:exported="false"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,14 @@ class AccountDaoSource : BaseDaoSource() {
* @param context Interface to global information about an application environment.
* @return The [AccountDao];
*/
fun getActiveAccountInformation(context: Context): AccountDao? {
fun getActiveAccountInformation(context: Context?): AccountDao? {
val selection = "$COL_IS_ACTIVE = ?"
val cursor = context.contentResolver.query(baseContentUri, null, selection, arrayOf("1"), null)
val cursor = context?.contentResolver?.query(baseContentUri, null, selection, arrayOf("1"),
null)

var account: AccountDao? = null

if (cursor != null && cursor.moveToFirst()) {
if (cursor?.moveToFirst() == true) {
account = getCurrentAccountDao(context, cursor)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,33 @@ class MessageDaoSource : BaseDaoSource() {
return generalMsgDetailsList
}

/**
* Get messages of some folder by the given state.
*
* @param context Interface to global information about an application environment.
* @param email The email of the [LocalFolder].
* @param state The message state.
* @return A list of [GeneralMessageDetails] objects.
*/
fun getMsgsWithState(context: Context, email: String, state: MessageState):
List<GeneralMessageDetails> {
val contentResolver = context.contentResolver
val selection = "$COL_EMAIL= ? AND $COL_STATE = ?"
val cursor = contentResolver.query(baseContentUri,
null, selection, arrayOf(email, state.value.toString()), null)

val generalMsgDetailsList = mutableListOf<GeneralMessageDetails>()

if (cursor != null) {
while (cursor.moveToNext()) {
generalMsgDetailsList.add(getMsgInfo(cursor))
}
cursor.close()
}

return generalMsgDetailsList
}

/**
* Get all messages of the outbox folder.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ class JobIdManager {
const val JOB_TYPE_SEND_MESSAGES = 5
const val JOB_TYPE_DOWNLOAD_FORWARDED_ATTACHMENTS = 6
const val JOB_TYPE_FEEDBACK = 6
const val JOB_TYPE_MOVE_MESSAGES = 7
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/*
* © 2016-2019 FlowCrypt Limited. Limitations apply. Contact [email protected]
* Contributors: DenBond7
*/

package com.flowcrypt.email.jobscheduler

import android.app.job.JobInfo
import android.app.job.JobParameters
import android.app.job.JobScheduler
import android.app.job.JobService
import android.content.ComponentName
import android.content.Context
import android.os.AsyncTask
import android.util.Log
import com.flowcrypt.email.api.email.FoldersManager
import com.flowcrypt.email.api.email.protocol.OpenStoreHelper
import com.flowcrypt.email.database.MessageState
import com.flowcrypt.email.database.dao.source.AccountDao
import com.flowcrypt.email.database.dao.source.AccountDaoSource
import com.flowcrypt.email.database.dao.source.imap.MessageDaoSource
import com.flowcrypt.email.util.LogsUtil
import com.flowcrypt.email.util.exception.ExceptionUtil
import com.flowcrypt.email.util.exception.FolderNotAvailableException
import com.sun.mail.imap.IMAPFolder
import java.lang.ref.WeakReference
import javax.mail.Folder
import javax.mail.Message
import javax.mail.Session
import javax.mail.Store

/**
* This [JobService] moves messages from one folder to another one (archiving, deleting, total
* deleting and etc).
*
* @author Denis Bondarenko
* Date: 10/9/19
* Time: 7:05 PM
* E-mail: [email protected]
*/
class MessagesMovingJobService : JobService() {

override fun onCreate() {
super.onCreate()
LogsUtil.d(TAG, "onCreate")
}

override fun onDestroy() {
super.onDestroy()
LogsUtil.d(TAG, "onDestroy")
}

override fun onStartJob(jobParameters: JobParameters): Boolean {
LogsUtil.d(TAG, "onStartJob")
MoveMessagesAsyncTask(this).execute(jobParameters)
return true
}

override fun onStopJob(jobParameters: JobParameters): Boolean {
LogsUtil.d(TAG, "onStopJob")
jobFinished(jobParameters, true)
return false
}

/**
* This is an implementation of [AsyncTask] which sends the outgoing messages.
*/
private class MoveMessagesAsyncTask internal constructor(jobService: MessagesMovingJobService)
: AsyncTask<JobParameters, Boolean, JobParameters>() {
private val weakRef: WeakReference<MessagesMovingJobService> = WeakReference(jobService)

private var sess: Session? = null
private var store: Store? = null
private var isFailed: Boolean = false

override fun doInBackground(vararg params: JobParameters): JobParameters {
LogsUtil.d(TAG, "doInBackground")
try {
if (weakRef.get() != null) {
val context = weakRef.get()?.applicationContext
val account = AccountDaoSource().getActiveAccountInformation(context)
val msgDaoSource = MessageDaoSource()

if (context != null && account != null) {
val candidatesForArchiving = msgDaoSource.getMsgsWithState(context, account.email,
MessageState.PENDING_ARCHIVING)

if (candidatesForArchiving.isNotEmpty()) {
sess = OpenStoreHelper.getAccountSess(context, account)
store = OpenStoreHelper.openStore(context, account, sess!!)
}

if (candidatesForArchiving.isNotEmpty()) {
archiveMsgs(context, account, msgDaoSource, store!!)
}

store?.close()
}
}

publishProgress(false)
} catch (e: Exception) {
e.printStackTrace()
publishProgress(true)
}

return params[0]
}

override fun onPostExecute(jobParameters: JobParameters) {
LogsUtil.d(TAG, "onPostExecute")
try {
weakRef.get()?.jobFinished(jobParameters, isFailed)
} catch (e: NullPointerException) {
e.printStackTrace()
}

}

override fun onProgressUpdate(vararg values: Boolean?) {
super.onProgressUpdate(*values)
isFailed = values[0] ?: true
}

fun archiveMsgs(context: Context, account: AccountDao, msgDaoSource: MessageDaoSource, store: Store) {
val foldersManager = FoldersManager.fromDatabase(context, account.email)
val inboxFolder = foldersManager.findInboxFolder() ?: return
val allMsgsFolder = foldersManager.folderAll ?: return

val srcFolder = store.getFolder(inboxFolder.fullName) as IMAPFolder
val destFolder = store.getFolder(allMsgsFolder.fullName) as IMAPFolder

if (!srcFolder.exists()) {
throw FolderNotAvailableException("The invalid source folder: \"${srcFolder}\"")
}

srcFolder.open(Folder.READ_WRITE)

if (!destFolder.exists()) {
throw FolderNotAvailableException("The invalid destination folder: \"$destFolder\"")
}

destFolder.open(Folder.READ_WRITE)

while (true) {
val candidatesForArchiving = msgDaoSource.getMsgsWithState(context, account.email,
MessageState.PENDING_ARCHIVING)

if (candidatesForArchiving.isEmpty()) {
break
} else {
val uidList = candidatesForArchiving.map { it.uid.toLong() }
val msgs: List<Message> = srcFolder.getMessagesByUID(uidList.toLongArray()).filterNotNull()

if (msgs.isNotEmpty()) {
srcFolder.moveMessages(msgs.toTypedArray(), destFolder)
msgDaoSource.deleteMsgsByUID(context, account.email, inboxFolder.fullName, uidList)
}
}
}

destFolder.close(false)
srcFolder.close(false)
}
}

companion object {

private val TAG = MessagesMovingJobService::class.java.simpleName

@JvmStatic
fun schedule(context: Context?) {
context ?: return

val jobInfoBuilder = JobInfo.Builder(JobIdManager.JOB_TYPE_MOVE_MESSAGES,
ComponentName(context, MessagesMovingJobService::class.java))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)

val scheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler

for (jobInfo in scheduler.allPendingJobs) {
if (jobInfo.id == JobIdManager.JOB_TYPE_MOVE_MESSAGES) {
//skip schedule a new job if we already have another one
LogsUtil.d(TAG, "A job has already scheduled! Skip scheduling a new job.")
return
}
}

val result = scheduler.schedule(jobInfoBuilder.build())
if (result == JobScheduler.RESULT_SUCCESS) {
LogsUtil.d(TAG, "A job has scheduled successfully")
} else {
val errorMsg = "Error. Can't schedule a job"
Log.e(TAG, errorMsg)
ExceptionUtil.handleError(IllegalStateException(errorMsg))
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.flowcrypt.email.database.dao.source.AccountDao
import com.flowcrypt.email.database.dao.source.AccountDaoSource
import com.flowcrypt.email.database.dao.source.ActionQueueDaoSource
import com.flowcrypt.email.jobscheduler.ForwardedAttachmentsDownloaderJobService
import com.flowcrypt.email.jobscheduler.MessagesMovingJobService
import com.flowcrypt.email.jobscheduler.MessagesSenderJobService
import com.flowcrypt.email.security.SecurityUtils
import com.flowcrypt.email.service.EmailSyncService
Expand Down Expand Up @@ -49,6 +50,7 @@ class LauncherActivity : BaseActivity() {
PreferenceManager.setDefaultValues(this, R.xml.preferences_notifications_settings, false)
ForwardedAttachmentsDownloaderJobService.schedule(applicationContext)
MessagesSenderJobService.schedule(applicationContext)
MessagesMovingJobService.schedule(applicationContext)
FeedbackJobIntentService.enqueueWork(this)

account = AccountDaoSource().getActiveAccountInformation(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import com.flowcrypt.email.database.MessageState
import com.flowcrypt.email.database.dao.source.AccountDaoSource
import com.flowcrypt.email.database.dao.source.ContactsDaoSource
import com.flowcrypt.email.database.dao.source.imap.MessageDaoSource
import com.flowcrypt.email.jobscheduler.MessagesMovingJobService
import com.flowcrypt.email.model.MessageEncryptionType
import com.flowcrypt.email.model.MessageType
import com.flowcrypt.email.service.attachment.AttachmentDownloadManagerService
Expand Down Expand Up @@ -212,6 +213,7 @@ class MessageDetailsFragment : BaseSyncFragment(), View.OnClickListener {
R.id.menuActionArchiveMessage -> {
MessageDaoSource().updateMsgState(context!!, details?.email ?: "", details?.label ?: "",
details?.uid?.toLong() ?: 0, MessageState.PENDING_ARCHIVING)
MessagesMovingJobService.schedule(context?.applicationContext)
activity?.finish()
true
}
Expand Down

0 comments on commit 8487f53

Please sign in to comment.