Skip to content

Commit

Permalink
Removed MessageDaoSource.deleteMsgsByUID() method. Modified the datab…
Browse files Browse the repository at this point in the history
…ase structure: added a foreign key for 'attachment' table. Refactored code.| #793
  • Loading branch information
DenBond7 committed Dec 20, 2019
1 parent cf1c291 commit f1bd83d
Show file tree
Hide file tree
Showing 15 changed files with 117 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 20,
"identityHash": "142525ec1065962bd52b6c45a0e7a640",
"identityHash": "b34e1ed31528ece202469e8317791361",
"entities": [
{
"tableName": "accounts_aliases",
Expand Down Expand Up @@ -332,7 +332,7 @@
},
{
"tableName": "attachment",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `email` TEXT NOT NULL, `folder` TEXT NOT NULL, `uid` INTEGER NOT NULL, `name` TEXT NOT NULL, `encodedSize` INTEGER DEFAULT 0, `type` TEXT NOT NULL, `attachment_id` TEXT, `file_uri` TEXT, `forwarded_folder` TEXT, `forwarded_uid` INTEGER DEFAULT -1, `path` TEXT NOT NULL)",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `email` TEXT NOT NULL, `folder` TEXT NOT NULL, `uid` INTEGER NOT NULL, `name` TEXT NOT NULL, `encodedSize` INTEGER DEFAULT 0, `type` TEXT NOT NULL, `attachment_id` TEXT, `file_uri` TEXT, `forwarded_folder` TEXT, `forwarded_uid` INTEGER DEFAULT -1, `path` TEXT NOT NULL, FOREIGN KEY(`email`, `folder`, `uid`) REFERENCES `messages`(`email`, `folder`, `uid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
Expand Down Expand Up @@ -426,9 +426,35 @@
"path"
],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `email_uid_folder_path_in_attachment` ON `${TABLE_NAME}` (`email`, `uid`, `folder`, `path`)"
},
{
"name": "email_folder_uid_in_attachment",
"unique": false,
"columnNames": [
"email",
"folder",
"uid"
],
"createSql": "CREATE INDEX IF NOT EXISTS `email_folder_uid_in_attachment` ON `${TABLE_NAME}` (`email`, `folder`, `uid`)"
}
],
"foreignKeys": []
"foreignKeys": [
{
"table": "messages",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"email",
"folder",
"uid"
],
"referencedColumns": [
"email",
"folder",
"uid"
]
}
]
},
{
"tableName": "contacts",
Expand Down Expand Up @@ -770,7 +796,7 @@
"defaultValue": "NULL"
},
{
"fieldPath": "isMessageHasAttachments",
"fieldPath": "hasAttachments",
"columnName": "is_message_has_attachments",
"affinity": "INTEGER",
"notNull": false,
Expand Down Expand Up @@ -892,7 +918,7 @@
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '142525ec1065962bd52b6c45a0e7a640')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b34e1ed31528ece202469e8317791361')"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package com.flowcrypt.email.api.email.sync.tasks

import com.flowcrypt.email.api.email.FoldersManager
import com.flowcrypt.email.api.email.sync.SyncListener
import com.flowcrypt.email.database.FlowCryptRoomDatabase
import com.flowcrypt.email.database.MessageState
import com.flowcrypt.email.database.dao.source.AccountDao
import com.flowcrypt.email.database.dao.source.imap.MessageDaoSource
Expand Down Expand Up @@ -48,7 +49,7 @@ class ArchiveMsgsSyncTask(ownerKey: String, requestCode: Int) : BaseSyncTask(own

if (msgs.isNotEmpty()) {
remoteSrcFolder.moveMessages(msgs.toTypedArray(), remoteDestFolder)
msgDaoSource.deleteMsgsByUID(context, account.email, inboxFolder.fullName, uidList)
FlowCryptRoomDatabase.getDatabase(context).msgDao().deleteByUIDs(account.email, inboxFolder.fullName, uidList)
}

remoteSrcFolder.close()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package com.flowcrypt.email.api.email.sync.tasks
import com.flowcrypt.email.api.email.FoldersManager
import com.flowcrypt.email.api.email.JavaEmailConstants
import com.flowcrypt.email.api.email.sync.SyncListener
import com.flowcrypt.email.database.FlowCryptRoomDatabase
import com.flowcrypt.email.database.MessageState
import com.flowcrypt.email.database.dao.source.AccountDao
import com.flowcrypt.email.database.dao.source.imap.MessageDaoSource
Expand Down Expand Up @@ -61,7 +62,7 @@ class DeleteMessagesSyncTask(ownerKey: String,

if (msgs.isNotEmpty()) {
remoteSrcFolder.moveMessages(msgs.toTypedArray(), remoteDestFolder)
msgDaoSource.deleteMsgsByUID(context, account.email, folder, uidList)
FlowCryptRoomDatabase.getDatabase(context).msgDao().deleteByUIDs(account.email, folder, uidList)
}

remoteSrcFolder.close()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package com.flowcrypt.email.api.email.sync.tasks
import com.flowcrypt.email.api.email.FoldersManager
import com.flowcrypt.email.api.email.JavaEmailConstants
import com.flowcrypt.email.api.email.sync.SyncListener
import com.flowcrypt.email.database.FlowCryptRoomDatabase
import com.flowcrypt.email.database.MessageState
import com.flowcrypt.email.database.dao.source.AccountDao
import com.flowcrypt.email.database.dao.source.imap.MessageDaoSource
Expand Down Expand Up @@ -58,7 +59,7 @@ class MovingToInboxSyncTask(ownerKey: String, requestCode: Int) : BaseSyncTask(o

if (msgs.isNotEmpty()) {
remoteSrcFolder.moveMessages(msgs.toTypedArray(), remoteDestFolder)
msgDaoSource.deleteMsgsByUID(context, account.email, folder, uidList)
FlowCryptRoomDatabase.getDatabase(context).msgDao().deleteByUIDs(account.email, inboxFolder.fullName, uidList)
}

remoteSrcFolder.close(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,19 @@ abstract class FlowCryptRoomDatabase : RoomDatabase() {
database.execSQL("INSERT INTO contacts SELECT * FROM contacts_temp;")
database.execSQL("DROP TABLE IF EXISTS contacts_temp;")

//Recreate 'attachment' table to use an ability of foreign keys
//delete non-OUTBOX attachments
database.delete("attachment", "folder NOT IN (?)", arrayOf(JavaEmailConstants.FOLDER_OUTBOX))
val tempTableName = "attachment_temp"

database.execSQL("CREATE TEMP TABLE IF NOT EXISTS $tempTableName AS SELECT * FROM attachment;")
database.execSQL("DROP TABLE IF EXISTS attachment;")
database.execSQL("CREATE TABLE `attachment` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `email` TEXT NOT NULL, `folder` TEXT NOT NULL, `uid` INTEGER NOT NULL, `name` TEXT NOT NULL, `encodedSize` INTEGER DEFAULT 0, `type` TEXT NOT NULL, `attachment_id` TEXT, `file_uri` TEXT, `forwarded_folder` TEXT, `forwarded_uid` INTEGER DEFAULT -1, `path` TEXT NOT NULL, FOREIGN KEY(`email`, `folder`, `uid`) REFERENCES `messages`(`email`, `folder`, `uid`) ON UPDATE NO ACTION ON DELETE CASCADE );")
database.execSQL("CREATE UNIQUE INDEX `email_uid_folder_path_in_attachment` ON `attachment` (`email`, `uid`, `folder`, `path`);")
database.execSQL("CREATE INDEX `email_folder_uid_in_attachment` ON `attachment` (`email`, `folder`, `uid`);")
database.execSQL("INSERT INTO attachment SELECT * FROM $tempTableName;")
database.execSQL("DROP TABLE IF EXISTS $tempTableName;")

database.setTransactionSuccessful()
} finally {
database.endTransaction()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import androidx.lifecycle.LiveData
import androidx.paging.DataSource
import androidx.room.Dao
import androidx.room.Query
import androidx.room.Transaction
import com.flowcrypt.email.database.entity.MessageEntity
import java.util.*

/**
* This class describes available methods for [MessageEntity]
Expand All @@ -20,16 +22,41 @@ import com.flowcrypt.email.database.entity.MessageEntity
* E-mail: [email protected]
*/
@Dao
interface MessagesDao : BaseDao<MessageEntity> {
abstract class MessagesDao : BaseDao<MessageEntity> {
@Query("SELECT * FROM messages WHERE email = :account AND folder = :folder")
fun getMessages(account: String, folder: String): LiveData<MessageEntity>
abstract fun getMessages(account: String, folder: String): LiveData<MessageEntity>

@Query("SELECT * FROM messages WHERE email = :account AND folder = :folder ORDER BY received_date DESC")
fun getMessagesDataSourceFactory(account: String, folder: String): DataSource.Factory<Int, MessageEntity>
abstract fun getMessagesDataSourceFactory(account: String, folder: String): DataSource
.Factory<Int, MessageEntity>

@Query("SELECT * FROM messages")
fun msgs(): DataSource.Factory<Int, MessageEntity>
abstract fun msgs(): DataSource.Factory<Int, MessageEntity>

@Query("DELETE FROM messages WHERE email = :email AND folder = :label")
suspend fun delete(email: String?, label: String?): Int
abstract suspend fun delete(email: String?, label: String?): Int

@Query("DELETE FROM messages WHERE email = :email AND folder = :label AND uid IN (:msgsUID)")
abstract fun delete(email: String?, label: String?, msgsUID: Collection<Long>): Int

@Transaction
open fun deleteByUIDs(email: String?, label: String?, msgsUID: Collection<Long>) {
val step = 50
val list = ArrayList(msgsUID)

if (msgsUID.size <= step) {
delete(email, label, msgsUID)
} else {
var i = 0
while (i < list.size) {
val stepUIDs = if (list.size - i > step) {
list.subList(i, i + step)
} else {
list.subList(i, list.size)
}
delete(email, label, stepUIDs)
i += step
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,66 +49,6 @@ class MessageDaoSource : BaseDaoSource() {

override val tableName: String = TABLE_NAME_MESSAGES

/**
* This method delete cached messages.
*
* @param context Interface to global information about an application environment.
* @param email The email that the message linked.
* @param label The folder label.
* @param msgsUID The list of messages UID.
*/
fun deleteMsgsByUID(context: Context, email: String?, label: String?, msgsUID: Collection<Long>) {
val contentResolver = context.contentResolver
if (email != null && label != null && contentResolver != null) {
val step = 50

val selectionArgs = LinkedList<String>()
selectionArgs.add(email)
selectionArgs.add(label)

val list = ArrayList(msgsUID)

if (msgsUID.size <= step) {
for (uid in msgsUID) {
selectionArgs.add(uid.toString())
}

val where = COL_EMAIL + "= ? AND " + COL_FOLDER + " = ? AND " + COL_UID + " IN (" +
prepareSelectionArgsString(msgsUID.toTypedArray()) + ");"

contentResolver.delete(baseContentUri, where, selectionArgs.toTypedArray())
} else {
val ops = ArrayList<ContentProviderOperation>()

var i = 0
while (i < list.size) {
val stepUIDs = if (list.size - i > step) {
list.subList(i, i + step)
} else {
list.subList(i, list.size)
}

val selectionArgsForStep = LinkedList(selectionArgs)

for (uid in stepUIDs) {
selectionArgsForStep.add(uid.toString())
}

val selection = (COL_EMAIL + "= ? AND " + COL_FOLDER + " = ? AND " + COL_UID
+ " IN (" + prepareSelectionArgsString(stepUIDs.toTypedArray()) + ");")

ops.add(ContentProviderOperation.newDelete(baseContentUri)
.withSelection(selection, selectionArgsForStep.toTypedArray())
.withYieldAllowed(true)
.build())
i += step
}

contentResolver.applyBatch(baseContentUri.authority!!, ops)
}
}
}

/**
* This method update cached messages.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package com.flowcrypt.email.database.entity
import android.provider.BaseColumns
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey

Expand All @@ -19,7 +20,12 @@ import androidx.room.PrimaryKey
*/
@Entity(tableName = "attachment",
indices = [
Index(name = "email_uid_folder_path_in_attachment", value = ["email", "uid", "folder", "path"], unique = true)
Index(name = "email_uid_folder_path_in_attachment", value = ["email", "uid", "folder", "path"], unique = true),
Index(name = "email_folder_uid_in_attachment", value = ["email", "folder", "uid"])
],
foreignKeys = [
ForeignKey(entity = MessageEntity::class, parentColumns = ["email", "folder", "uid"],
childColumns = ["email", "folder", "uid"], onDelete = ForeignKey.CASCADE)
]
)
data class AttachmentEntity(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ class MessagesViewModel(application: Application) : BaseAndroidViewModel(applica

fun cleanFolderCache(folderName: String?) {
viewModelScope.launch {
val t = roomDatabase.attachmentDao().delete(accountLiveData.value?.email, folderName)
roomDatabase.msgDao().delete(accountLiveData.value?.email, folderName)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class SyncJobService : JobService(), SyncListener {

val generalMsgDetailsBeforeUpdate = msgDaoSource.getNewMsgs(applicationContext, account.email, folderName)

msgDaoSource.deleteMsgsByUID(applicationContext, account.email, folderName, deleteCandidatesUIDs)
FlowCryptRoomDatabase.getDatabase(applicationContext).msgDao().deleteByUIDs(account.email, folderName, deleteCandidatesUIDs)

msgDaoSource.updateMsgsByUID(applicationContext, account.email, folderName, remoteFolder,
EmailUtil.genUpdateCandidates(mapOfUIDsAndMsgsFlags, remoteFolder, updateMsgs))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ class EmailSyncService : BaseService(), SyncListener {
val msgsUIDs = HashSet(mapOfUIDAndMsgFlags.keys)
val deleteCandidatesUIDs = EmailUtil.genDeleteCandidates(msgsUIDs, remoteFolder, updateMsgs)

msgsDaoSource.deleteMsgsByUID(this, email, folderName, deleteCandidatesUIDs)
FlowCryptRoomDatabase.getDatabase(context).msgDao().deleteByUIDs(account.email, folderName, deleteCandidatesUIDs)

val folderType = FoldersManager.getFolderType(localFolder)
if (!GeneralUtil.isAppForegrounded() && folderType === FoldersManager.FolderType.INBOX) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,8 @@ class EmailManagerActivity : BaseEmailListActivity(), NavigationView.OnNavigatio
}
}

R.id.syns_request_code_force_load_new_messages -> {
onFetchMsgsCompleted()
R.id.syns_request_code_refresh_msgs -> {
onRefreshMsgsCompleted()
msgsIdlingResource.setIdleState(true)
}

Expand All @@ -304,10 +304,10 @@ class EmailManagerActivity : BaseEmailListActivity(), NavigationView.OnNavigatio

override fun onErrorHappened(requestCode: Int, errorType: Int, e: Exception) {
when (requestCode) {
R.id.syns_request_code_force_load_new_messages -> {
msgsIdlingResource.setIdleState(true)
R.id.syns_request_code_refresh_msgs -> {
onErrorOccurred(requestCode, errorType, e)
onFetchMsgsCompleted()
onRefreshMsgsCompleted()
msgsIdlingResource.setIdleState(true)
}

R.id.syns_request_code_update_label_passive, R.id.syns_request_code_update_label_active -> {
Expand Down Expand Up @@ -696,6 +696,10 @@ class EmailManagerActivity : BaseEmailListActivity(), NavigationView.OnNavigatio
(supportFragmentManager.findFragmentById(R.id.emailListFragment) as? EmailListFragment?)?.onFetchMsgsCompleted()
}

private fun onRefreshMsgsCompleted() {
(supportFragmentManager.findFragmentById(R.id.emailListFragment) as? EmailListFragment?)?.onRefreshMsgsCompleted()
}

/**
* The custom realization of [ActionBarDrawerToggle]. Will be used to start a labels
* update task when the drawer will be opened.
Expand Down
Loading

0 comments on commit f1bd83d

Please sign in to comment.