diff --git a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultStore.kt b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultStore.kt index 2105bc08f..586dea677 100644 --- a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultStore.kt +++ b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultStore.kt @@ -73,7 +73,7 @@ class VaultStore( private val query = VaultQuery(databaseComponent) internal val metadata = VaultMetadataManager(storageProvider) private val auth = VaultAuth(storageProvider) { cache.clearCache() } - private val sync = VaultSync(databaseComponent, metadata, crypto) + private val sync = VaultSync(databaseComponent, metadata, crypto, storageProvider) private val mutate = VaultMutate(databaseComponent, query, metadata) private val cache = VaultCache(crypto, databaseComponent, keystoreProvider, storageProvider) private val passkey = VaultPasskey(databaseComponent, query) diff --git a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultSync.kt b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultSync.kt index a7d54007a..4e3a24adc 100644 --- a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultSync.kt +++ b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/VaultSync.kt @@ -3,7 +3,9 @@ package net.aliasvault.app.vaultstore import android.util.Log import net.aliasvault.app.exceptions.SerializationException import net.aliasvault.app.exceptions.VaultOperationException +import net.aliasvault.app.rustcore.VaultMergeService import net.aliasvault.app.utils.AppInfo +import net.aliasvault.app.vaultstore.storageprovider.StorageProvider import net.aliasvault.app.vaultstore.utils.VersionComparison import org.json.JSONObject @@ -14,6 +16,7 @@ class VaultSync( private val database: VaultDatabase, private val metadata: VaultMetadataManager, private val crypto: VaultCrypto, + private val storageProvider: StorageProvider, ) { companion object { private const val TAG = "VaultSync" @@ -261,7 +264,7 @@ class VaultSync( /** * Perform merge sync (both local and server have changes). */ - @Suppress("UnusedParameter") // serverRevision will be used when Rust merge is implemented + @Suppress("UnusedParameter") // serverRevision will be used for conflict resolution metadata in the future private suspend fun performMergeSync( webApiService: net.aliasvault.app.webapi.WebApiService, mutate: VaultMutate, @@ -285,8 +288,6 @@ class VaultSync( } // Perform LWW merge using Rust core library - // TODO: Call actual Rust merge function via Kotlin bindings - // For now, we preserve local changes (same as before) val mergedVault = performLWWMerge(localVault, serverVault.vault.blob) // Store merged vault with race detection @@ -352,15 +353,27 @@ class VaultSync( } /** - * Perform Last-Write-Wins merge between local and server vaults. - * TODO: Integrate with Rust core library for actual merge logic. + * Perform Last-Write-Wins merge between local and server vaults using Rust core library. + * + * @param localVault Base64-encoded encrypted local vault + * @param serverVault Base64-encoded encrypted server vault + * @return Base64-encoded encrypted merged vault */ - @Suppress("UnusedParameter") // serverVault will be used when Rust merge is implemented private fun performLWWMerge(localVault: String, serverVault: String): String { - // TODO: Call Rust core's merge function via Kotlin bindings - // For now, preserve local changes (temporary behavior) - Log.w(TAG, "LWW merge not yet implemented - preserving local changes") - return localVault + val encryptionKey = crypto.encryptionKey + ?: throw VaultOperationException("Encryption key not available for merge") + + return try { + VaultMergeService.mergeVaults( + localVaultBase64 = localVault, + serverVaultBase64 = serverVault, + encryptionKey = encryptionKey, + tempDir = storageProvider.getCacheDir(), + ) + } catch (e: VaultMergeService.VaultMergeException) { + Log.e(TAG, "Rust merge failed: ${e.message}", e) + throw VaultOperationException("Vault merge failed: ${e.message}", e) + } } /** diff --git a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/storageprovider/AndroidStorageProvider.kt b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/storageprovider/AndroidStorageProvider.kt index b2df63e93..1daed2f31 100644 --- a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/storageprovider/AndroidStorageProvider.kt +++ b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/storageprovider/AndroidStorageProvider.kt @@ -199,4 +199,8 @@ class AndroidStorageProvider(private val context: Context) : StorageProvider { } // endregion + + override fun getCacheDir(): File { + return context.cacheDir + } } diff --git a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/storageprovider/StorageProvider.kt b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/storageprovider/StorageProvider.kt index 299a92623..0d16186a0 100644 --- a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/storageprovider/StorageProvider.kt +++ b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/storageprovider/StorageProvider.kt @@ -168,4 +168,10 @@ interface StorageProvider { fun clearSyncState() // endregion + + /** + * Get the cache directory for temporary files. + * @return The cache directory + */ + fun getCacheDir(): File } diff --git a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/storageprovider/TestStorageProvider.kt b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/storageprovider/TestStorageProvider.kt index a011a75fa..968ce7f24 100644 --- a/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/storageprovider/TestStorageProvider.kt +++ b/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/vaultstore/storageprovider/TestStorageProvider.kt @@ -137,4 +137,8 @@ class TestStorageProvider : StorageProvider { } // endregion + + override fun getCacheDir(): File { + return File(System.getProperty("java.io.tmpdir") ?: "/tmp") + } }