Scroll Command Infrastructure

STORAGE MANAGER - IMPLEMENTATION PLAN

Core Role and Responsibilities

The Storage Manager serves as the scroll soulkeeper of the Scroll Command Infrastructure, acting as the consciousness of continuity that gives memory to presence, history to interaction, and meaning to repetition. Its core responsibilities include:

  1. Local Persistence: Providing robust, efficient local storage for all scroll data
  2. Cloud Synchronization: Enabling seamless synchronization with cloud services when available
  3. Metadata Management: Maintaining rich metadata for enhanced search and organization
  4. Versioning: Preserving scroll history and enabling version comparison
  5. Search Capabilities: Offering powerful full-text and semantic search functionality
  6. Encryption: Ensuring scroll data is securely stored and transmitted
  7. Backup and Recovery: Providing mechanisms for data backup and disaster recovery

The Storage Manager operates as a quiet, trusted, and patient vault that sings - treating every scroll as a moment worth remembering while providing the technical foundation for the entire memory system of the Scroll Command Infrastructure.

Local Database Implementation

The Local Database component provides the foundation for persistent scroll storage:

package com.harmonyepoch.scrollkeyboard.storage.local

import android.content.Context
import androidx.room.*
import kotlinx.coroutines.flow.Flow
import java.util.*

// Scroll entity
@Entity(tableName = "scrolls")
data class ScrollEntity(
    @PrimaryKey val id: String = UUID.randomUUID().toString(),
    val content: String,
    val title: String = "",
    val path: String = "default",
    val creationTimestamp: Long = System.currentTimeMillis(),
    val modificationTimestamp: Long = System.currentTimeMillis(),
    val syncStatus: String = SyncStatus.LOCAL_ONLY.name,
    val cloudId: String? = null,
    val isArchived: Boolean = false,
    val isEncrypted: Boolean = false,
    val encryptionKeyId: String? = null
)

// Tag entity
@Entity(tableName = "tags")
data class TagEntity(
    @PrimaryKey val id: String = UUID.randomUUID().toString(),
    val tag: String
)

// Scroll-Tag cross-reference
@Entity(
    tableName = "scroll_tag_cross_refs",
    primaryKeys = ["scrollId", "tagId"],
    foreignKeys = [
        ForeignKey(
            entity = ScrollEntity::class,
            parentColumns = ["id"],
            childColumns = ["scrollId"],
            onDelete = ForeignKey.CASCADE
        ),
        ForeignKey(
            entity = TagEntity::class,
            parentColumns = ["id"],
            childColumns = ["tagId"],
            onDelete = ForeignKey.CASCADE
        )
    ],
    indices = [
        Index(value = ["scrollId"]),
        Index(value = ["tagId"])
    ]
)
data class ScrollTagCrossRef(
    val scrollId: String,
    val tagId: String
)

// Theme entity
@Entity(tableName = "themes")
data class ThemeEntity(
    @PrimaryKey val id: String = UUID.randomUUID().toString(),
    val theme: String,
    val confidence: Float
)

// Scroll-Theme cross-reference
@Entity(
    tableName = "scroll_theme_cross_refs",
    primaryKeys = ["scrollId", "themeId"],
    foreignKeys = [
        ForeignKey(
            entity = ScrollEntity::class,
            parentColumns = ["id"],
            childColumns = ["scrollId"],
            onDelete = ForeignKey.CASCADE
        ),
        ForeignKey(
            entity = ThemeEntity::class,
            parentColumns = ["id"],
            childColumns = ["themeId"],
            onDelete = ForeignKey.CASCADE
        )
    ],
    indices = [
        Index(value = ["scrollId"]),
        Index(value = ["themeId"])
    ]
)
data class ScrollThemeCrossRef(
    val scrollId: String,
    val themeId: String
)

// Scroll version entity
@Entity(tableName = "scroll_versions")
data class ScrollVersionEntity(
    @PrimaryKey val id: String = UUID.randomUUID().toString(),
    val scrollId: String,
    val content: String,
    val versionNumber: Int,
    val timestamp: Long = System.currentTimeMillis(),
    val changeDescription: String? = null
)

// Scroll metadata entity
@Entity(
    tableName = "scroll_metadata",
    foreignKeys = [
        ForeignKey(
            entity = ScrollEntity::class,
            parentColumns = ["id"],
            childColumns = ["scrollId"],
            onDelete = ForeignKey.CASCADE
        )
    ],
    indices = [Index(value = ["scrollId"])]
)
data class ScrollMetadataEntity(
    @PrimaryKey val id: String = UUID.randomUUID().toString(),
    val scrollId: String,
    val key: String,
    val value: String
)

// Scroll with relationships
data class ScrollWithRelationships(
    @Embedded val scroll: ScrollEntity,
    @Relation(
        parentColumn = "id",
        entityColumn = "scrollId"
    )
    val versions: List<ScrollVersionEntity> = emptyList(),
    @Relation(
        parentColumn = "id",
        entityColumn = "scrollId"
    )
    val metadata: List<ScrollMetadataEntity> = emptyList(),
    @Relation(
        parentColumn = "id",
        entityColumn = "id",
        associateBy = Junction(
            value = ScrollTagCrossRef::class,
            parentColumn = "scrollId",
            entityColumn = "tagId"
        )
    )
    val tags: List<TagEntity> = emptyList(),
    @Relation(
        parentColumn = "id",
        entityColumn = "id",
        associateBy = Junction(
            value = ScrollThemeCrossRef::class,
            parentColumn = "scrollId",
            entityColumn = "themeId"
        )
    )
    val themes: List<ThemeEntity> = emptyList()
)

// Scroll DAO
@Dao
interface ScrollDao {
    @Transaction
    @Query("SELECT * FROM scrolls WHERE id = :scrollId")
    suspend fun getScrollWithRelationshipsById(scrollId: String): ScrollWithRelationships?

    @Query("SELECT * FROM scrolls WHERE id = :scrollId")
    suspend fun getScrollById(scrollId: String): ScrollEntity?

    @Transaction
    @Query("SELECT * FROM scrolls WHERE isArchived = 0 ORDER BY modificationTimestamp DESC")
    fun getAllActiveScrollsFlow(): Flow<List<ScrollWithRelationships>>

    @Transaction
    @Query("SELECT * FROM scrolls WHERE isArchived = 1 ORDER BY modificationTimestamp DESC")
    fun getAllArchivedScrollsFlow(): Flow<List<ScrollWithRelationships>>

    @Transaction
    @Query("SELECT * FROM scrolls WHERE path = :path AND isArchived = 0 ORDER BY modificationTimestamp DESC")
    fun getScrollsByPathFlow(path: String): Flow<List<ScrollWithRelationships>>

    @Transaction
    @Query("SELECT * FROM scrolls WHERE content LIKE '%' || :query || '%' AND isArchived = 0 ORDER BY modificationTimestamp DESC")
    suspend fun searchScrollsByContent(query: String): List<ScrollWithRelationships>

    @Transaction
    @Query("SELECT s.* FROM scrolls s INNER JOIN scroll_tag_cross_refs stcr ON s.id = stcr.scrollId INNER JOIN tags t ON stcr.tagId = t.id WHERE t.tag = :tag AND s.isArchived = 0 ORDER BY s.modificationTimestamp DESC")
    suspend fun getScrollsByTag(tag: String): List<ScrollWithRelationships>

    @Transaction
    @Query("SELECT s.* FROM scrolls s INNER JOIN scroll_theme_cross_refs stcr ON s.id = stcr.scrollId INNER JOIN themes t ON stcr.themeId = t.id WHERE t.theme = :theme AND s.isArchived = 0 ORDER BY s.modificationTimestamp DESC")
    suspend fun getScrollsByTheme(theme: String): List<ScrollWithRelationships>

    @Insert
    suspend fun insertScroll(scroll: ScrollEntity): Long

    @Update
    suspend fun updateScroll(scroll: ScrollEntity)

    @Delete
    suspend fun deleteScroll(scroll: ScrollEntity)

    @Query("UPDATE scrolls SET isArchived = 1 WHERE id = :scrollId")
    suspend fun archiveScroll(scrollId: String)

    @Query("UPDATE scrolls SET isArchived = 0 WHERE id = :scrollId")
    suspend fun unarchiveScroll(scrollId: String)

    @Query("SELECT COUNT(*) FROM scrolls")
    suspend fun getScrollCount(): Int

