Skip to content

refactor(db): migrate realm-java → Room (fixes 16 KB page size) + scroll-to-top FAB - #2

Merged
ygpark80 merged 2 commits into
masterfrom
feat/room-migration
Jul 24, 2026
Merged

ygpark80 merged 2 commits into
masterfrom
feat/room-migration

Conversation

@ygpark80

Copy link
Copy Markdown

Why

realm-java (io.realm) is archived and its librealm-jni.so has 4 KB-aligned LOAD segments → triggers the Android 15+ 16 KB page-size incompatibility warning and would fail to load on 16 KB-enforcing devices. This migrates the whole persistence layer to Room + framework SQLite, which ships no native library at all — eliminating the 16 KB issue at the source (the new debug APK contains zero .so files).

What changed

  • Domain models (13) → plain Kotlin (no RealmObject/RealmList/annotations).
  • New Room layer in :data (data/db): 15 @Entity + 2 junction tables, 5 @Relation POJOs, TypeConverters, Entity↔model mappers, 8 DAOs, QuikDatabase.
  • All 9 repositories rewritten onto DAOs. RxJava2 kept via room-rxjava2 (DAO Flowable → Observable); observed RealmResults → Observable<List<X>>, one-shot reads → List<X>; in-place Realm mutations → DAO @Query UPDATEs. EmojiReactionRepository dropped its external realm param.
  • Presentation (~25 files) — ViewModels/Presenters/adapters/State classes adapted to the new reactive signatures. QkRealmAdapter base is now a plain List-backed RecyclerView.Adapter.
  • Scroll-to-top FAB (separate commit): the compose "+" button morphs to ↑ when the list is scrolled and returns to top on tap.
  • KeyManager, DI (QuikDatabase + DAO providers), QKApplication de-Realm'd. Removed realm gradle plugin/deps/classpath from all modules; deleted RealmExtensions, QkRealmMigration, LiveRealmData.

Data safety

The on-disk .realm file is not deleted. Room starts empty, so lastSync == 0 on first launch triggers a full re-sync that rebuilds messages/conversations/contacts from the OS Telephony provider (the source of truth).

⚠️ Follow-up (not in this PR): a one-time importer to restore Realm-only metadata (conversation archive/pin/block/name/draft, blocked numbers, content filters, scheduled messages) before the real-app cutover.

Verification

End-to-end on a physical device (debug build):

  • App launches, no crash (DI graph + Room init OK).
  • Fresh Room DB re-syncs and renders the full conversation list (names, avatars, snippets, unread, group tags).
  • Opening a thread renders full message history (Room read path + relations).
  • Scroll reveals ↑ FAB, tap returns to top.
  • New debug APK: 0 .so files — librealm-jni.so gone.
  • Room kapt validated all 15 entities and ~80 DAO queries; assembleDebug = BUILD SUCCESSFUL.

🤖 Generated with Claude Code

ygpark80 and others added 2 commits July 24, 2026 15:52
The compose "+" FAB in the conversation list doubles as a scroll-to-top
button: once the list is scrolled down it morphs into an up arrow and
tapping it returns to the top instead of starting a new conversation.
Icon re-syncs on Inbox<->Archived page changes.

Verified on device (R5KL301NYYZ): scrolling reveals ↑, tap returns to top.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…B page size)

realm-java (io.realm) is archived and ships librealm-jni.so with 4 KB-aligned
LOAD segments, which triggers the Android 15+ 16 KB page-size incompatibility
warning and would fail to load on 16 KB-enforcing devices. Migrate the entire
persistence layer to Room (androidx.room) + framework SQLite, which ships no
native library at all — eliminating the 16 KB issue at the source.

What changed:
- Domain models (13) stripped of RealmObject/RealmList/annotations -> plain Kotlin.
- New Room layer in :data (data/db): 15 @entity + 2 junction tables, 5 @relation
  POJOs, TypeConverters, Entity<->model Mappers, 8 DAOs, QuikDatabase.
- All 9 repositories rewritten off Realm onto DAOs. RxJava2 kept via room-rxjava2
  (DAO Flowable -> Observable); live RealmResults -> Observable<List<X>> (observed)
  or List<X> (one-shot). In-place Realm mutations -> DAO @query UPDATEs.
  EmojiReactionRepository dropped its external `realm` param.
- Presentation (~25 files: ViewModels/Presenters/adapters/States) adapted to the
  new reactive signatures; QkRealmAdapter base is now a plain List-backed
  RecyclerView.Adapter; scroll-to-top FAB preserved.
- KeyManager, DI (QuikDatabase + DAO providers), QKApplication de-Realm'd.
- Removed realm gradle plugin/deps/classpath from all modules; deleted
  RealmExtensions, QkRealmMigration, LiveRealmData.

