:core:database)| Entity | Table | Key | Purpose |
|---|---|---|---|
ThreadEntity | threads | threadId (PK) | Thread-level metadata |
EmailEntity | emails | id (PK) | Individual email messages |
ScheduledMessageEntity | scheduled_messages | id (PK) | Scheduled sends |
PendingActionEntity | pending_actions | id (PK) | Optimistic write queue |
Current schema version: 12 (10 migrations from v2 → v12).
MIGRATION_2_3 through MIGRATION_7_8: incremental column additionsMIGRATION_8_9: new table (pending_actions)MIGRATION_9_10: new column (bodyIsHtml)MIGRATION_10_11: new column (fromAlias)MIGRATION_11_12: composite indices for performance:core:data)This is the central nervous system:
class EmailRepository @Inject constructor(
private val database: AppDatabase,
private val providerFactory: (UserProfile) -> EmailProvider,
private val accountManager: AccountManager,
private val pendingActionDao: PendingActionDao
) Use Room Flow queries with composite indices on (accountId, folder, date):
fun getInboxThreadsFlow(accountId: String): Flow<List<EmailThread>> =
threadDao.getThreadsByFolder(accountId, inInbox = true) Follow this pattern:
suspend fun archiveThread(accountId: String, threadId: String) {
// 1. Optimistic local update
threadDao.markAsArchived(threadId, accountId)
emailDao.markAsArchivedByThread(threadId, accountId)
// 2. Schedule server sync
insertPendingAction(ARCHIVE, accountId, threadId)
} insertPendingAction creates a PendingActionEntity row. The ActionQueueManager processes it against the server.
:app)@Singleton
class ActionQueueManager @Inject constructor(
private val pendingActionDao: PendingActionDao,
private val accountManager: AccountManager,
private val providerFactory: (UserProfile) -> EmailProvider
) {
fun start() // called from MonoMailApp.onCreate()
fun stop() // on app shutdown
} PendingActionDao.getNextPending() every 2 seconds.incrementRetry().FAILED on AuthFailedException or max retries.getPendingFlow() and getFailedCountFlow() for UI.All settings in one AppSettings data class, persisted as DataStore Preferences:
data class AppSettings(
val themeMode: ThemeMode = ThemeMode.SYSTEM,
val fontScale: FontScale = FontScale.DEFAULT,
val showDividers: Boolean = false,
val compactList: Boolean = false,
val showSnippet: Boolean = true,
val swipeLeftAction: SwipeAction = SwipeAction.READ_UNREAD,
val swipeRightAction: SwipeAction = SwipeAction.ARCHIVE,
val confirmBeforeSending: Boolean = false,
val defaultReply: DefaultReply = DefaultReply.REPLY,
val emailNotifications: Boolean = true,
val syncFrequency: SyncFrequency = SyncFrequency.MIN_15,
val unifiedInbox: Boolean = false,
val smartGrouping: Boolean = true,
val groupRecentOnly: Boolean = true,
val organizeByThread: Boolean = true,
val loadRemoteImages: Boolean = true,
val renderMarkdown: Boolean = false,
val navScale: Float = 1.0f,
val undoSendEnabled: Boolean = true,
val undoSendWindow: UndoSendWindow = UndoSendWindow.SEC_10,
val dockConfig: DockConfig = DockConfig(),
val hasSeenWelcomePrompt: Boolean = false,
val hasDismissedUpdatePrompt: Boolean = false,
val lastKnownVersion: String = ""
) Also stores: emailTemplates (list as JSON), accountNotificationSettings (per account).