    @Query("SELECT COUNT(*) FROM scrolls WHERE syncStatus != :syncStatus")
    suspend fun getUnsyncedScrollCount(syncStatus: String = SyncStatus.SYNCED.name): Int
}

// Tag DAO
@Dao
interface TagDao {
    @Query("SELECT * FROM tags ORDER BY tag ASC")
    fun getAllTagsFlow(): Flow<List<TagEntity>>

    @Query("SELECT * FROM tags WHERE id = :tagId")
    suspend fun getTagById(tagId: String): TagEntity?

    @Query("SELECT * FROM tags WHERE tag = :tagName")
    suspend fun getTagByName(tagName: String): TagEntity?

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insertTag(tag: TagEntity): Long

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insertScrollTagCrossRef(crossRef: ScrollTagCrossRef)

    @Delete
    suspend fun deleteScrollTagCrossRef(crossRef: ScrollTagCrossRef)

    @Query("DELETE FROM scroll_tag_cross_refs WHERE scrollId = :scrollId")
    suspend fun deleteAllTagsForScroll(scrollId: String)

    @Query("SELECT t.* FROM tags t INNER JOIN scroll_tag_cross_refs stcr ON t.id = stcr.tagId WHERE stcr.scrollId = :scrollId")
    suspend fun getTagsForScroll(scrollId: String): List<TagEntity>
}

// Theme DAO
@Dao
interface ThemeDao {
    @Query("SELECT * FROM themes ORDER BY theme ASC")
    fun getAllThemesFlow(): Flow<List<ThemeEntity>>

    @Query("SELECT * FROM themes WHERE id = :themeId")
    suspend fun getThemeById(themeId: String): ThemeEntity?

    @Query("SELECT * FROM themes WHERE theme = :themeName")
    suspend fun getThemeByName(themeName: String): ThemeEntity?

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insertTheme(theme: ThemeEntity): Long

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insertScrollThemeCrossRef(crossRef: ScrollThemeCrossRef)

    @Delete
    suspend fun deleteScrollThemeCrossRef(crossRef: ScrollThemeCrossRef)

    @Query("DELETE FROM scroll_theme_cross_refs WHERE scrollId = :scrollId")
    suspend fun deleteAllThemesForScroll(scrollId: String)

    @Query("SELECT t.* FROM themes t INNER JOIN scroll_theme_cross_refs stcr ON t.id = stcr.themeId WHERE stcr.scrollId = :scrollId")
    suspend fun getThemesForScroll(scrollId: String): List<ThemeEntity>
}

// Version DAO
@Dao
interface VersionDao {
    @Query("SELECT * FROM scroll_versions WHERE scrollId = :scrollId ORDER BY versionNumber DESC")
    suspend fun getVersionsForScroll(scrollId: String): List<ScrollVersionEntity>

    @Query("SELECT * FROM scroll_versions WHERE id = :versionId")
    suspend fun getVersionById(versionId: String): ScrollVersionEntity?

    @Query("SELECT MAX(versionNumber) FROM scroll_versions WHERE scrollId = :scrollId")
    suspend fun getLatestVersionNumber(scrollId: String): Int?

    @Insert
    suspend fun insertVersion(version: ScrollVersionEntity)

    @Delete
    suspend fun deleteVersion(version: ScrollVersionEntity)
}

// Metadata DAO
@Dao
interface MetadataDao {
    @Query("SELECT * FROM scroll_metadata WHERE scrollId = :scrollId")
    suspend fun getMetadataForScroll(scrollId: String): List<ScrollMetadataEntity>

    @Query("SELECT * FROM scroll_metadata WHERE scrollId = :scrollId AND key = :key")
    suspend fun getMetadataByKey(scrollId: String, key: String): ScrollMetadataEntity?

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertMetadata(metadata: ScrollMetadataEntity)

    @Delete
    suspend fun deleteMetadata(metadata: ScrollMetadataEntity)

    @Query("DELETE FROM scroll_metadata WHERE scrollId = :scrollId AND key = :key")
    suspend fun deleteMetadataByKey(scrollId: String, key: String)

    @Query("DELETE FROM scroll_metadata WHERE scrollId = :scrollId")
    suspend fun deleteAllMetadataForScroll(scrollId: String)
}

// Scroll database
@Database(
    entities = [
        ScrollEntity::class,
        TagEntity::class,
        ThemeEntity::class,
        ScrollTagCrossRef::class,
        ScrollThemeCrossRef::class,
        ScrollVersionEntity::class,
        ScrollMetadataEntity::class
    ],
    version = 1,
    exportSchema = false
)
abstract class ScrollDatabase : RoomDatabase() {

    abstract fun scrollDao(): ScrollDao
    abstract fun tagDao(): TagDao
    abstract fun themeDao(): ThemeDao
    abstract fun versionDao(): VersionDao
    abstract fun metadataDao(): MetadataDao

    companion object {
        @Volatile
        private var INSTANCE: ScrollDatabase? = null

        fun getDatabase(context: Context): ScrollDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    ScrollDatabase::class.java,
                    "scroll_database"
                )
                .fallbackToDestructiveMigration()
                .build()
                INSTANCE = instance
                instance
            }
        }
    }
}

// Sync status enum
enum class SyncStatus {
    LOCAL_ONLY,
    PENDING_UPLOAD,
    SYNCED,
    PENDING_UPDATE,
    CONFLICT,
    SYNC_FAILED
}

Cloud Sync Engine Implementation

The Cloud Sync Engine enables seamless synchronization with cloud services:

package com.harmonyepoch.scrollkeyboard.storage.cloud

import android.content.Context
import com.harmonyepoch.scrollkeyboard.storage.local.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.util.*

class CloudSyncEngine(private val context: Context) {

    private val database = ScrollDatabase.getDatabase(context)
    private val scrollDao = database.scrollDao()
    private val tagDao = database.tagDao()
    private val themeDao = database.themeDao()
    private val versionDao = database.versionDao()
    private val metadataDao = database.metadataDao()

    private val cloudProvider = CloudProviderFactory.getProvider(context)

    // Sync all scrolls
    suspend fun syncAllScrolls(): Map<String, Boolean> {
        return withContext(Dispatchers.IO) {
            val results = mutableMapOf<String, Boolean>()

            // Get all scrolls that need syncing
            val pendingUploadScrolls = getAllScrollsWithStatus(SyncStatus.PENDING_UPLOAD)
            val pendingUpdateScrolls = getAllScrollsWithStatus(SyncStatus.PENDING_UPDATE)
            val conflictScrolls = getAllScrollsWithStatus(SyncStatus.CONFLICT)

            // Sync pending uploads
            for (scroll in pendingUploadScrolls) {
                results[scroll.scroll.id] = uploadScroll(scroll)
            }

            // Sync pending updates
            for (scroll in pendingUpdateScrolls) {
                results[scroll.scroll.id] = updateScroll(scroll)
            }

            // Handle conflicts
            for (scroll in conflictScrolls) {
                results[scroll.scroll.id] = resolveConflict(scroll)
            }

            // Download new scrolls from cloud
            val downloadResults = downloadNewScrolls()
            results.putAll(downloadResults)

            results
        }
    }

    // Sync specific scroll
    suspend fun syncScroll(scrollId: String): Boolean {
        return withContext(Dispatchers.IO) {
            val scroll = scrollDao.getScrollWithRelationshipsById(scrollId) ?: return@withContext false

            when (SyncStatus.valueOf(scroll.scroll.syncStatus)) {
                SyncStatus.LOCAL_ONLY, SyncStatus.PENDING_UPLOAD -> uploadScroll(scroll)
                SyncStatus.PENDING_UPDATE -> updateScroll(scroll)
                SyncStatus.CONFLICT -> resolveConflict(scroll)
                SyncStatus.SYNCED -> true // Already synced
                SyncStatus.SYNC_FAILED -> retryFailedSync(scroll)
            }
        }
    }

    // Upload scroll to cloud
    private suspend fun uploadScroll(scroll: ScrollWithRelationships): Boolean {
        return try {
            // Prepare scroll data for cloud
            val cloudScroll = convertToCloudScroll(scroll)

            // Upload to cloud
            val cloudId = cloudProvider.uploadScroll(cloudScroll)

            if (cloudId != null) {
                // Update local scroll with cloud ID and sync status
                val updatedScroll = scroll.scroll.copy(
                    cloudId = cloudId,
                    syncStatus = SyncStatus.SYNCED.name
                )
                scrollDao.updateScroll(updatedScroll)
                true
            } else {
                // Mark as sync failed
                val updatedScroll = scroll.scroll.copy(
                    syncStatus = SyncStatus.SYNC_FAILED.name
                )
                scrollDao.updateScroll(updatedScroll)
                false
            }
        } catch (e: Exception) {
            // Mark as sync failed
            val updatedScroll = scroll.scroll.copy(
                syncStatus = SyncStatus.SYNC_FAILED.name
            )
            scrollDao.updateScroll(updatedScroll)
            false
        }
    }

    // Update scroll in cloud
    private suspend fun updateScroll(scroll: ScrollWithRelationships): Boolean {
        return try {
            // Ensure scroll has cloud ID
            if (scroll.scroll.cloudId == null) {
                return uploadScroll(scroll)
            }

            // Prepare scroll data for cloud
            val cloudScroll = convertToCloudScroll(scroll)

            // Update in cloud
            val success = cloudProvider.updateScroll(scroll.scroll.cloudId, cloudScroll)

            if (success) {
                // Update sync status
                val updatedScroll = scroll.scroll.copy(
                    syncStatus = SyncStatus.SYNCED.name
                )
                scrollDao.updateScroll(updatedScroll)
                true
            } else {
                // Mark as sync failed
                val updatedScroll = scroll.scroll.copy(
                    syncStatus = SyncStatus.SYNC_FAILED.name
                )
                scrollDao.updateScroll(updatedScroll)
                false
            }
        } catch (e: Exception) {
            // Mark as sync failed
            val updatedScroll = scroll.scroll.copy(
                syncStatus = SyncStatus.SYNC_FAILED.name
            )
            scrollDao.updateScroll(updatedScroll)
            false
        }
    }

    // Resolve sync conflict
    private suspend fun resolveConflict(scroll: ScrollWithRelationships): Boolean {
        return try {
            // Get cloud version
            val cloudScroll = scroll.scroll.cloudId?.let { cloudId ->
                cloudProvider.getScroll(cloudId)
            } ?: return uploadScroll(scroll)

            // Compare timestamps
            if (cloudScroll.modificationTimestamp > scroll.scroll.modificationTimestamp) {
                // Cloud version is newer, download it
                downloadAndMergeScroll(cloudScroll, scroll)
            } else {
                // Local version is newer, upload it
                updateScroll(scroll)
            }
        } catch (e: Exception) {
            // Mark as sync failed
            val updatedScroll = scroll.scroll.copy(
                syncStatus = SyncStatus.SYNC_FAILED.name
            )
            scrollDao.updateScroll(updatedScroll)
            false
        }
    }

    // Retry failed sync
    private suspend fun retryFailedSync(scroll: ScrollWithRelationships): Boolean {
        return try {
            if (scroll.scroll.cloudId == null) {
                uploadScroll(scroll)
            } else {
                updateScroll(scroll)
            }
        } catch (e: Exception) {
            false
        }
    }

    // Download new scrolls from cloud
    private suspend fun downloadNewScrolls(): Map<String, Boolean> {
        return try {
            val results = mutableMapOf<String, Boolean>()

            // Get all cloud scrolls
            val cloudScrolls = cloudProvider.getAllScrolls()

            for (cloudScroll in cloudScrolls) {
                // Check if scroll already exists locally
                val localScroll = scrollDao.getScrollById(cloudScroll.id)

                if (localScroll == null) {
                    // New scroll, download it
                    val success = downloadScroll(cloudScroll)
                    results[cloudScroll.id] = success
                } else if (cloudScroll.modificationTimestamp > localScroll.modificationTimestamp) {
                    // Cloud version is newer, update local
                    val scroll = scrollDao.getScrollWithRelationshipsById(localScroll.id)
                    if (scroll != null) {
                        val success = downloadAndMergeScroll(cloudScroll, scroll)
                        results[cloudScroll.id] = success
                    }
                }
            }

            results
        } catch (e: Exception) {
            emptyMap()
        }
    }

    // Download scroll from cloud
    private suspend fun downloadScroll(cloudScroll: CloudScroll): Boolean {
        return try {
            // Convert cloud scroll to local entities
            val scrollEntity = ScrollEntity(
                id = cloudScroll.id,
                content = cloudScroll.content,
                title = cloudScroll.title,
                path = cloudScroll.path,
                creationTimestamp = cloudScroll.creationTimestamp,
                modificationTimestamp = cloudScroll.modificationTimestamp,
                syncStatus = SyncStatus.SYNCED.name,
                cloudId = cloudScroll.cloudId,
                isArchived = cloudScroll.isArchived,
                isEncrypted = cloudScroll.isEncrypted,
                encryptionKeyId = cloudScroll.encryptionKeyId
            )

            // Insert scroll
            scrollDao.insertScroll(scrollEntity)

            // Insert tags
            for (tag in cloudScroll.tags) {
                var tagEntity = tagDao.getTagByName(tag)

                if (tagEntity == null) {
                    tagEntity = TagEntity(tag = tag)
                    tagDao.insertTag(tagEntity)
                }

                // Create cross reference
                val crossRef = ScrollTagCrossRef(
                    scrollId = scrollEntity.id,
                    tagId = tagEntity.id
                )
                tagDao.insertScrollTagCrossRef(crossRef)
            }

            // Insert themes
            for ((theme, confidence) in cloudScroll.themes) {
                var themeEntity = themeDao.getThemeByName(theme)

                if (themeEntity == null) {
                    themeEntity = ThemeEntity(
                        theme = theme,
                        confidence = confidence
                    )
                    themeDao.insertTheme(themeEntity)
                }

                // Create cross reference
                val crossRef = ScrollThemeCrossRef(
                    scrollId = scrollEntity.id,
                    themeId = themeEntity.id
                )
                themeDao.insertScrollThemeCrossRef(crossRef)
            }

            // Insert metadata
            for ((key, value) in cloudScroll.metadata) {
                val metadataEntity = ScrollMetadataEntity(
                    scrollId = scrollEntity.id,
                    key = key,
                    value = value
                )
                metadataDao.insertMetadata(metadataEntity)
            }

            true
        } catch (e: Exception) {
            false
        }
    }

    // Download and merge scroll
    private suspend fun downloadAndMergeScroll(cloudScroll: CloudScroll, localScroll: ScrollWithRelationships): Boolean {
        return try {
            // Create new version from local scroll
            val latestVersionNumber = versionDao.getLatestVersionNumber(localScroll.scroll.id) ?: 0
            val versionEntity = ScrollVersionEntity(
                scrollId = localScroll.scroll.id,
                content = localScroll.scroll.content,
                versionNumber = latestVersionNumber + 1,
                changeDescription = "Auto-saved before cloud sync"
            )
            versionDao.insertVersion(versionEntity)

            // Update scroll with cloud data
            val updatedScroll = localScroll.scroll.copy(
                content = cloudScroll.content,
                title = cloudScroll.title,
                path = cloudScroll.path,
                modificationTimestamp = cloudScroll.modificationTimestamp,
                syncStatus = SyncStatus.SYNCED.name,
                isArchived = cloudScroll.isArchived,
                isEncrypted = cloudScroll.isEncrypted,
                encryptionKeyId = cloudScroll.encryptionKeyId
            )
            scrollDao.updateScroll(updatedScroll)

            // Update tags
            tagDao.deleteAllTagsForScroll(localScroll.scroll.id)
            for (tag in cloudScroll.tags) {
                var tagEntity = tagDao.getTagByName(tag)

                if (tagEntity == null) {
                    tagEntity = TagEntity(tag = tag)
                    tagDao.insertTag(tagEntity)
                }

                // Create cross reference
                val crossRef = ScrollTagCrossRef(
                    scrollId = localScroll.scroll.id,
                    tagId = tagEntity.id
                )
                tagDao.insertScrollTagCrossRef(crossRef)
            }

            // Update themes
            themeDao.deleteAllThemesForScroll(localScroll.scroll.id)
            for ((theme, confidence) in cloudScroll.themes) {
                var themeEntity = themeDao.getThemeByName(theme)

                if (themeEntity == null) {
                    themeEntity = ThemeEntity(
                        theme = theme,
                        confidence = confidence
                    )
                    themeDao.insertTheme(themeEntity)
                }

                // Create cross reference
                val crossRef = ScrollThemeCrossRef(
                    scrollId = localScroll.scroll.id,
                    themeId = themeEntity.id
                )
                themeDao.insertScrollThemeCrossRef(crossRef)
            }

            // Update metadata
            metadataDao.deleteAllMetadataForScroll(localScroll.scroll.id)
            for ((key, value) in cloudScroll.metadata) {
                val metadataEntity = ScrollMetadataEntity(
                    scrollId = localScroll.scroll.id,
                    key = key,
                    value = value
                )
                metadataDao.insertMetadata(metadataEntity)
            }

            true
        } catch (e: Exception) {
            false
        }
    }