Data safety: the on-disk .realm file is NOT deleted. Room starts empty, so on
first launch lastSync==0 triggers a full re-sync that rebuilds messages/
conversations/contacts from the OS Telephony provider (source of truth). A
one-time importer to restore Realm-only metadata (archive/pin/block/name/draft,
blocked numbers, content filters, scheduled messages) is a planned follow-up.

Verified end-to-end on device (R5KL301NYYZ, debug build): app launches with no
crash, fresh Room DB re-syncs and renders the full conversation list, opening a
thread renders message history, and the new debug APK contains ZERO .so files
(librealm-jni.so gone). Room's kapt validated all 15 entities and ~80 DAO queries.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@codeant-ai

codeant-ai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Skipping CodeAnt AI review — this PR changes more than 100 files, which usually means a migration, codemod, or vendored drop. Line-level review on diffs this large produces duplicate findings on the same rewrite pattern and drowns out anything that actually matters.

If you still want a review, comment @codeant-ai : review. For better signal, consider splitting the PR into smaller chunks.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 101 files, which is 1 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b0562f18-66f8-43e6-9174-44288ee25a56

📥 Commits

Reviewing files that changed from the base of the PR and between ca1c777 and 16e19ae.

📒 Files selected for processing (104)
  • build.gradle
  • data/build.gradle
  • data/src/main/java/com/moez/QKSMS/data/db/Converters.kt
  • data/src/main/java/com/moez/QKSMS/data/db/Mappers.kt
  • data/src/main/java/com/moez/QKSMS/data/db/QuikDatabase.kt
  • data/src/main/java/com/moez/QKSMS/data/db/dao/BlockedNumberDao.kt
  • data/src/main/java/com/moez/QKSMS/data/db/dao/ContactDao.kt
  • data/src/main/java/com/moez/QKSMS/data/db/dao/ConversationDao.kt
  • data/src/main/java/com/moez/QKSMS/data/db/dao/EmojiReactionDao.kt
  • data/src/main/java/com/moez/QKSMS/data/db/dao/MessageContentFilterDao.kt
  • data/src/main/java/com/moez/QKSMS/data/db/dao/MessageDao.kt
  • data/src/main/java/com/moez/QKSMS/data/db/dao/ScheduledMessageDao.kt
  • data/src/main/java/com/moez/QKSMS/data/db/dao/SyncDao.kt
  • data/src/main/java/com/moez/QKSMS/data/db/entity/BlockedNumberEntity.kt
  • data/src/main/java/com/moez/QKSMS/data/db/entity/ContactEntity.kt
  • data/src/main/java/com/moez/QKSMS/data/db/entity/ContactGroupContactEntity.kt
  • data/src/main/java/com/moez/QKSMS/data/db/entity/ContactGroupEntity.kt
  • data/src/main/java/com/moez/QKSMS/data/db/entity/ConversationEntity.kt
  • data/src/main/java/com/moez/QKSMS/data/db/entity/ConversationRecipientEntity.kt
  • data/src/main/java/com/moez/QKSMS/data/db/entity/EmojiReactionEntity.kt
  • data/src/main/java/com/moez/QKSMS/data/db/entity/EmojiSyncNeededEntity.kt
  • data/src/main/java/com/moez/QKSMS/data/db/entity/MessageContentFilterEntity.kt
  • data/src/main/java/com/moez/QKSMS/data/db/entity/MessageEntity.kt
  • data/src/main/java/com/moez/QKSMS/data/db/entity/MmsPartEntity.kt
  • data/src/main/java/com/moez/QKSMS/data/db/entity/PhoneNumberEntity.kt
  • data/src/main/java/com/moez/QKSMS/data/db/entity/RecipientEntity.kt
  • data/src/main/java/com/moez/QKSMS/data/db/entity/ScheduledMessageEntity.kt
  • data/src/main/java/com/moez/QKSMS/data/db/entity/SyncLogEntity.kt
  • data/src/main/java/com/moez/QKSMS/data/db/relation/ContactGroupWithContacts.kt
  • data/src/main/java/com/moez/QKSMS/data/db/relation/ContactWithNumbers.kt
  • data/src/main/java/com/moez/QKSMS/data/db/relation/ConversationWithRelations.kt
  • data/src/main/java/com/moez/QKSMS/data/db/relation/MessageWithRelations.kt
  • data/src/main/java/com/moez/QKSMS/data/db/relation/RecipientWithContact.kt
  • data/src/main/java/com/moez/QKSMS/extensions/RealmExtensions.kt
  • data/src/main/java/com/moez/QKSMS/manager/KeyManagerImpl.kt
  • data/src/main/java/com/moez/QKSMS/migration/QkRealmMigration.kt
  • data/src/main/java/com/moez/QKSMS/receiver/RemoteMessagingReceiver.kt
  • data/src/main/java/com/moez/QKSMS/repository/BackupRepositoryImpl.kt
  • data/src/main/java/com/moez/QKSMS/repository/BlockingRepositoryImpl.kt
  • data/src/main/java/com/moez/QKSMS/repository/ContactRepositoryImpl.kt
  • data/src/main/java/com/moez/QKSMS/repository/ConversationRepositoryImpl.kt
  • data/src/main/java/com/moez/QKSMS/repository/EmojiReactionRepositoryImpl.kt
  • data/src/main/java/com/moez/QKSMS/repository/MessageContentFilterRepositoryImpl.kt
  • data/src/main/java/com/moez/QKSMS/repository/MessageRepositoryImpl.kt
  • data/src/main/java/com/moez/QKSMS/repository/ScheduledMessageRepositoryImpl.kt
  • data/src/main/java/com/moez/QKSMS/repository/SyncRepositoryImpl.kt
  • domain/build.gradle
  • domain/src/main/java/com/moez/QKSMS/interactor/AddScheduledMessage.kt
  • domain/src/main/java/com/moez/QKSMS/interactor/SendScheduledMessage.kt
  • domain/src/main/java/com/moez/QKSMS/interactor/UpdateScheduledMessageAlarms.kt
  • domain/src/main/java/com/moez/QKSMS/model/BlockedNumber.kt
  • domain/src/main/java/com/moez/QKSMS/model/Contact.kt
  • domain/src/main/java/com/moez/QKSMS/model/ContactGroup.kt
  • domain/src/main/java/com/moez/QKSMS/model/Conversation.kt
  • domain/src/main/java/com/moez/QKSMS/model/EmojiReaction.kt
  • domain/src/main/java/com/moez/QKSMS/model/EmojiSyncNeeded.kt
  • domain/src/main/java/com/moez/QKSMS/model/Message.kt
  • domain/src/main/java/com/moez/QKSMS/model/MessageContentFilter.kt
  • domain/src/main/java/com/moez/QKSMS/model/MmsPart.kt
  • domain/src/main/java/com/moez/QKSMS/model/PhoneNumber.kt
  • domain/src/main/java/com/moez/QKSMS/model/Recipient.kt
  • domain/src/main/java/com/moez/QKSMS/model/ScheduledMessage.kt
  • domain/src/main/java/com/moez/QKSMS/model/SyncLog.kt
  • domain/src/main/java/com/moez/QKSMS/repository/BlockingRepository.kt
  • domain/src/main/java/com/moez/QKSMS/repository/ContactRepository.kt
  • domain/src/main/java/com/moez/QKSMS/repository/ConversationRepository.kt
  • domain/src/main/java/com/moez/QKSMS/repository/EmojiReactionRepository.kt
  • domain/src/main/java/com/moez/QKSMS/repository/MessageContentFilterRepository.kt
  • domain/src/main/java/com/moez/QKSMS/repository/MessageRepository.kt
  • domain/src/main/java/com/moez/QKSMS/repository/ScheduledMessageRepository.kt
  • presentation/build.gradle
  • presentation/src/main/java/com/moez/QKSMS/adb/AdbMessagesProvider.kt
  • presentation/src/main/java/com/moez/QKSMS/common/QKApplication.kt
  • presentation/src/main/java/com/moez/QKSMS/common/base/QkRealmAdapter.kt
  • presentation/src/main/java/com/moez/QKSMS/common/base/QkThemedActivity.kt
  • presentation/src/main/java/com/moez/QKSMS/common/util/LiveRealmData.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/blocking/filters/MessageContentFiltersPresenter.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/blocking/filters/MessageContentFiltersState.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/blocking/messages/BlockedMessagesPresenter.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/blocking/messages/BlockedMessagesState.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/blocking/numbers/BlockedNumbersPresenter.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/blocking/numbers/BlockedNumbersState.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeState.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeViewModel.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/compose/MessagesAdapter.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/compose/editing/ComposeItem.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/contacts/ContactsViewModel.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/conversationinfo/ConversationInfoItem.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/conversationinfo/ConversationInfoPresenter.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/gallery/GalleryActivity.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/gallery/GalleryState.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/gallery/GalleryViewModel.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/main/MainActivity.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/main/MainState.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/main/MainViewModel.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/qkreply/QkReplyActivity.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/qkreply/QkReplyState.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/qkreply/QkReplyViewModel.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/scheduled/ScheduledMessageAdapter.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/scheduled/ScheduledState.kt
  • presentation/src/main/java/com/moez/QKSMS/feature/scheduled/ScheduledViewModel.kt
  • presentation/src/main/java/com/moez/QKSMS/injection/AppModule.kt
  • presentation/src/main/res/values/strings.xml

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/room-migration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ygpark80
ygpark80 merged commit 883c4fd into master Jul 24, 2026
1 check passed
@ygpark80
ygpark80 deleted the feat/room-migration branch October 8, 2026 08:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant