Skip to content

Commit e10ad80

Browse files
committed
feat: Enable SQLCipher encryption for Desktop JVM
This commit introduces database encryption support for the Desktop JVM target using SQLCipher, achieving feature parity with the Android and iOS versions. This was accomplished by replacing the standard `sqlite-jdbc` driver with `sqlite-jdbc-crypt`, a fork that bundles SQLCipher. Key changes include: - **feat(db-sqldelight):** - Added `sqlite-jdbc-crypt` as the JDBC driver for the `jvmMain` source set. - Updated `JdbcDatabaseHolder` to construct an encrypted JDBC URL with SQLCipher parameters (`cipher=sqlcipher`, `legacy=4`, and `key`) when a password is provided. - Implemented `JvmCipherUtils` to handle the encryption and decryption of the database file by leveraging SQLCipher's `ATTACH` and `sqlcipher_export` functionalities. - Added `JvmCipherUtilsEncryptionTest` to provide robust testing for the encryption/decryption logic. - **test(desktop):** - Enabled previously ignored encryption-related UI tests for the desktop module (`flowAfterCryptTest`, `settingPasswordTest`). - Improved the reliability of UI tests by adding descriptive logging, explicit waits, and ensuring `requireNotNull` checks in the navigation router for better error reporting. - **refactor(ui-test):** - Enhanced UI test helpers (`waitUntilDisplayed`, `waitAssert`, etc.) with descriptions for better debugging and logging. - Added logging to UI test cases (`SettingPasswordTestCase`, `FlowAfterCryptTestCase`) to trace test execution flow. - **chore(deps):** - Bumped various dependencies, including AGP, Firebase, Compose, and SQLCipher. - Updated package versions for Android, iOS, and Desktop to `8.4.8`. - **docs:** - Updated `README.md` and `ARCHITECTURE.md` files to reflect that encryption is now supported on the Desktop JVM platform. - **fix(android):** - Configured the Crashlytics log writer to capture logs from `Debug` severity and higher in release builds to improve remote diagnostics.
1 parent 7eb17dc commit e10ad80

File tree

35 files changed

+835
-274
lines changed

35 files changed

+835
-274
lines changed

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
- Android app: `./gradlew :app:android:assembleDebug` (APK), `:installDebug` (to device).
2020
- Android instrumentation tests: `./gradlew :app:android:connectedCheck` (requires emulator/device; uses AndroidX Test Orchestrator).
2121
- Desktop app: `./gradlew :app:desktop:run` (launches JVM desktop Compose app).
22+
- Desktop UI tests: `./gradlew :app:desktop:jvmTest` (uses `uiTestJUnit4`).
2223
- iOS: `cd iosApp && pod install` then open `iosApp/iosApp.xcworkspace` in Xcode and run. Regenerate podspec if needed: `./gradlew :app:ios-kit:podspec`.
2324
- Web app: `./gradlew :app:web:wasmJsBrowserDevelopmentRun --continuous` (launches the web app in a browser with hot reload).
2425
- Build without iOS link tasks: `./gradle/build_quick.sh` (see [gradle/build_quick.sh](gradle/build_quick.sh))

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Supported platforms:
3939
| feature \ platform | Android | iOS | Desktop Java | Web |
4040
|:------------------:|:-------:|:---:|:------------:|:---:|
4141
| database |||||
42-
| encryption ||| | |
42+
| encryption ||| | |
4343
| ui |||||
4444

4545
Check out [CONTRIBUTING.md](/CONTRIBUTING.md) if you want to develop missing features.