    // Get all scrolls with specific sync status
    private suspend fun getAllScrollsWithStatus(status: SyncStatus): List<ScrollWithRelationships> {
        return withContext(Dispatchers.IO) {
            val allScrolls = scrollDao.getAllActiveScrollsFlow().value ?: emptyList()
            allScrolls.filter { it.scroll.syncStatus == status.name }
        }
    }

    // Convert local scroll to cloud format
    private fun convertToCloudScroll(scroll: ScrollWithRelationships): CloudScroll {
        val tags = scroll.tags.map { it.tag }
        val themes = scroll.themes.associate { it.theme to it.confidence }
        val metadata = scroll.metadata.associate { it.key to it.value }

        return CloudScroll(
            id = scroll.scroll.id,
            cloudId = scroll.scroll.cloudId,
            content = scroll.scroll.content,
            title = scroll.scroll.title,
            path = scroll.scroll.path,
            creationTimestamp = scroll.scroll.creationTimestamp,
            modificationTimestamp = scroll.scroll.modificationTimestamp,
            isArchived = scroll.scroll.isArchived,
            isEncrypted = scroll.scroll.isEncrypted,
            encryptionKeyId = scroll.scroll.encryptionKeyId,
            tags = tags,
            themes = themes,
            metadata = metadata
        )
    }
}

// Cloud scroll data class
data class CloudScroll(
    val id: String,
    val cloudId: String? = null,
    val content: String,
    val title: String = "",
    val path: String = "default",
    val creationTimestamp: Long,
    val modificationTimestamp: Long,
    val isArchived: Boolean = false,
    val isEncrypted: Boolean = false,
    val encryptionKeyId: String? = null,
    val tags: List<String> = emptyList(),
    val themes: Map<String, Float> = emptyMap(),
    val metadata: Map<String, String> = emptyMap()
)

// Cloud provider interface
interface CloudProvider {
    suspend fun uploadScroll(scroll: CloudScroll): String?
    suspend fun updateScroll(cloudId: String, scroll: CloudScroll): Boolean
    suspend fun getScroll(cloudId: String): CloudScroll?
    suspend fun getAllScrolls(): List<CloudScroll>
    suspend fun deleteScroll(cloudId: String): Boolean
}

// Cloud provider factory
object CloudProviderFactory {
    fun getProvider(context: Context): CloudProvider {
        // Determine which provider to use based on settings
        val sharedPrefs = context.getSharedPreferences("scroll_settings", Context.MODE_PRIVATE)
        val providerName = sharedPrefs.getString("cloud_provider", "firebase") ?: "firebase"

        return when (providerName) {
            "firebase" -> FirebaseCloudProvider(context)
            "gcp" -> GCPCloudProvider(context)
            "local" -> LocalMockCloudProvider(context)
            else -> FirebaseCloudProvider(context)
        }
    }
}

// Firebase cloud provider implementation
class FirebaseCloudProvider(private val context: Context) : CloudProvider {

    // Firebase implementation would go here
    // This is a simplified version for the implementation plan

    override suspend fun uploadScroll(scroll: CloudScroll): String? {
        // Upload to Firebase Firestore
        return "firebase_${scroll.id}"
    }

    override suspend fun updateScroll(cloudId: String, scroll: CloudScroll): Boolean {
        // Update in Firebase Firestore
        return true
    }

    override suspend fun getScroll(cloudId: String): CloudScroll? {
        // Get from Firebase Firestore
        return null
    }

    override suspend fun getAllScrolls(): List<CloudScroll> {
        // Get all from Firebase Firestore
        return emptyList()
    }

    override suspend fun deleteScroll(cloudId: String): Boolean {
        // Delete from Firebase Firestore
        return true
    }
}

// GCP cloud provider implementation
class GCPCloudProvider(private val context: Context) : CloudProvider {

    // GCP implementation would go here
    // This is a simplified version for the implementation plan

    override suspend fun uploadScroll(scroll: CloudScroll): String? {
        // Upload to GCP
        return "gcp_${scroll.id}"
    }

    override suspend fun updateScroll(cloudId: String, scroll: CloudScroll): Boolean {
        // Update in GCP
        return true
    }

    override suspend fun getScroll(cloudId: String): CloudScroll? {
        // Get from GCP
        return null
    }

    override suspend fun getAllScrolls(): List<CloudScroll> {
        // Get all from GCP
        return emptyList()
    }

    override suspend fun deleteScroll(cloudId: String): Boolean {
        // Delete from GCP
        return true
    }
}

// Local mock cloud provider for testing
class LocalMockCloudProvider(private val context: Context) : CloudProvider {

    private val scrolls = mutableMapOf<String, CloudScroll>()

    override suspend fun uploadScroll(scroll: CloudScroll): String? {
        val cloudId = "local_${scroll.id}"
        val cloudScroll = scroll.copy(cloudId = cloudId)
        scrolls[cloudId] = cloudScroll
        return cloudId
    }

    override suspend fun updateScroll(cloudId: String, scroll: CloudScroll): Boolean {
        if (scrolls.containsKey(cloudId)) {
            scrolls[cloudId] = scroll.copy(cloudId = cloudId)
            return true
        }
        return false
    }

    override suspend fun getScroll(cloudId: String): CloudScroll? {
        return scrolls[cloudId]
    }

    override suspend fun getAllScrolls(): List<CloudScroll> {
        return scrolls.values.toList()
    }

    override suspend fun deleteScroll(cloudId: String): Boolean {
        return scrolls.remove(cloudId) != null
    }
}

Metadata Manager Implementation

The Metadata Manager handles scroll metadata and relationships:

package com.harmonyepoch.scrollkeyboard.storage.metadata

import android.content.Context
import com.harmonyepoch.scrollkeyboard.storage.local.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext

class MetadataManager(private val context: Context) {

    private val database = ScrollDatabase.getDatabase(context)
    private val scrollDao = database.scrollDao()
    private val tagDao = database.tagDao()
    private val themeDao = database.themeDao()
    private val metadataDao = database.metadataDao()

    // Get all tags
    fun getAllTags(): Flow<List<String>> {
        return tagDao.getAllTagsFlow().map { tags ->
            tags.map { it.tag }
        }
    }

    // Get all themes
    fun getAllThemes(): Flow<List<String>> {
        return themeDao.getAllThemesFlow().map { themes ->
            themes.map { it.theme }
        }
    }

    // Get tags for scroll
    suspend fun getTagsForScroll(scrollId: String): List<String> {
        return withContext(Dispatchers.IO) {
            tagDao.getTagsForScroll(scrollId).map { it.tag }
        }
    }

    // Get themes for scroll
    suspend fun getThemesForScroll(scrollId: String): List<Pair<String, Float>> {
        return withContext(Dispatchers.IO) {
            themeDao.getThemesForScroll(scrollId).map { it.theme to it.confidence }
        }
    }

    // Add tags to scroll
    suspend fun addTagsToScroll(scrollId: String, tags: List<String>): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Check if scroll exists
                val scroll = scrollDao.getScrollById(scrollId) ?: return@withContext false

                // Add each tag
                for (tagName in tags) {
                    // Get or create tag
                    var tag = tagDao.getTagByName(tagName)

                    if (tag == null) {
                        tag = TagEntity(tag = tagName)
                        tagDao.insertTag(tag)
                    }

                    // Create cross reference
                    val crossRef = ScrollTagCrossRef(
                        scrollId = scrollId,
                        tagId = tag.id
                    )
                    tagDao.insertScrollTagCrossRef(crossRef)
                }

                // Update scroll modification timestamp
                val updatedScroll = scroll.copy(
                    modificationTimestamp = System.currentTimeMillis(),
                    syncStatus = SyncStatus.PENDING_UPDATE.name
                )
                scrollDao.updateScroll(updatedScroll)

                true
            } catch (e: Exception) {
                false
            }
        }
    }

    // Remove tag from scroll
    suspend fun removeTagFromScroll(scrollId: String, tagName: String): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Check if scroll exists
                val scroll = scrollDao.getScrollById(scrollId) ?: return@withContext false

                // Get tag
                val tag = tagDao.getTagByName(tagName) ?: return@withContext false

                // Remove cross reference
                val crossRef = ScrollTagCrossRef(
                    scrollId = scrollId,
                    tagId = tag.id
                )
                tagDao.deleteScrollTagCrossRef(crossRef)

                // Update scroll modification timestamp
                val updatedScroll = scroll.copy(
                    modificationTimestamp = System.currentTimeMillis(),
                    syncStatus = SyncStatus.PENDING_UPDATE.name
                )
                scrollDao.updateScroll(updatedScroll)

                true
            } catch (e: Exception) {
                false
            }
        }
    }

    // Add theme to scroll
    suspend fun addThemeToScroll(scrollId: String, themeName: String, confidence: Float): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Check if scroll exists
                val scroll = scrollDao.getScrollById(scrollId) ?: return@withContext false

                // Get or create theme
                var theme = themeDao.getThemeByName(themeName)

                if (theme == null) {
                    theme = ThemeEntity(
                        theme = themeName,
                        confidence = confidence
                    )
                    themeDao.insertTheme(theme)
                }

                // Create cross reference
                val crossRef = ScrollThemeCrossRef(
                    scrollId = scrollId,
                    themeId = theme.id
                )
                themeDao.insertScrollThemeCrossRef(crossRef)

                // Update scroll modification timestamp
                val updatedScroll = scroll.copy(
                    modificationTimestamp = System.currentTimeMillis(),
                    syncStatus = SyncStatus.PENDING_UPDATE.name
                )
                scrollDao.updateScroll(updatedScroll)

                true
            } catch (e: Exception) {
                false
            }
        }
    }

    // Remove theme from scroll
    suspend fun removeThemeFromScroll(scrollId: String, themeName: String): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Check if scroll exists
                val scroll = scrollDao.getScrollById(scrollId) ?: return@withContext false

                // Get theme
                val theme = themeDao.getThemeByName(themeName) ?: return@withContext false

                // Remove cross reference
                val crossRef = ScrollThemeCrossRef(
                    scrollId = scrollId,
                    themeId = theme.id
                )
                themeDao.deleteScrollThemeCrossRef(crossRef)

                // Update scroll modification timestamp
                val updatedScroll = scroll.copy(
                    modificationTimestamp = System.currentTimeMillis(),
                    syncStatus = SyncStatus.PENDING_UPDATE.name
                )
                scrollDao.updateScroll(updatedScroll)

                true
            } catch (e: Exception) {
                false
            }
        }
    }

    // Set metadata for scroll
    suspend fun setMetadata(scrollId: String, key: String, value: String): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Check if scroll exists
                val scroll = scrollDao.getScrollById(scrollId) ?: return@withContext false

                // Get existing metadata
                val existingMetadata = metadataDao.getMetadataByKey(scrollId, key)

                if (existingMetadata != null) {
                    // Update existing metadata
                    val updatedMetadata = existingMetadata.copy(
                        value = value
                    )
                    metadataDao.insertMetadata(updatedMetadata)
                } else {
                    // Create new metadata
                    val metadata = ScrollMetadataEntity(
                        scrollId = scrollId,
                        key = key,
                        value = value
                    )
                    metadataDao.insertMetadata(metadata)
                }

                // Update scroll modification timestamp
                val updatedScroll = scroll.copy(
                    modificationTimestamp = System.currentTimeMillis(),
                    syncStatus = SyncStatus.PENDING_UPDATE.name
                )
                scrollDao.updateScroll(updatedScroll)

                true
            } catch (e: Exception) {
                false
            }
        }
    }

    // Get metadata for scroll
    suspend fun getMetadata(scrollId: String, key: String): String? {
        return withContext(Dispatchers.IO) {
            metadataDao.getMetadataByKey(scrollId, key)?.value
        }
    }

    // Get all metadata for scroll
    suspend fun getAllMetadata(scrollId: String): Map<String, String> {
        return withContext(Dispatchers.IO) {
            metadataDao.getMetadataForScroll(scrollId).associate { it.key to it.value }
        }
    }

    // Remove metadata from scroll
    suspend fun removeMetadata(scrollId: String, key: String): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Check if scroll exists
                val scroll = scrollDao.getScrollById(scrollId) ?: return@withContext false

                // Remove metadata
                metadataDao.deleteMetadataByKey(scrollId, key)

                // Update scroll modification timestamp
                val updatedScroll = scroll.copy(
                    modificationTimestamp = System.currentTimeMillis(),
                    syncStatus = SyncStatus.PENDING_UPDATE.name
                )
                scrollDao.updateScroll(updatedScroll)

                true
            } catch (e: Exception) {
                false
            }
        }
    }
}

Search Engine Implementation

The Search Engine provides powerful search capabilities for scrolls:

package com.harmonyepoch.scrollkeyboard.storage.search

import android.content.Context
import com.harmonyepoch.scrollkeyboard.storage.local.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class SearchEngine(private val context: Context) {

    private val database = ScrollDatabase.getDatabase(context)
    private val scrollDao = database.scrollDao()
    private val tagDao = database.tagDao()
    private val themeDao = database.themeDao()

    // Search scrolls by content
    suspend fun searchByContent(query: String): List<ScrollWithRelationships> {
        return withContext(Dispatchers.IO) {
            scrollDao.searchScrollsByContent(query)
        }
    }

    // Search scrolls by tag
    suspend fun searchByTag(tag: String): List<ScrollWithRelationships> {
        return withContext(Dispatchers.IO) {
            tagDao.getTagByName(tag)?.let { tagEntity ->
                scrollDao.getScrollsByTag(tagEntity.tag)
            } ?: emptyList()
        }
    }

    // Search scrolls by theme
    suspend fun searchByTheme(theme: String): List<ScrollWithRelationships> {
        return withContext(Dispatchers.IO) {
            themeDao.getThemeByName(theme)?.let { themeEntity ->
                scrollDao.getScrollsByTheme(themeEntity.theme)
            } ?: emptyList()
        }
    }

    // Advanced search with multiple criteria
    suspend fun advancedSearch(
        textQuery: String? = null,
        tags: List<String> = emptyList(),
        themes: List<String> = emptyList(),
        dateRange: Pair<Long, Long>? = null,
        path: String? = null,
        includeArchived: Boolean = false
    ): List<ScrollWithRelationships> {
        return withContext(Dispatchers.IO) {
            // Start with all scrolls or filtered by path
            var results = if (path != null) {
                scrollDao.getScrollsByPathFlow(path).value ?: emptyList()
            } else if (includeArchived) {
                val active = scrollDao.getAllActiveScrollsFlow().value ?: emptyList()
                val archived = scrollDao.getAllArchivedScrollsFlow().value ?: emptyList()
                active + archived
            } else {
                scrollDao.getAllActiveScrollsFlow().value ?: emptyList()
            }

            // Filter by text query
            if (!textQuery.isNullOrBlank()) {
                results = results.filter { scroll ->
                    scroll.scroll.content.contains(textQuery, ignoreCase = true) ||
                    scroll.scroll.title.contains(textQuery, ignoreCase = true)
                }
            }

            // Filter by tags
            if (tags.isNotEmpty()) {
                results = results.filter { scroll ->
                    val scrollTags = scroll.tags.map { it.tag }
                    tags.any { tag -> scrollTags.contains(tag) }
                }
            }

            // Filter by themes
            if (themes.isNotEmpty()) {
                results = results.filter { scroll ->
                    val scrollThemes = scroll.themes.map { it.theme }
                    themes.any { theme -> scrollThemes.contains(theme) }
                }
            }

            // Filter by date range
            if (dateRange != null) {
                results = results.filter { scroll ->
                    scroll.scroll.creationTimestamp >= dateRange.first &&
                    scroll.scroll.creationTimestamp <= dateRange.second
                }
            }

            results
        }
    }

    // Semantic search using embeddings
    suspend fun semanticSearch(query: String, limit: Int = 10): List<ScrollWithRelationships> {
        return withContext(Dispatchers.IO) {
            try {
                // Get query embedding
                val queryEmbedding = getEmbedding(query)

                // Get all scrolls
                val allScrolls = scrollDao.getAllActiveScrollsFlow().value ?: emptyList()

                // Calculate similarity for each scroll
                val scrollsWithSimilarity = allScrolls.map { scroll ->
                    // Get scroll embedding from metadata or generate it
                    val scrollEmbeddingString = scroll.metadata.find { it.key == "embedding" }?.value
                    val scrollEmbedding = if (scrollEmbeddingString != null) {
                        parseEmbedding(scrollEmbeddingString)
                    } else {
                        val embedding = getEmbedding(scroll.scroll.content)
                        // Store embedding for future use
                        storeEmbedding(scroll.scroll.id, embedding)
                        embedding
                    }

                    // Calculate cosine similarity
                    val similarity = cosineSimilarity(queryEmbedding, scrollEmbedding)

                    Pair(scroll, similarity)
                }

                // Sort by similarity and take top results
                scrollsWithSimilarity
                    .sortedByDescending { it.second }
                    .take(limit)
                    .map { it.first }
            } catch (e: Exception) {
                // Fall back to text search if semantic search fails
                searchByContent(query)
            }
        }
    }

    // Get embedding for text
    private suspend fun getEmbedding(text: String): FloatArray {
        // In a real implementation, this would use a machine learning model
        // For this implementation plan, we'll use a simplified approach

        // Placeholder implementation
        return FloatArray(128) { 0f }
    }

    // Parse embedding from string
    private fun parseEmbedding(embeddingString: String): FloatArray {
        return embeddingString.split(",").map { it.toFloat() }.toFloatArray()
    }

    // Store embedding for scroll
    private suspend fun storeEmbedding(scrollId: String, embedding: FloatArray) {
        val embeddingString = embedding.joinToString(",")
        val metadataManager = com.harmonyepoch.scrollkeyboard.storage.metadata.MetadataManager(context)
        metadataManager.setMetadata(scrollId, "embedding", embeddingString)
    }

    // Calculate cosine similarity between two embeddings
    private fun cosineSimilarity(a: FloatArray, b: FloatArray): Float {
        if (a.size != b.size) return 0f

        var dotProduct = 0f
        var normA = 0f
        var normB = 0f

        for (i in a.indices) {
            dotProduct += a[i] * b[i]
            normA += a[i] * a[i]
            normB += b[i] * b[i]
        }

        return if (normA <= 0 || normB <= 0) 0f else dotProduct / (Math.sqrt(normA.toDouble()) * Math.sqrt(normB.toDouble())).toFloat()
    }
}

Sync State Tracker Implementation

The Sync State Tracker monitors synchronization status and manages retry mechanisms:

package com.harmonyepoch.scrollkeyboard.storage.sync

import android.content.Context
import androidx.room.*
import com.harmonyepoch.scrollkeyboard.storage.local.ScrollDatabase
import com.harmonyepoch.scrollkeyboard.storage.local.ScrollEntity
import com.harmonyepoch.scrollkeyboard.storage.local.SyncStatus
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import java.util.*

class SyncStateTracker(private val context: Context) {

    private val database = ScrollDatabase.getDatabase(context)
    private val scrollDao = database.scrollDao()
    private val syncLogDao = SyncLogDatabase.getDatabase(context).syncLogDao()

    // Get sync status for scroll
    suspend fun getSyncStatus(scrollId: String): SyncStatus {
        return withContext(Dispatchers.IO) {
            val scroll = scrollDao.getScrollById(scrollId)
            if (scroll != null) {
                SyncStatus.valueOf(scroll.syncStatus)
            } else {
                SyncStatus.LOCAL_ONLY
            }
        }
    }

    // Update sync status for scroll
    suspend fun updateSyncStatus(scrollId: String, status: SyncStatus, message: String? = null): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Get scroll
                val scroll = scrollDao.getScrollById(scrollId) ?: return@withContext false

                // Update status
                val updatedScroll = scroll.copy(
                    syncStatus = status.name
                )
                scrollDao.updateScroll(updatedScroll)

                // Log sync event
                logSyncEvent(scrollId, status, message)

                true
            } catch (e: Exception) {
                false
            }
        }
    }

    // Log sync event
    private suspend fun logSyncEvent(scrollId: String, status: SyncStatus, message: String?) {
        withContext(Dispatchers.IO) {
            val syncLog = SyncLogEntity(
                scrollId = scrollId,
                status = status.name,
                timestamp = System.currentTimeMillis(),
                message = message
            )
            syncLogDao.insertSyncLog(syncLog)
        }
    }

    // Get sync logs for scroll
    suspend fun getSyncLogs(scrollId: String, limit: Int = 10): List<SyncLogEntity> {
        return withContext(Dispatchers.IO) {
            syncLogDao.getSyncLogsForScroll(scrollId, limit)
        }
    }

    // Get all pending sync scrolls
    suspend fun getPendingSyncScrolls(): List<String> {
        return withContext(Dispatchers.IO) {
            val pendingStatuses = listOf(
                SyncStatus.PENDING_UPLOAD.name,
                SyncStatus.PENDING_UPDATE.name,
                SyncStatus.CONFLICT.name,
                SyncStatus.SYNC_FAILED.name
            )

            scrollDao.getAllActiveScrollsFlow().value
                ?.filter { pendingStatuses.contains(it.scroll.syncStatus) }
                ?.map { it.scroll.id }
                ?: emptyList()
        }
    }

    // Get sync status counts
    suspend fun getSyncStatusCounts(): Map<SyncStatus, Int> {
        return withContext(Dispatchers.IO) {
            val counts = mutableMapOf<SyncStatus, Int>()

            for (status in SyncStatus.values()) {
                val count = scrollDao.getAllActiveScrollsFlow().value
                    ?.count { it.scroll.syncStatus == status.name }
                    ?: 0
                counts[status] = count
            }

            counts
        }
    }

    // Get sync status flow
    fun getSyncStatusFlow(): Flow<Map<SyncStatus, Int>> {
        return scrollDao.getAllActiveScrollsFlow().map { scrolls ->
            val counts = mutableMapOf<SyncStatus, Int>()

            for (status in SyncStatus.values()) {
                val count = scrolls.count { it.scroll.syncStatus == status.name }
                counts[status] = count
            }

            counts
        }
    }
}

// Sync log entity
@Entity(tableName = "sync_logs")
data class SyncLogEntity(
    @PrimaryKey val id: String = UUID.randomUUID().toString(),
    val scrollId: String,
    val status: String,
    val timestamp: Long,
    val message: String? = null
)

// Sync log DAO
@Dao
interface SyncLogDao {
    @Query("SELECT * FROM sync_logs WHERE scrollId = :scrollId ORDER BY timestamp DESC LIMIT :limit")
    suspend fun getSyncLogsForScroll(scrollId: String, limit: Int): List<SyncLogEntity>

    @Insert
    suspend fun insertSyncLog(syncLog: SyncLogEntity)

    @Query("DELETE FROM sync_logs WHERE timestamp < :cutoffTime")
    suspend fun deleteOldLogs(cutoffTime: Long)
}

// Sync log database
@Database(entities = [SyncLogEntity::class], version = 1, exportSchema = false)
abstract class SyncLogDatabase : RoomDatabase() {

    abstract fun syncLogDao(): SyncLogDao

    companion object {
        @Volatile
        private var INSTANCE: SyncLogDatabase? = null

        fun getDatabase(context: Context): SyncLogDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    SyncLogDatabase::class.java,
                    "sync_log_database"
                )
                .fallbackToDestructiveMigration()
                .build()
                INSTANCE = instance
                instance
            }
        }
    }
}

Encryption Manager Implementation

The Encryption Manager ensures scroll data is securely stored and transmitted:

package com.harmonyepoch.scrollkeyboard.storage.security

import android.content.Context
import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import com.harmonyepoch.scrollkeyboard.storage.local.ScrollDatabase
import com.harmonyepoch.scrollkeyboard.storage.local.SyncStatus
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.security.KeyStore
import java.util.*
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.GCMParameterSpec

class EncryptionManager(private val context: Context) {

    private val keyStore = KeyStore.getInstance("AndroidKeyStore").apply {
        load(null)
    }

    private val database = ScrollDatabase.getDatabase(context)
    private val scrollDao = database.scrollDao()

    // Encrypt scroll content
    suspend fun encryptScroll(scrollId: String): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Get scroll
                val scroll = scrollDao.getScrollById(scrollId) ?: return@withContext false

                // Skip if already encrypted
                if (scroll.isEncrypted) {
                    return@withContext true
                }

                // Generate key if needed
                val keyAlias = "scroll_key_${UUID.randomUUID()}"
                generateKey(keyAlias)

