Skip to content

Commit 5cd9cce

Browse files
authored
refactor: room integration POC (#134)
* docs: room integration * ci: POC that builds on linux * fix: back to ksp * test: closer to passing * ci: .gitignore update * test: a version of the poc tests that pass * fix: code cleanup and docs about how we got it working * fix: rollback bidirectional sync changes that aren't ready to go yet just did them to verify crsqlite was working * docs: plan update * docs: update * fix: dramatically simplify with RequerySQLiteOpenHelperFactory * fix: even simpler * fix: scripts/build_crsqlite_arm64 * ci: hopefully fix report retrival contention refs #136
1 parent 1416983 commit 5cd9cce

19 files changed

Lines changed: 1532 additions & 73 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ out/
161161

162162
# Gradle files
163163
.gradle/
164+
android/.kotlin/
164165
build/
165166
!docs/build/
166167

android/app/build.gradle

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ plugins {
44
id 'kotlinx-serialization'
55
id 'jacoco'
66
id 'com.facebook.react'
7+
id 'com.google.devtools.ksp' // Using KSP for Room (avoids SQLite JDBC issue on Windows)
78
}
89

910
//
@@ -60,6 +61,9 @@ android {
6061
force 'org.objenesis:objenesis:3.3'
6162
force 'net.bytebuddy:byte-buddy:1.14.11'
6263
force 'net.bytebuddy:byte-buddy-agent:1.14.11'
64+
// Force sqlite version for Room 2.8.4 compatibility (overrides consistent resolution constraint)
65+
force 'androidx.sqlite:sqlite:2.6.2'
66+
force 'androidx.sqlite:sqlite-framework:2.6.2'
6367
}
6468
}
6569

@@ -72,6 +76,9 @@ android {
7276
excludes += ['META-INF/AL2.0']
7377
excludes += ['META-INF/LGPL2.1']
7478
}
79+
jniLibs {
80+
useLegacyPackaging = true
81+
}
7582
pickFirst '**/*.so'
7683
}
7784

@@ -226,6 +233,9 @@ android {
226233
// React Native configuration for RN 0.76+
227234
// NOTE: All react {} configuration is consolidated at the bottom of this file
228235

236+
// KSP avoids the SQLite JDBC native library extraction issue that KAPT has on Windows
237+
// Room arguments can be passed via: ksp { arg("room.schemaLocation", "...") } if needed
238+
229239
dependencies {
230240
implementation fileTree(dir: 'libs', include: ['*.jar'])
231241

@@ -333,6 +343,19 @@ dependencies {
333343

334344
// Jacoco
335345
androidTestImplementation 'org.jacoco:org.jacoco.core:0.8.12'
346+
347+
// Room POC (test only - verifying cr-sqlite compatibility)
348+
// See docs/dev_todo/database_modernization_plan.md
349+
// Note: Room 2.7.0+ required for Kotlin 2.0.x metadata compatibility
350+
// https://developer.android.com/jetpack/androidx/releases/room
351+
def roomVersion = "2.8.4"
352+
androidTestImplementation "androidx.room:room-runtime:$roomVersion"
353+
androidTestImplementation "androidx.room:room-ktx:$roomVersion"
354+
androidTestImplementation "androidx.room:room-testing:$roomVersion"
355+
kspAndroidTest "androidx.room:room-compiler:$roomVersion"
356+
// Room 2.8+ uses the new androidx.sqlite library
357+
androidTestImplementation "androidx.sqlite:sqlite:2.6.2"
358+
androidTestImplementation "androidx.sqlite:sqlite-framework:2.6.2"
336359
}
337360

338361
// https://reactnative.dev/docs/react-native-gradle-plugin
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//
2+
// Calendar Notifications Plus
3+
// Copyright (C) 2025 William Harris (wharris+cnplus@upscalews.com)
4+
//
5+
// This program is free software; you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation; either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with this program; if not, write to the Free Software Foundation,
17+
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18+
//
19+
20+
package com.github.quarck.calnotify.database.poc
21+
22+
import androidx.sqlite.db.SupportSQLiteOpenHelper
23+
import com.github.quarck.calnotify.logs.DevLog
24+
import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory
25+
import io.requery.android.database.sqlite.SQLiteCustomExtension
26+
import io.requery.android.database.sqlite.SQLiteDatabaseConfiguration
27+
28+
/**
29+
* SupportSQLiteOpenHelper.Factory that uses requery with cr-sqlite extension.
30+
*
31+
* Uses RequerySQLiteOpenHelperFactory's ConfigurationOptions to add cr-sqlite,
32+
* and wraps the helper to ensure crsql_finalize() is called on close.
33+
*
34+
* See: https://github.com/requery/sqlite-android#support-library-compatibility
35+
* See: docs/testing/crsqlite_room_testing.md
36+
*/
37+
class CrSqliteRoomFactory : SupportSQLiteOpenHelper.Factory {
38+
39+
companion object {
40+
private const val LOG_TAG = "CrSqliteRoomFactory"
41+
}
42+
43+
// ConfigurationOptions that adds cr-sqlite extension
44+
private val crSqliteOptions = object : RequerySQLiteOpenHelperFactory.ConfigurationOptions {
45+
override fun apply(config: SQLiteDatabaseConfiguration): SQLiteDatabaseConfiguration {
46+
config.customExtensions.add(
47+
SQLiteCustomExtension("crsqlite_requery", "sqlite3_crsqlite_init")
48+
)
49+
DevLog.info(LOG_TAG, "Added cr-sqlite extension to configuration")
50+
return config
51+
}
52+
}
53+
54+
// Requery's factory with our cr-sqlite configuration
55+
private val requeryFactory = RequerySQLiteOpenHelperFactory(listOf(crSqliteOptions))
56+
57+
override fun create(configuration: SupportSQLiteOpenHelper.Configuration): SupportSQLiteOpenHelper {
58+
val requeryHelper = requeryFactory.create(configuration)
59+
return CrSqliteFinalizeWrapper(requeryHelper)
60+
}
61+
}
62+
63+
/**
64+
* Wrapper that ensures crsql_finalize() is called before close.
65+
* Uses Kotlin delegation - only overrides close().
66+
*/
67+
class CrSqliteFinalizeWrapper(
68+
private val delegate: SupportSQLiteOpenHelper
69+
) : SupportSQLiteOpenHelper by delegate {
70+
71+
override fun close() {
72+
try {
73+
writableDatabase.query("SELECT crsql_finalize()").use { it.moveToFirst() }
74+
} catch (e: Exception) {
75+
DevLog.error("CrSqliteFinalizeWrapper", "Error calling crsql_finalize: ${e.message}")
76+
}
77+
delegate.close()
78+
}
79+
}

0 commit comments

Comments
 (0)