app/android/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ android {
2424
applicationId = "com.softartdev.noteroom"
2525
minSdk = libs.versions.minSdk.get().toInt()
2626
targetSdk = libs.versions.targetSdk.get().toInt()
27-
versionCode = 847
28-
versionName = "8.4.7"
27+
versionCode = 848
28+
versionName = "8.4.8"
2929
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
3030
testInstrumentationRunnerArguments["clearPackageData"] = "true"
3131
vectorDrawables.useSupportLibrary = true

app/android/src/main/java/com/softartdev/notedelight/MainApplication.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package com.softartdev.notedelight
55
import android.app.Application
66
import co.touchlab.kermit.ExperimentalKermitApi
77
import co.touchlab.kermit.Logger
8+
import co.touchlab.kermit.Severity
89
import co.touchlab.kermit.crashlytics.CrashlyticsLogWriter
910
import co.touchlab.kermit.platformLogWriter
1011
import com.softartdev.notedelight.di.sharedModules
@@ -21,7 +22,7 @@ class MainApplication : Application() {
2122
super.onCreate()
2223
if (isInLeakCanaryAnalyzerProcess) return
2324
Logger.setTag(DEFAULT_APP_LOG_TAG)
24-
Logger.setLogWriters(if (BuildConfig.DEBUG) platformLogWriter() else CrashlyticsLogWriter())
25+
Logger.setLogWriters(if (BuildConfig.DEBUG) platformLogWriter() else CrashlyticsLogWriter(minSeverity = Severity.Debug))
2526
startKoin {
2627
kermitLogger()
2728
androidContext(this@MainApplication)

app/desktop/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Overview
44

5-
The `app:desktop` module is the **Desktop JVM application** for NoteDelight, built with Compose Multiplatform. It provides a native desktop experience on Windows, macOS, and Linux with full feature parity to the mobile apps.
5+
The `app:desktop` module is the **Desktop JVM application** for NoteDelight, built with Compose Multiplatform. It provides a native desktop experience on Windows, macOS, and Linux with full feature parity to the mobile apps, including database encryption support via SQLCipher.
66

77
## Purpose
88

@@ -78,7 +78,7 @@ See `app/desktop/keystore.properties` for the stub template. For CI/CD, the cert
7878

7979
### Desktop UI Tests
8080

81-
Located in `src/jvmTest/`:
81+
Located in `src/jvmTest/`. The desktop UI tests include full encryption test coverage (setting password, flow after encryption, etc.).
8282

8383
### Running Tests
8484

app/desktop/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ compose.desktop {
5050
nativeDistributions {
5151
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
5252
packageName = "Note Delight"
53-
packageVersion = "8.4.7"
53+
packageVersion = "8.4.8"
5454
description = "Note app with encryption"
5555
copyright = "© 2023 SoftArtDev"
5656
macOS.iconFile.set(project.file("src/jvmMain/resources/app_icon.icns"))

app/desktop/src/jvmTest/kotlin/com/softartdev/notedelight/ui/DesktopUiTests.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import androidx.compose.ui.test.junit4.ComposeContentTestRule
1111
import androidx.compose.ui.test.junit4.createComposeRule
1212
import androidx.compose.ui.test.onNodeWithContentDescription
1313
import androidx.compose.ui.test.performClick
14+
import androidx.lifecycle.Lifecycle
1415
import androidx.lifecycle.compose.LocalLifecycleOwner
1516
import co.touchlab.kermit.Logger
1617
import co.touchlab.kermit.platformLogWriter
@@ -26,14 +27,14 @@ import kotlinx.coroutines.swing.Swing
2627
import kotlinx.coroutines.test.runTest
2728
import org.junit.After
2829
import org.junit.Before
29-
import org.junit.Ignore
3030
import org.junit.Rule
3131
import org.junit.Test
3232
import org.koin.core.context.GlobalContext
3333
import org.koin.core.context.loadKoinModules
3434
import org.koin.core.context.startKoin
3535
import org.koin.core.context.unloadKoinModules
3636
import org.koin.java.KoinJavaComponent.get
37+
import java.io.File
3738

3839
class DesktopUiTests : AbstractUiTests() {
3940

@@ -51,16 +52,22 @@ class DesktopUiTests : AbstractUiTests() {
5152
else -> loadKoinModules(sharedModules + uiTestModules)
5253
}
5354
val safeRepo: SafeRepo = get(SafeRepo::class.java)
55+
safeRepo.closeDatabase()
56+
File(safeRepo.dbPath).takeIf(File::exists)?.delete()
5457
safeRepo.buildDbIfNeed()
5558
val noteDAO: NoteDAO = get(NoteDAO::class.java)
5659
noteDAO.deleteAll()
5760
super.setUp()
58-
val lifecycleOwner = TestLifecycleOwner(coroutineDispatcher = Dispatchers.Swing)
61+
val lifecycleOwner = TestLifecycleOwner(
62+
initialState = Lifecycle.State.RESUMED,
63+
coroutineDispatcher = Dispatchers.Swing
64+
)
5965
composeTestRule.setContent {
6066
CompositionLocalProvider(LocalLifecycleOwner provides lifecycleOwner) {
6167
App()
6268
}
6369
}
70+
composeTestRule.waitForIdle()
6471
}
6572

6673
@After
@@ -82,11 +89,9 @@ class DesktopUiTests : AbstractUiTests() {
8289
@Test
8390
override fun prepopulateDatabase() = super.prepopulateDatabase()
8491

85-
@Ignore("Desktop app doesn't support encryption yet")
8692
@Test
8793
override fun flowAfterCryptTest() = super.flowAfterCryptTest()
8894

89-
@Ignore("Desktop app doesn't support encryption yet")
9095
@Test
9196
override fun settingPasswordTest() = super.settingPasswordTest()
9297

app/iosApp/iosApp.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@
475475
"$(inherited)",
476476
"@executable_path/Frameworks",
477477
);
478-
MARKETING_VERSION = 8.4.7;
478+
MARKETING_VERSION = 8.4.8;
479479
OTHER_LDFLAGS = (
480480
"$(inherited)",
481481
"-ObjC",
@@ -517,7 +517,7 @@
517517
"$(inherited)",
518518
"@executable_path/Frameworks",
519519
);
520-
MARKETING_VERSION = 8.4.7;
520+
MARKETING_VERSION = 8.4.8;
521521
OTHER_LDFLAGS = (
522522
"$(inherited)",
523523
"-ObjC",
Binary file not shown.

app/iosApp/iosApp/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<key>CFBundlePackageType</key>
2323
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
2424
<key>CFBundleShortVersionString</key>
25-
<string>8.4.7</string>
25+
<string>8.4.8</string>
2626
<key>CFBundleVersion</key>
2727
<string>19</string>
2828
<key>LSRequiresIPhoneOS</key>

0 commit comments

Comments
 (0)