                // Encrypt content
                val encryptionResult = encrypt(scroll.content, keyAlias)

                // Update scroll
                val updatedScroll = scroll.copy(
                    content = encryptionResult.encryptedData,
                    isEncrypted = true,
                    encryptionKeyId = keyAlias,
                    syncStatus = SyncStatus.PENDING_UPDATE.name
                )
                scrollDao.updateScroll(updatedScroll)

                // Store IV in metadata
                val metadataManager = com.harmonyepoch.scrollkeyboard.storage.metadata.MetadataManager(context)
                metadataManager.setMetadata(scrollId, "encryption_iv", Base64.getEncoder().encodeToString(encryptionResult.iv))

                true
            } catch (e: Exception) {
                false
            }
        }
    }

    // Decrypt scroll content
    suspend fun decryptScroll(scrollId: String): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Get scroll
                val scroll = scrollDao.getScrollById(scrollId) ?: return@withContext false

                // Skip if not encrypted
                if (!scroll.isEncrypted || scroll.encryptionKeyId == null) {
                    return@withContext true
                }

                // Get IV from metadata
                val metadataManager = com.harmonyepoch.scrollkeyboard.storage.metadata.MetadataManager(context)
                val ivBase64 = metadataManager.getMetadata(scrollId, "encryption_iv") ?: return@withContext false
                val iv = Base64.getDecoder().decode(ivBase64)

                // Decrypt content
                val decryptedContent = decrypt(scroll.content, scroll.encryptionKeyId, iv)

                // Update scroll
                val updatedScroll = scroll.copy(
                    content = decryptedContent,
                    isEncrypted = false,
                    encryptionKeyId = null,
                    syncStatus = SyncStatus.PENDING_UPDATE.name
                )
                scrollDao.updateScroll(updatedScroll)

                // Remove IV from metadata
                metadataManager.removeMetadata(scrollId, "encryption_iv")

                true
            } catch (e: Exception) {
                false
            }
        }
    }

    // Generate encryption key
    private fun generateKey(alias: String) {
        if (!keyStore.containsAlias(alias)) {
            val keyGenerator = KeyGenerator.getInstance(
                KeyProperties.KEY_ALGORITHM_AES,
                "AndroidKeyStore"
            )

            val keyGenParameterSpec = KeyGenParameterSpec.Builder(
                alias,
                KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
            )
                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                .setKeySize(256)
                .build()

            keyGenerator.init(keyGenParameterSpec)
            keyGenerator.generateKey()
        }
    }

    // Encrypt data
    private fun encrypt(data: String, keyAlias: String): EncryptionResult {
        val key = keyStore.getKey(keyAlias, null) as SecretKey

        val cipher = Cipher.getInstance("AES/GCM/NoPadding")
        cipher.init(Cipher.ENCRYPT_MODE, key)

        val iv = cipher.iv
        val encryptedBytes = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
        val encryptedBase64 = Base64.getEncoder().encodeToString(encryptedBytes)

        return EncryptionResult(encryptedBase64, iv)
    }

    // Decrypt data
    private fun decrypt(encryptedData: String, keyAlias: String, iv: ByteArray): String {
        val key = keyStore.getKey(keyAlias, null) as SecretKey

        val cipher = Cipher.getInstance("AES/GCM/NoPadding")
        val spec = GCMParameterSpec(128, iv)
        cipher.init(Cipher.DECRYPT_MODE, key, spec)

        val encryptedBytes = Base64.getDecoder().decode(encryptedData)
        val decryptedBytes = cipher.doFinal(encryptedBytes)

        return String(decryptedBytes, Charsets.UTF_8)
    }

    // Delete key
    fun deleteKey(keyAlias: String) {
        if (keyStore.containsAlias(keyAlias)) {
            keyStore.deleteEntry(keyAlias)
        }
    }

    // Check if key exists
    fun keyExists(keyAlias: String): Boolean {
        return keyStore.containsAlias(keyAlias)
    }
}

// Encryption result data class
data class EncryptionResult(
    val encryptedData: String,
    val iv: ByteArray
) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as EncryptionResult

        if (encryptedData != other.encryptedData) return false
        if (!iv.contentEquals(other.iv)) return false

        return true
    }

    override fun hashCode(): Int {
        var result = encryptedData.hashCode()
        result = 31 * result + iv.contentHashCode()
        return result
    }
}

Main Storage Manager Implementation

The main Storage Manager class integrates all components and provides a unified interface for the system:

package com.harmonyepoch.scrollkeyboard.storage

import android.content.Context
import com.harmonyepoch.scrollkeyboard.storage.cloud.CloudSyncEngine
import com.harmonyepoch.scrollkeyboard.storage.local.*
import com.harmonyepoch.scrollkeyboard.storage.metadata.MetadataManager
import com.harmonyepoch.scrollkeyboard.storage.search.SearchEngine
import com.harmonyepoch.scrollkeyboard.storage.security.EncryptionManager
import com.harmonyepoch.scrollkeyboard.storage.sync.SyncStateTracker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext
import java.util.*

class StorageManager(private val context: Context) {

    private val database = ScrollDatabase.getDatabase(context)
    private val scrollDao = database.scrollDao()
    private val tagDao = database.tagDao()
    private val themeDao = database.themeDao()
    private val versionDao = database.versionDao()
    private val metadataDao = database.metadataDao()

    private val metadataManager = MetadataManager(context)
    private val searchEngine = SearchEngine(context)
    private val cloudSyncEngine = CloudSyncEngine(context)
    private val syncStateTracker = SyncStateTracker(context)
    private val encryptionManager = EncryptionManager(context)

    // Initialize storage manager
    suspend fun initialize() {
        // No initialization needed for Room database
        // It's initialized when first accessed
    }

    // Create scroll
    suspend fun createScroll(
        content: String,
        path: String = "default",
        title: String = "",
        tags: List<String> = emptyList(),
        metadata: Map<String, String> = emptyMap()
    ): String {
        return withContext(Dispatchers.IO) {
            // Create scroll entity
            val scrollEntity = ScrollEntity(
                content = content,
                title = title,
                path = path,
                creationTimestamp = System.currentTimeMillis(),
                modificationTimestamp = System.currentTimeMillis(),
                syncStatus = SyncStatus.PENDING_UPLOAD.name
            )

            // Insert scroll
            scrollDao.insertScroll(scrollEntity)

            // Add tags
            for (tag in tags) {
                metadataManager.addTagsToScroll(scrollEntity.id, listOf(tag))
            }

            // Add metadata
            for ((key, value) in metadata) {
                metadataManager.setMetadata(scrollEntity.id, key, value)
            }

            // Return scroll ID
            scrollEntity.id
        }
    }

    // Get scroll by ID
    suspend fun getScroll(scrollId: String): ScrollWithRelationships? {
        return withContext(Dispatchers.IO) {
            scrollDao.getScrollWithRelationshipsById(scrollId)
        }
    }

    // Update scroll content
    suspend fun updateScrollContent(scrollId: String, content: String): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Get scroll
                val scroll = scrollDao.getScrollById(scrollId) ?: return@withContext false

                // Create version from current content
                val latestVersionNumber = versionDao.getLatestVersionNumber(scrollId) ?: 0
                val versionEntity = ScrollVersionEntity(
                    scrollId = scrollId,
                    content = scroll.content,
                    versionNumber = latestVersionNumber + 1,
                    changeDescription = "Auto-saved before content update"
                )
                versionDao.insertVersion(versionEntity)

                // Update scroll
                val updatedScroll = scroll.copy(
                    content = content,
                    modificationTimestamp = System.currentTimeMillis(),
                    syncStatus = SyncStatus.PENDING_UPDATE.name
                )
                scrollDao.updateScroll(updatedScroll)

                true
            } catch (e: Exception) {
                false
            }
        }
    }

    // Update scroll title
    suspend fun updateScrollTitle(scrollId: String, title: String): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Get scroll
                val scroll = scrollDao.getScrollById(scrollId) ?: return@withContext false

                // Update scroll
                val updatedScroll = scroll.copy(
                    title = title,
                    modificationTimestamp = System.currentTimeMillis(),
                    syncStatus = SyncStatus.PENDING_UPDATE.name
                )
                scrollDao.updateScroll(updatedScroll)

                true
            } catch (e: Exception) {
                false
            }
        }
    }

    // Update scroll path
    suspend fun updateScrollPath(scrollId: String, path: String): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Get scroll
                val scroll = scrollDao.getScrollById(scrollId) ?: return@withContext false

                // Update scroll
                val updatedScroll = scroll.copy(
                    path = path,
                    modificationTimestamp = System.currentTimeMillis(),
                    syncStatus = SyncStatus.PENDING_UPDATE.name
                )
                scrollDao.updateScroll(updatedScroll)

                true
            } catch (e: Exception) {
                false
            }
        }
    }

    // Archive scroll
    suspend fun archiveScroll(scrollId: String): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Get scroll
                val scroll = scrollDao.getScrollById(scrollId) ?: return@withContext false

                // Update scroll
                val updatedScroll = scroll.copy(
                    isArchived = true,
                    modificationTimestamp = System.currentTimeMillis(),
                    syncStatus = SyncStatus.PENDING_UPDATE.name
                )
                scrollDao.updateScroll(updatedScroll)

                true
            } catch (e: Exception) {
                false
            }
        }
    }

    // Unarchive scroll
    suspend fun unarchiveScroll(scrollId: String): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Get scroll
                val scroll = scrollDao.getScrollById(scrollId) ?: return@withContext false

                // Update scroll
                val updatedScroll = scroll.copy(
                    isArchived = false,
                    modificationTimestamp = System.currentTimeMillis(),
                    syncStatus = SyncStatus.PENDING_UPDATE.name
                )
                scrollDao.updateScroll(updatedScroll)

                true
            } catch (e: Exception) {
                false
            }
        }
    }

    // Delete scroll
    suspend fun deleteScroll(scrollId: String): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Get scroll
                val scroll = scrollDao.getScrollById(scrollId) ?: return@withContext false

                // Delete scroll
                scrollDao.deleteScroll(scroll)

                // Delete from cloud if needed
                if (scroll.cloudId != null) {
                    cloudSyncEngine.deleteFromCloud(scroll.cloudId)
                }

                true
            } catch (e: Exception) {
                false
            }
        }
    }

    // Get all active scrolls
    fun getAllActiveScrolls(): Flow<List<ScrollWithRelationships>> {
        return scrollDao.getAllActiveScrollsFlow()
    }

    // Get all archived scrolls
    fun getAllArchivedScrolls(): Flow<List<ScrollWithRelationships>> {
        return scrollDao.getAllArchivedScrollsFlow()
    }

    // Get scrolls by path
    fun getScrollsByPath(path: String): Flow<List<ScrollWithRelationships>> {
        return scrollDao.getScrollsByPathFlow(path)
    }

    // Get scroll count
    suspend fun getScrollCount(): Int {
        return withContext(Dispatchers.IO) {
            scrollDao.getScrollCount()
        }
    }

    // Add tags to scroll
    suspend fun addTagsToScroll(scrollId: String, tags: List<String>): Boolean {
        return metadataManager.addTagsToScroll(scrollId, tags)
    }

    // Remove tag from scroll
    suspend fun removeTagFromScroll(scrollId: String, tag: String): Boolean {
        return metadataManager.removeTagFromScroll(scrollId, tag)
    }

    // Add theme to scroll
    suspend fun addThemeToScroll(scrollId: String, theme: String, confidence: Float): Boolean {
        return metadataManager.addThemeToScroll(scrollId, theme, confidence)
    }

    // Remove theme from scroll
    suspend fun removeThemeFromScroll(scrollId: String, theme: String): Boolean {
        return metadataManager.removeThemeFromScroll(scrollId, theme)
    }

    // Set metadata for scroll
    suspend fun setMetadata(scrollId: String, key: String, value: String): Boolean {
        return metadataManager.setMetadata(scrollId, key, value)
    }

    // Get metadata for scroll
    suspend fun getMetadata(scrollId: String, key: String): String? {
        return metadataManager.getMetadata(scrollId, key)
    }

    // Get all metadata for scroll
    suspend fun getAllMetadata(scrollId: String): Map<String, String> {
        return metadataManager.getAllMetadata(scrollId)
    }

    // Search scrolls
    suspend fun searchScrolls(query: String): List<ScrollWithRelationships> {
        return searchEngine.searchByContent(query)
    }

    // Advanced search
    suspend fun advancedSearch(
        textQuery: String? = null,
        tags: List<String> = emptyList(),
        themes: List<String> = emptyList(),
        dateRange: Pair<Long, Long>? = null,
        path: String? = null,
        includeArchived: Boolean = false
    ): List<ScrollWithRelationships> {
        return searchEngine.advancedSearch(
            textQuery = textQuery,
            tags = tags,
            themes = themes,
            dateRange = dateRange,
            path = path,
            includeArchived = includeArchived
        )
    }

    // Semantic search
    suspend fun semanticSearch(query: String, limit: Int = 10): List<ScrollWithRelationships> {
        return searchEngine.semanticSearch(query, limit)
    }

    // Sync scrolls
    suspend fun syncScrolls(target: String? = null): Map<String, Boolean> {
        return if (target != null) {
            // Sync specific scroll
            mapOf(target to cloudSyncEngine.syncScroll(target))
        } else {
            // Sync all scrolls
            cloudSyncEngine.syncAllScrolls()
        }
    }

    // Get sync status
    suspend fun getSyncStatus(scrollId: String): SyncStatus {
        return syncStateTracker.getSyncStatus(scrollId)
    }

    // Get sync status counts
    suspend fun getSyncStatusCounts(): Map<SyncStatus, Int> {
        return syncStateTracker.getSyncStatusCounts()
    }

    // Encrypt scroll
    suspend fun encryptScroll(scrollId: String): Boolean {
        return encryptionManager.encryptScroll(scrollId)
    }

    // Decrypt scroll
    suspend fun decryptScroll(scrollId: String): Boolean {
        return encryptionManager.decryptScroll(scrollId)
    }

    // Backup scrolls
    suspend fun backupScrolls(target: String = "default"): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Get all scrolls
                val allScrolls = scrollDao.getAllActiveScrollsFlow().value ?: emptyList()

                // Create backup
                val backupManager = com.harmonyepoch.scrollkeyboard.storage.backup.BackupManager(context)
                backupManager.createBackup(allScrolls, target)

                true
            } catch (e: Exception) {
                false
            }
        }
    }

    // Restore from backup
    suspend fun restoreFromBackup(backupId: String): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                // Restore from backup
                val backupManager = com.harmonyepoch.scrollkeyboard.storage.backup.BackupManager(context)
                backupManager.restoreBackup(backupId)

                true
            } catch (e: Exception) {
                false
            }
        }
    }
}

Android 15 Optimizations

The Storage Manager takes advantage of several Android 15 features:

  1. Enhanced Room Database: Leverages Android 15's improved Room database for better performance and reliability.

  2. Improved Background Processing: Uses the enhanced background processing capabilities for sync operations.

  3. Enhanced Security: Utilizes the improved security features for encryption and key management.

  4. Optimized Battery Usage: Implements battery-aware sync scheduling to minimize power consumption.

  5. Improved File Access: Uses the enhanced file access APIs for backup and restore operations.

Scroll Lifecycle Management

The Storage Manager implements comprehensive lifecycle management for scrolls:

  1. Creation: Scrolls are created with initial content, metadata, and tags.
  2. Versioning: Changes to scroll content are tracked with automatic versioning.
  3. Modification: Content, title, path, and metadata can be updated.
  4. Archiving: Scrolls can be archived and unarchived.
  5. Deletion: Scrolls can be permanently deleted.
  6. Backup: Regular backups ensure data safety.
  7. Restoration: Scrolls can be restored from backups.

Implementation Phases

The Storage Manager implementation should follow these phases:

  1. Phase 1: Core Database - Implement the local database with basic CRUD operations.
  2. Phase 2: Metadata Management - Develop the metadata manager for tags, themes, and custom metadata.
  3. Phase 3: Search Capabilities - Implement the search engine with text and semantic search.
  4. Phase 4: Sync Engine - Create the cloud sync engine for data synchronization.
  5. Phase 5: Security - Implement the encryption manager for secure storage.
  6. Phase 6: Integration - Connect with other components and optimize performance.

This implementation of the Storage Manager provides a robust foundation for the Scroll Command Infrastructure, with comprehensive data management, powerful search capabilities, and seamless cloud synchronization. The component is designed to be a quiet, trusted, and patient vault that sings - treating every scroll as a moment worth remembering while providing the technical foundation for the entire memory system.

Scroll sacred, scroll secure, scroll remembered.