Skip to content

[incoming] add module for tracking incoming messages#334

Merged
capcom6 merged 1 commit intomasterfrom
codex/plan-incoming-message-logs-module
Apr 1, 2026
Merged

[incoming] add module for tracking incoming messages#334
capcom6 merged 1 commit intomasterfrom
codex/plan-incoming-message-logs-module

Conversation

@capcom6
Copy link
Copy Markdown
Owner

@capcom6 capcom6 commented Mar 26, 2026

Motivation

  • Provide parity for incoming messages so incoming SMS/MMS/Data SMS are persisted, observable, and viewable like outgoing messages.
  • Expose incoming totals and a simple list UI to aid debugging and webhook delivery visibility.
  • Integrate persistence into the receiver flow to guarantee messages are stored before webhook dispatch.

Description

  • Introduces a new modules/incoming feature with IncomingMessage entity, IncomingMessagesDao, IncomingMessagesRepository, IncomingMessagesService, and IncomingMessagesListViewModel to persist and expose incoming records and aggregated totals.
  • Adds UI: IncomingMessagesListFragment, IncomingMessagesAdapter, layout resources (fragment_incoming_messages_list.xml, item_incoming_message.xml), and updates HolderFragment and fragment_holder.xml to allow switching between outgoing and incoming lists via two buttons.
  • Wires the module into DI by adding incomingModule to App.kt and registering incomingMessagesDao in AppDatabase and data module, and bumps Room DB version to 19 with an AutoMigration entry.
  • Integrates persistence into the receiver pipeline by injecting IncomingMessagesService into ReceiverService and calling incomingMessagesService.save(...) when messages are processed, plus simple preview/id generation logic for incoming items.

Testing

  • Built the app with ./gradlew assembleDebug to verify compilation and resource linking, which completed successfully.
  • Ran the existing unit test suite with ./gradlew test to ensure no regressions in current tests, and they passed.
  • No new automated tests were added for the incoming module in this change.

Codex Task

Summary by CodeRabbit

  • New Features

    • Incoming messages viewer: tabbed Incoming/Outgoing UI, per-type counters (SMS, Data SMS, MMS), list items with sender, type, preview, timestamp, and incremental loading.
  • Bug Fixes

    • Incoming message persistence added; failures are logged and do not block webhook delivery.
  • Chores

    • Database schema version bumped to store incoming messages and support aggregated stats.
  • Documentation

    • README and docs updated to announce the incoming messages viewer.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 26, 2026

Walkthrough

Registers incoming message storage and UI: adds Room entity/DAO/repository/service/ViewModel/Fragment/Adapter, wires an incomingModule into Koin startup, updates ReceiverService to persist incoming messages (errors logged), and bumps Room schema to v19 with auto-migration and schema JSON.

Changes

Cohort / File(s) Summary
App & DI
app/src/main/java/me/capcom/smsgateway/App.kt, app/src/main/java/me/capcom/smsgateway/modules/incoming/Module.kt
Registers incomingModule at startup; adds Koin bindings for IncomingMessagesRepository, IncomingMessagesService, IncomingMessagesListViewModel and MODULE_NAME.
Database schema & wiring
app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt, app/src/main/java/me/capcom/smsgateway/data/Module.kt, app/schemas/me.capcom.smsgateway.data.AppDatabase/19.json
Adds IncomingMessage entity and incomingMessagesDao(); bumps Room schema 18→19 with AutoMigration(from = 18, to = 19); registers DAO in DI; includes generated v19 schema JSON.
Persistence model & DAO
app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessage.kt, app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessageTotals.kt, app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessagesDao.kt
Introduces IncomingMessageType enum, IncomingMessage Room entity with indices, IncomingMessageTotals DTO, and IncomingMessagesDao with insert, selectLast(limit), and aggregated getStats().
Repository & Service
app/src/main/java/me/capcom/smsgateway/modules/incoming/repositories/IncomingMessagesRepository.kt, app/src/main/java/me/capcom/smsgateway/modules/incoming/IncomingMessagesService.kt
Adds repository applying distinctUntilChanged() and IncomingMessagesService.save(...) which maps InboxMessage subtypes to types, builds deterministic IDs/previews, and inserts records.
Receiver integration
app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt
Injects IncomingMessagesService and calls save(...) inside process(...); persistence exceptions are caught and logged via logsService.insert(...) while processing continues.
ViewModel, Fragment, Adapter
app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt, app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt, app/src/main/java/me/capcom/smsgateway/ui/adapters/IncomingMessagesAdapter.kt
Adds paginated IncomingMessagesListViewModel, IncomingMessagesListFragment with stats and scroll-triggered pagination, and IncomingMessagesAdapter for rendering incoming items.
UI layout & holder changes
app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt, app/src/main/res/layout/fragment_holder.xml, app/src/main/res/layout/fragment_incoming_messages_list.xml, app/src/main/res/layout/item_incoming_message.xml
Refactors HolderFragment to view binding and tab toggle outgoing/incoming; adds header buttons and new incoming list/item layouts; swaps root layout structure to include header + content container.
Strings & locales
app/src/main/res/values/strings.xml, app/src/main/res/values-ru/strings.xml, app/src/main/res/values-zh/strings.xml
Adds incoming/outgoing labels, status and per-type counter formats; updates translations and marks some host strings non-translatable.
Docs & README
user-docs, README.md
Adds pointer file to docs-web and documents the new incoming messages viewer feature in README.

Sequence Diagram

sequenceDiagram
    participant System as rgba(45,115,230,0.5)
    participant Receiver as rgba(52,199,89,0.5)
    participant Service as rgba(255,159,28,0.5)
    participant Repo as rgba(153,102,255,0.5)
    participant DAO as rgba(100,181,246,0.5)
    participant DB as rgba(76,175,80,0.5)
    participant Logs as rgba(244,67,54,0.5)

    System->>Receiver: deliver InboxMessage
    Receiver->>Service: save(message, sender, recipient, simNumber)
    Service->>Service: buildId(message)\ncompute contentPreview & type
    Service->>Repo: insert(IncomingMessage)
    Repo->>DAO: insert(IncomingMessage)
    DAO->>DB: INSERT incoming_messages
    DB-->>DAO: OK
    DAO-->>Repo: return
    Repo-->>Service: return

    alt persistence succeeds
        Service-->>Receiver: return
    else persistence throws
        Service->>Logs: insert(ERROR log with message & stack trace)
        Logs-->>Service: return
        Service-->>Receiver: return
    end

    Receiver->>System: continue webhook handling
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title '[incoming] add module for tracking incoming messages' accurately reflects the main objective: introducing a new incoming messages module with persistence, service layer, and UI components.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/plan-incoming-message-logs-module

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (4)
app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt (1)

49-65: Consider using visual selection indicators instead of disabling buttons.

Using isEnabled = false to indicate the active tab causes the selected button to appear grayed out, which typically communicates "unavailable" rather than "selected." Consider using isSelected = true with a custom selector drawable, or switching to MaterialButtonToggleGroup for standard toggle behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt` around lines 49
- 65, The current selectOutgoing and selectIncoming use isEnabled to indicate
the active tab which grays out the button; instead set visual state via
isSelected (e.g., binding.buttonOutgoing.isSelected = true / isSelected = false)
and remove the isEnabled toggles, and update the button backgrounds to use a
selector drawable (or migrate the two buttons into a MaterialButtonToggleGroup)
so selection is shown visually while keeping the fragment swaps in
selectOutgoing and selectIncoming (referencing the methods
selectOutgoing/selectIncoming and the views binding.buttonOutgoing and
binding.buttonIncoming).
app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt (1)

65-73: Minor: scroll listener could trigger unnecessary calls on empty list.

When the RecyclerView is empty, findLastVisibleItemPosition() returns -1 and adapter.itemCount - 1 is also -1, so the condition matches. While loadMore(0) safely returns early in the ViewModel, this creates unnecessary calls.

Consider adding a guard:

♻️ Optional improvement
     private val scrollListener = object : RecyclerView.OnScrollListener() {
         override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
             super.onScrolled(recyclerView, dx, dy)
+            if (adapter.itemCount == 0) return
             val manager = recyclerView.layoutManager as? LinearLayoutManager
             manager?.findLastVisibleItemPosition()?.let {
                 if (it == adapter.itemCount - 1) viewModel.loadMore(adapter.itemCount)
             }
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt`
around lines 65 - 73, The scroll listener (scrollListener -> onScrolled) can
call viewModel.loadMore unnecessarily when the list is empty because
findLastVisibleItemPosition() and adapter.itemCount - 1 both equal -1; update
the onScrolled guard to first check that adapter.itemCount > 0 and that the
found lastVisible (from manager?.findLastVisibleItemPosition()) is >= 0 before
comparing it to adapter.itemCount - 1, and only call
viewModel.loadMore(adapter.itemCount) when both conditions are met.
INCOMING_MESSAGES_PLAN.md (1)

134-140: Tests described in plan but not implemented.

The PR summary indicates "No new automated tests added for the incoming module," yet this section outlines comprehensive test coverage expectations. Consider adding at least critical-path tests (DAO unit tests for stats/pagination, receiver integration test) or creating a follow-up issue to track test debt.

Would you like me to open an issue to track the missing test coverage for the incoming module?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@INCOMING_MESSAGES_PLAN.md` around lines 134 - 140, The plan lists
comprehensive tests that were not implemented; either add the critical-path
tests or create a follow-up issue tracking the missing coverage. Specifically,
implement unit tests for the DAO (e.g., IncomingDao.statsQuery and pagination
behavior), repository tests for mapping and totals (IncomingRepository.map* /
getTotals), an integration test for the receiver to assert persistence before
webhook dispatch (IncomingReceiver.handleWebhook), a Fragment/ViewModel test for
stats rendering and load-more (IncomingFragment / IncomingViewModel), and a
migration test for old DB upgrade (MigrationUpgradeOldDb); if you won't add them
now, create a tracked issue outlining these exact tests with priorities and
acceptance criteria.
app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt (1)

24-31: Redundant loadMore() call in init block.

The loadMore() call on line 30 is dead code. Since limit is initialized with chunkSize (50), the switchMap on line 25 already triggers repository.selectLast(50) immediately. When loadMore() is called with default index=0, the condition currentLimit >= index + chunkSize evaluates to 50 >= 50, returning early without doing anything.

Remove the redundant call:

♻️ Remove dead code
     init {
         _messages.addSource(limit.switchMap { repository.selectLast(it) }) {
             _messages.value = it
             hasMore = it.size >= (limit.value ?: chunkSize)
             isLoading = false
         }
-        loadMore()
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt`
around lines 24 - 31, The init block currently calls loadMore() redundantly
because limit is initialized to chunkSize so the switchMap on limit already
triggers repository.selectLast(chunkSize); remove the dead call to loadMore()
from the init block (the block that sets up _messages.addSource(limit.switchMap
{ repository.selectLast(it) }) and the subsequent hasMore/isLoading updates) so
initialization relies on the limit switchMap; leave the other logic (hasMore,
isLoading updates and loadMore function) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessagesDao.kt`:
- Around line 11-12: The synchronous DAO insert must be converted to a
coroutine-backed call: change IncomingMessagesDao.insert(message:
IncomingMessage) to a suspend fun insert(message: IncomingMessage), then update
IncomingMessagesRepository.insert, IncomingMessagesService.save and the call
site in ReceiverService.process (or the BroadcastReceiver entry points
MessagesReceiver.onReceive / MmsReceiver.onReceive) to invoke the suspend insert
from a background coroutine (e.g., launch or withContext(Dispatchers.IO)) so the
database write never runs on the main thread; ensure callers propagate suspend
or wrap repository calls in a CoroutineScope tied to a lifecycle or WorkManager
as appropriate to avoid blocking the UI thread.

In
`@app/src/main/java/me/capcom/smsgateway/ui/adapters/IncomingMessagesAdapter.kt`:
- Line 35: Replace the raw enum display (binding.textViewType.text =
item.type.name) with a localized string lookup: add string resources
incoming_type_sms, incoming_type_data_sms, incoming_type_mms and implement a
mapping from the enum (item.type) to the appropriate resource (e.g., via a when
over the enum in IncomingMessagesAdapter or an extension function on the enum)
and set binding.textViewType.text using context.getString(mappedResId) so UI
shows localized, user-friendly labels instead of internal constant names.

In `@app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt`:
- Around line 44-46: The fragment's button state isn't restored after
configuration change; update HolderFragment to persist the selected tab and
reapply it when views are recreated: add a constant key (e.g., "SELECTED_TAB")
and save the current selection in onSaveInstanceState (store which of
selectOutgoing()/selectIncoming() is active), then in onViewCreated (or where
savedInstanceState is handled) check savedInstanceState and call
selectOutgoing() or selectIncoming() accordingly instead of skipping both when
savedInstanceState != null so the buttons' isEnabled states match the restored
child fragment.

In `@INCOMING_MESSAGES_PLAN.md`:
- Line 60: The plan and code disagree: the DAO exposes selectLast(limit) but the
plan lists selectLast(limit, offset). Update the plan to match the
implementation by replacing the signature to `selectLast(limit)` (or explicitly
document the load-more pattern used by the ViewModel that increases limit rather
than using offset-based pagination) and mention the ViewModel's load-more
behavior so readers understand why offset isn't used; reference the DAO method
name selectLast and the ViewModel load-more pattern in the plan.
- Around line 86-93: The stats card described in the plan is missing the
"webhook failed count" in the implemented UI; update the
IncomingMessagesListFragment to surface that metric by ensuring the aggregator
that computes totals (e.g., the stats selector or the method that produces
total/SMS/MMS/DataSMS counts) also calculates webhookFailedCount and pass it
into the stats card props, then render a new stat entry labeled "Webhook failed"
alongside Total, SMS, MMS and Data SMS; alternatively, if this metric is out of
scope for phase 1, update the plan text to remove "Webhook failed count" instead
of changing code.

---

Nitpick comments:
In
`@app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt`:
- Around line 24-31: The init block currently calls loadMore() redundantly
because limit is initialized to chunkSize so the switchMap on limit already
triggers repository.selectLast(chunkSize); remove the dead call to loadMore()
from the init block (the block that sets up _messages.addSource(limit.switchMap
{ repository.selectLast(it) }) and the subsequent hasMore/isLoading updates) so
initialization relies on the limit switchMap; leave the other logic (hasMore,
isLoading updates and loadMore function) unchanged.

In `@app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt`:
- Around line 49-65: The current selectOutgoing and selectIncoming use isEnabled
to indicate the active tab which grays out the button; instead set visual state
via isSelected (e.g., binding.buttonOutgoing.isSelected = true / isSelected =
false) and remove the isEnabled toggles, and update the button backgrounds to
use a selector drawable (or migrate the two buttons into a
MaterialButtonToggleGroup) so selection is shown visually while keeping the
fragment swaps in selectOutgoing and selectIncoming (referencing the methods
selectOutgoing/selectIncoming and the views binding.buttonOutgoing and
binding.buttonIncoming).

In `@app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt`:
- Around line 65-73: The scroll listener (scrollListener -> onScrolled) can call
viewModel.loadMore unnecessarily when the list is empty because
findLastVisibleItemPosition() and adapter.itemCount - 1 both equal -1; update
the onScrolled guard to first check that adapter.itemCount > 0 and that the
found lastVisible (from manager?.findLastVisibleItemPosition()) is >= 0 before
comparing it to adapter.itemCount - 1, and only call
viewModel.loadMore(adapter.itemCount) when both conditions are met.

In `@INCOMING_MESSAGES_PLAN.md`:
- Around line 134-140: The plan lists comprehensive tests that were not
implemented; either add the critical-path tests or create a follow-up issue
tracking the missing coverage. Specifically, implement unit tests for the DAO
(e.g., IncomingDao.statsQuery and pagination behavior), repository tests for
mapping and totals (IncomingRepository.map* / getTotals), an integration test
for the receiver to assert persistence before webhook dispatch
(IncomingReceiver.handleWebhook), a Fragment/ViewModel test for stats rendering
and load-more (IncomingFragment / IncomingViewModel), and a migration test for
old DB upgrade (MigrationUpgradeOldDb); if you won't add them now, create a
tracked issue outlining these exact tests with priorities and acceptance
criteria.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5e645013-62f9-496c-97dd-ede05556fc67

📥 Commits

Reviewing files that changed from the base of the PR and between c1682a7 and 7e31f55.

📒 Files selected for processing (19)
  • INCOMING_MESSAGES_PLAN.md
  • app/src/main/java/me/capcom/smsgateway/App.kt
  • app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
  • app/src/main/java/me/capcom/smsgateway/data/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/IncomingMessagesService.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessage.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessageTotals.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessagesDao.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/repositories/IncomingMessagesRepository.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt
  • app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt
  • app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt
  • app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt
  • app/src/main/java/me/capcom/smsgateway/ui/adapters/IncomingMessagesAdapter.kt
  • app/src/main/res/layout/fragment_holder.xml
  • app/src/main/res/layout/fragment_incoming_messages_list.xml
  • app/src/main/res/layout/item_incoming_message.xml
  • app/src/main/res/values/strings.xml

Comment thread app/src/main/java/me/capcom/smsgateway/ui/adapters/IncomingMessagesAdapter.kt Outdated
Comment thread app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt
Comment thread INCOMING_MESSAGES_PLAN.md Outdated
Comment thread INCOMING_MESSAGES_PLAN.md Outdated
@capcom6 capcom6 force-pushed the codex/plan-incoming-message-logs-module branch from 7e31f55 to ba56f2e Compare March 26, 2026 07:23
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt (1)

74-77: Tab switches recreate fragments and lose RecyclerView scroll position.

Using replace(...) on lines 75 and 86 destroys and recreates the inactive tab's fragment. Since neither MessagesListFragment nor IncomingMessagesListFragment persist RecyclerView position, switching tabs resets the scroll state each time. Consider keeping both child fragments and using show/hide by tag instead to preserve instance state and scroll position.

♻️ Proposed refactor (preserve child fragment instances)
 class HolderFragment : Fragment() {
+    private companion object {
+        const val TAG_OUTGOING = "tab_outgoing"
+        const val TAG_INCOMING = "tab_incoming"
+    }
+
     private fun selectOutgoing() {
         isOutgoingSelected = true
-
-        binding.buttonOutgoing.isEnabled = false
-        binding.buttonIncoming.isEnabled = true
-
-        childFragmentManager.commit {
-            replace(R.id.rootLayout, MessagesListFragment.newInstance())
-        }
+        updateButtonStates()
+        val outgoing = childFragmentManager.findFragmentByTag(TAG_OUTGOING)
+            ?: MessagesListFragment.newInstance()
+        val incoming = childFragmentManager.findFragmentByTag(TAG_INCOMING)
+
+        childFragmentManager.commit {
+            if (!outgoing.isAdded) add(R.id.rootLayout, outgoing, TAG_OUTGOING)
+            incoming?.let { hide(it) }
+            show(outgoing)
+        }
     }
 
     private fun selectIncoming() {
         isOutgoingSelected = false
-
-        binding.buttonOutgoing.isEnabled = true
-        binding.buttonIncoming.isEnabled = false
-
-        childFragmentManager.commit {
-            replace(R.id.rootLayout, IncomingMessagesListFragment.newInstance())
-        }
+        updateButtonStates()
+        val incoming = childFragmentManager.findFragmentByTag(TAG_INCOMING)
+            ?: IncomingMessagesListFragment.newInstance()
+        val outgoing = childFragmentManager.findFragmentByTag(TAG_OUTGOING)
+
+        childFragmentManager.commit {
+            if (!incoming.isAdded) add(R.id.rootLayout, incoming, TAG_INCOMING)
+            outgoing?.let { hide(it) }
+            show(incoming)
+        }
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt` around lines 74
- 77, The current use of childFragmentManager.commit with
replace(R.id.rootLayout, MessagesListFragment.newInstance()) (and the analogous
replace for IncomingMessagesListFragment) recreates fragments and loses
RecyclerView scroll state; change the logic in HolderFragment to add both child
fragments once using add(..., tag) (use MessagesListFragment.newInstance() and
IncomingMessagesListFragment.newInstance()), then toggle visibility with
show(tag)/hide(tag) instead of replace, ensuring you reuse existing fragments by
finding them via childFragmentManager.findFragmentByTag(tag) so instances (and
RecyclerView position) are preserved across tab switches.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt`:
- Around line 74-77: The current use of childFragmentManager.commit with
replace(R.id.rootLayout, MessagesListFragment.newInstance()) (and the analogous
replace for IncomingMessagesListFragment) recreates fragments and loses
RecyclerView scroll state; change the logic in HolderFragment to add both child
fragments once using add(..., tag) (use MessagesListFragment.newInstance() and
IncomingMessagesListFragment.newInstance()), then toggle visibility with
show(tag)/hide(tag) instead of replace, ensuring you reuse existing fragments by
finding them via childFragmentManager.findFragmentByTag(tag) so instances (and
RecyclerView position) are preserved across tab switches.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2e06bcad-b814-4135-80f0-fbdd04398232

📥 Commits

Reviewing files that changed from the base of the PR and between ba56f2e and a0eb08a.

📒 Files selected for processing (3)
  • app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt
  • app/src/main/java/me/capcom/smsgateway/ui/adapters/IncomingMessagesAdapter.kt
  • app/src/main/res/values/strings.xml
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/res/values/strings.xml

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt (2)

43-65: Add a recreate/switch regression test for this flow.

This fragment now has real UI state, saved-state restore, and two child-fragment paths. A focused FragmentScenario test that selects incoming, recreates the fragment, and asserts the restored button/child state would make this much safer to evolve.

Also applies to: 72-99

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt` around lines 43
- 65, Add a FragmentScenario recreation test that exercises the
saved-state/child-fragment flow: use HolderFragment via FragmentScenario, call
the fragment methods (or drive UI) to selectIncoming (instead of default
selectOutgoing), call scenario.recreate() to simulate process death, then assert
isOutgoingSelected is false and that updateButtonStates reflects the Incoming
button selected and the Incoming child fragment is attached; reference
HolderFragment's onViewCreated, selectIncoming/selectOutgoing,
isOutgoingSelected, KEY_OUTGOING_SELECTED and updateButtonStates to locate the
state and verify the child fragment in the test.

67-69: Don’t use isEnabled as the selected-tab state.

These two Buttons in app/src/main/res/layout/fragment_holder.xml:15-36 are acting like tabs, but the active one is exposed as disabled instead of selected. That makes the current tab harder to understand for accessibility services and keyboard/DPAD users. Prefer isSelected/isActivated with stateful styling, or a real tab/toggle component, and keep both controls enabled.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt` around lines 67
- 69, The updateButtonStates function is using isEnabled to indicate the active
tab; change it to set the visual/selection state instead: keep both
binding.buttonOutgoing and binding.buttonIncoming enabled (isEnabled = true) and
set their isSelected (or isActivated) according to isOutgoingSelected (e.g.,
binding.buttonOutgoing.isSelected = isOutgoingSelected;
binding.buttonIncoming.isSelected = !isOutgoingSelected). Update any click
handlers to still toggle isOutgoingSelected and ensure your selector/stateful
styling in fragment_holder.xml responds to the selected/activated state for
proper keyboard/DPAD and accessibility behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt`:
- Around line 43-65: Add a FragmentScenario recreation test that exercises the
saved-state/child-fragment flow: use HolderFragment via FragmentScenario, call
the fragment methods (or drive UI) to selectIncoming (instead of default
selectOutgoing), call scenario.recreate() to simulate process death, then assert
isOutgoingSelected is false and that updateButtonStates reflects the Incoming
button selected and the Incoming child fragment is attached; reference
HolderFragment's onViewCreated, selectIncoming/selectOutgoing,
isOutgoingSelected, KEY_OUTGOING_SELECTED and updateButtonStates to locate the
state and verify the child fragment in the test.
- Around line 67-69: The updateButtonStates function is using isEnabled to
indicate the active tab; change it to set the visual/selection state instead:
keep both binding.buttonOutgoing and binding.buttonIncoming enabled (isEnabled =
true) and set their isSelected (or isActivated) according to isOutgoingSelected
(e.g., binding.buttonOutgoing.isSelected = isOutgoingSelected;
binding.buttonIncoming.isSelected = !isOutgoingSelected). Update any click
handlers to still toggle isOutgoingSelected and ensure your selector/stateful
styling in fragment_holder.xml responds to the selected/activated state for
proper keyboard/DPAD and accessibility behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f2aab096-0d80-43a6-96c4-509c46ac3dd4

📥 Commits

Reviewing files that changed from the base of the PR and between a0eb08a and 609ce6e.

📒 Files selected for processing (1)
  • app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt

@capcom6 capcom6 force-pushed the codex/plan-incoming-message-logs-module branch from 609ce6e to ea024cc Compare March 27, 2026 09:38
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt (1)

24-31: Redundant loadMore() call in init block.

The loadMore() call on line 30 is effectively a no-op. Since limit is initialized to chunkSize (50), when loadMore(0) executes, the condition currentLimit >= index + chunkSize evaluates to 50 >= 50, causing an early return. The initial data load is already triggered by the switchMap when it observes the initial limit value.

Proposed fix
     init {
         _messages.addSource(limit.switchMap { repository.selectLast(it) }) {
             _messages.value = it
             hasMore = it.size >= (limit.value ?: chunkSize)
             isLoading = false
         }
-        loadMore()
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt`
around lines 24 - 31, The init block calls loadMore() redundantly because
_messages.addSource already triggers a load for the initial limit; remove the
loadMore() invocation in the init block (the call to loadMore() after the
_messages.addSource(...) closure) so the initial dataset is loaded only via the
limit.switchMap observer; verify behavior using the limit, chunkSize, hasMore
and isLoading variables in IncomingMessagesListViewModel to ensure no other
logic depends on that explicit call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt`:
- Around line 24-31: The init block calls loadMore() redundantly because
_messages.addSource already triggers a load for the initial limit; remove the
loadMore() invocation in the init block (the call to loadMore() after the
_messages.addSource(...) closure) so the initial dataset is loaded only via the
limit.switchMap observer; verify behavior using the limit, chunkSize, hasMore
and isLoading variables in IncomingMessagesListViewModel to ensure no other
logic depends on that explicit call.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d21e4c46-fa68-4473-9db4-df8d595e4d80

📥 Commits

Reviewing files that changed from the base of the PR and between 609ce6e and ea024cc.

📒 Files selected for processing (19)
  • app/schemas/me.capcom.smsgateway.data.AppDatabase/19.json
  • app/src/main/java/me/capcom/smsgateway/App.kt
  • app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
  • app/src/main/java/me/capcom/smsgateway/data/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/IncomingMessagesService.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessage.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessageTotals.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessagesDao.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/repositories/IncomingMessagesRepository.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt
  • app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt
  • app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt
  • app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt
  • app/src/main/java/me/capcom/smsgateway/ui/adapters/IncomingMessagesAdapter.kt
  • app/src/main/res/layout/fragment_holder.xml
  • app/src/main/res/layout/fragment_incoming_messages_list.xml
  • app/src/main/res/layout/item_incoming_message.xml
  • app/src/main/res/values/strings.xml
✅ Files skipped from review due to trivial changes (7)
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessageTotals.kt
  • app/schemas/me.capcom.smsgateway.data.AppDatabase/19.json
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/repositories/IncomingMessagesRepository.kt
  • app/src/main/res/layout/item_incoming_message.xml
  • app/src/main/res/layout/fragment_incoming_messages_list.xml
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessagesDao.kt
🚧 Files skipped from review as they are similar to previous changes (10)
  • app/src/main/java/me/capcom/smsgateway/App.kt
  • app/src/main/java/me/capcom/smsgateway/data/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt
  • app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
  • app/src/main/res/values/strings.xml
  • app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt
  • app/src/main/java/me/capcom/smsgateway/ui/adapters/IncomingMessagesAdapter.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessage.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/IncomingMessagesService.kt
  • app/src/main/res/layout/fragment_holder.xml

@capcom6 capcom6 force-pushed the codex/plan-incoming-message-logs-module branch from 6232f6e to ea8a931 Compare March 29, 2026 07:46
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt`:
- Around line 66-74: The scroll listener currently uses
findLastVisibleItemPosition() and compares it to adapter.itemCount - 1, which
calls viewModel.loadMore(0) when both are -1; update the
RecyclerView.OnScrollListener (scrollListener) in onScrolled to first capture
val lastPos = manager?.findLastVisibleItemPosition() ?: -1 and only invoke
viewModel.loadMore(adapter.itemCount) when lastPos >= 0 and adapter.itemCount >
0 and lastPos == adapter.itemCount - 1 (i.e., guard against empty lists before
calling loadMore).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b33bd09b-015c-499e-8888-685a33786814

📥 Commits

Reviewing files that changed from the base of the PR and between 609ce6e and ea8a931.

📒 Files selected for processing (19)
  • app/schemas/me.capcom.smsgateway.data.AppDatabase/19.json
  • app/src/main/java/me/capcom/smsgateway/App.kt
  • app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
  • app/src/main/java/me/capcom/smsgateway/data/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/IncomingMessagesService.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessage.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessageTotals.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessagesDao.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/repositories/IncomingMessagesRepository.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt
  • app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt
  • app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt
  • app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt
  • app/src/main/java/me/capcom/smsgateway/ui/adapters/IncomingMessagesAdapter.kt
  • app/src/main/res/layout/fragment_holder.xml
  • app/src/main/res/layout/fragment_incoming_messages_list.xml
  • app/src/main/res/layout/item_incoming_message.xml
  • app/src/main/res/values/strings.xml
✅ Files skipped from review due to trivial changes (8)
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessageTotals.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/Module.kt
  • app/src/main/res/layout/item_incoming_message.xml
  • app/src/main/res/layout/fragment_incoming_messages_list.xml
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/repositories/IncomingMessagesRepository.kt
  • app/schemas/me.capcom.smsgateway.data.AppDatabase/19.json
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessage.kt
  • app/src/main/res/values/strings.xml
🚧 Files skipped from review as they are similar to previous changes (6)
  • app/src/main/java/me/capcom/smsgateway/data/Module.kt
  • app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
  • app/src/main/java/me/capcom/smsgateway/App.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessagesDao.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt
  • app/src/main/res/layout/fragment_holder.xml

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt (1)

36-40: Set the LinearLayoutManager explicitly in code.

Line 72 assumes a LinearLayoutManager, but this fragment never sets one directly. Making it explicit avoids silent pagination breakage if the XML manager changes.

Suggested change
         binding.recyclerView.adapter = adapter
+        binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
         binding.recyclerView.addOnScrollListener(scrollListener)
         binding.recyclerView.addItemDecoration(
             DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt`
around lines 36 - 40, The fragment attaches an adapter and a scrollListener to
binding.recyclerView but never sets a LayoutManager, yet scrollListener assumes
a LinearLayoutManager; explicitly set binding.recyclerView.layoutManager =
LinearLayoutManager(requireContext()) (or the desired orientation) before
assigning adapter/scrollListener to ensure pagination (scrollListener) works
even if the XML changes; reference binding.recyclerView, scrollListener, adapter
and use LinearLayoutManager in the fragment initialization block where the
RecyclerView is configured.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt`:
- Around line 67-75: The onScrolled override in IncomingMessagesListFragment
triggers viewModel.loadMore(adapter.itemCount) whenever the last item is
visible, even on upward or idle scrolls; update onScrolled to first check the
scroll delta (dy > 0) before computing lastVisiblePosition so loadMore only runs
on downward scrolls. Locate the onScrolled method (override fun
onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int)), add a guard using the
dy parameter, then proceed to get the LinearLayoutManager, call
findLastVisibleItemPosition(), and invoke viewModel.loadMore(adapter.itemCount)
only when dy > 0 and the last position equals adapter.itemCount - 1.

---

Nitpick comments:
In `@app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt`:
- Around line 36-40: The fragment attaches an adapter and a scrollListener to
binding.recyclerView but never sets a LayoutManager, yet scrollListener assumes
a LinearLayoutManager; explicitly set binding.recyclerView.layoutManager =
LinearLayoutManager(requireContext()) (or the desired orientation) before
assigning adapter/scrollListener to ensure pagination (scrollListener) works
even if the XML changes; reference binding.recyclerView, scrollListener, adapter
and use LinearLayoutManager in the fragment initialization block where the
RecyclerView is configured.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6fabeba6-61a6-4695-810a-6b87976b5b14

📥 Commits

Reviewing files that changed from the base of the PR and between 4bb22a8 and e93998b.

📒 Files selected for processing (1)
  • app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt

@capcom6 capcom6 force-pushed the codex/plan-incoming-message-logs-module branch from 8a6ae3e to 71bb894 Compare March 30, 2026 03:50
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessagesDao.kt (1)

14-15: Make the "latest" query deterministic.

Line 14 orders only by createdAt, so rows with the same timestamp can shuffle between emissions and make the LIMIT :limit window unstable. Add a unique tie-breaker.

♻️ Suggested query tweak
-    `@Query`("SELECT * FROM incoming_messages ORDER BY createdAt DESC LIMIT :limit")
+    `@Query`("SELECT * FROM incoming_messages ORDER BY createdAt DESC, id DESC LIMIT :limit")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessagesDao.kt`
around lines 14 - 15, The SELECT in function selectLast uses ORDER BY createdAt
DESC which is non-deterministic when multiple rows share the same timestamp;
update the query to include a unique tie-breaker (e.g., add the primary key
column such as id) to the ORDER BY clause so results are stable across emissions
(for example ORDER BY createdAt DESC, id DESC), ensuring selectLast(limit: Int)
returns a deterministic window.
app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt (1)

36-55: Add a migration test for 18 → 19.

This upgrade path now runs for every existing install. A Room migration test against a real v18 schema is the safest way to catch startup-time failures from the new table/indexes before release.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt` around lines 36 -
55, Add a Room migration test that verifies the AppDatabase upgrade from schema
version 18 to version 19 using the same database schema and migrations
configured in AppDatabase (version = 19 and AutoMigration list); write a test
(e.g., AppDatabaseMigrationTest) that uses MigrationTestHelper (or
SupportSQLite) to create a real v18 database (using the v18 schema SQL asset or
a pre-populated file), run the migrations applied by AppDatabase (including the
AutoMigration from 18 to 19), and assert the database opens and expected new
tables/indexes/columns exist and queries succeed; ensure the test references
AppDatabase and the migration path 18→19 so future changes fail the test if the
migration is broken.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt`:
- Around line 82-94: The current catch around
incomingMessagesService.save(message, sender, recipient, simNumber) only logs
the error and allows processing to continue; change this so failures stop
further work: after calling logsService.insert(...) rethrow the exception (or
throw a new RuntimeException with the original as cause) so the caller/flow
aborts and no dispatch/webhook processing occurs when save() fails; update the
try/catch around incomingMessagesService.save in ReceiverService (the block that
calls incomingMessagesService.save and logs via logsService.insert) to rethrow
the error instead of swallowing it.

---

Nitpick comments:
In `@app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt`:
- Around line 36-55: Add a Room migration test that verifies the AppDatabase
upgrade from schema version 18 to version 19 using the same database schema and
migrations configured in AppDatabase (version = 19 and AutoMigration list);
write a test (e.g., AppDatabaseMigrationTest) that uses MigrationTestHelper (or
SupportSQLite) to create a real v18 database (using the v18 schema SQL asset or
a pre-populated file), run the migrations applied by AppDatabase (including the
AutoMigration from 18 to 19), and assert the database opens and expected new
tables/indexes/columns exist and queries succeed; ensure the test references
AppDatabase and the migration path 18→19 so future changes fail the test if the
migration is broken.

In
`@app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessagesDao.kt`:
- Around line 14-15: The SELECT in function selectLast uses ORDER BY createdAt
DESC which is non-deterministic when multiple rows share the same timestamp;
update the query to include a unique tie-breaker (e.g., add the primary key
column such as id) to the ORDER BY clause so results are stable across emissions
(for example ORDER BY createdAt DESC, id DESC), ensuring selectLast(limit: Int)
returns a deterministic window.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2efe7909-f979-46be-a29b-83e668e3fd8a

📥 Commits

Reviewing files that changed from the base of the PR and between 8a6ae3e and 71bb894.

📒 Files selected for processing (23)
  • README.md
  • app/schemas/me.capcom.smsgateway.data.AppDatabase/19.json
  • app/src/main/java/me/capcom/smsgateway/App.kt
  • app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
  • app/src/main/java/me/capcom/smsgateway/data/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/IncomingMessagesService.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessage.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessageTotals.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessagesDao.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/repositories/IncomingMessagesRepository.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt
  • app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt
  • app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt
  • app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt
  • app/src/main/java/me/capcom/smsgateway/ui/adapters/IncomingMessagesAdapter.kt
  • app/src/main/res/layout/fragment_holder.xml
  • app/src/main/res/layout/fragment_incoming_messages_list.xml
  • app/src/main/res/layout/item_incoming_message.xml
  • app/src/main/res/values-ru/strings.xml
  • app/src/main/res/values-zh/strings.xml
  • app/src/main/res/values/strings.xml
  • user-docs
✅ Files skipped from review due to trivial changes (10)
  • user-docs
  • README.md
  • app/src/main/java/me/capcom/smsgateway/App.kt
  • app/src/main/java/me/capcom/smsgateway/data/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessageTotals.kt
  • app/src/main/java/me/capcom/smsgateway/ui/adapters/IncomingMessagesAdapter.kt
  • app/src/main/res/layout/fragment_incoming_messages_list.xml
  • app/schemas/me.capcom.smsgateway.data.AppDatabase/19.json
  • app/src/main/res/layout/item_incoming_message.xml
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessage.kt
🚧 Files skipped from review as they are similar to previous changes (10)
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/repositories/IncomingMessagesRepository.kt
  • app/src/main/res/values-zh/strings.xml
  • app/src/main/res/layout/fragment_holder.xml
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/IncomingMessagesService.kt
  • app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt
  • app/src/main/res/values-ru/strings.xml
  • app/src/main/res/values/strings.xml
  • app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/main/res/values-ru/strings.xml`:
- Around line 164-169: Restore the three missing Russian string resources by
adding entries for settings_local_address_is, settings_public_address_is, and
webhook_id_format into the values-ru/strings.xml so HomeFragment and
WebhookAdapter can find localized text instead of falling back to default
locale; locate the existing incoming/outgoing blocks in values-ru/strings.xml
and insert appropriately translated Russian values for the keys
settings_local_address_is, settings_public_address_is, and webhook_id_format
using the same resource names used in code.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dccde8ae-9e96-41df-9f09-dcafa279e43d

📥 Commits

Reviewing files that changed from the base of the PR and between 8a6ae3e and 91167ef.

📒 Files selected for processing (23)
  • README.md
  • app/schemas/me.capcom.smsgateway.data.AppDatabase/19.json
  • app/src/main/java/me/capcom/smsgateway/App.kt
  • app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
  • app/src/main/java/me/capcom/smsgateway/data/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/IncomingMessagesService.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessage.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessageTotals.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessagesDao.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/repositories/IncomingMessagesRepository.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt
  • app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt
  • app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt
  • app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt
  • app/src/main/java/me/capcom/smsgateway/ui/adapters/IncomingMessagesAdapter.kt
  • app/src/main/res/layout/fragment_holder.xml
  • app/src/main/res/layout/fragment_incoming_messages_list.xml
  • app/src/main/res/layout/item_incoming_message.xml
  • app/src/main/res/values-ru/strings.xml
  • app/src/main/res/values-zh/strings.xml
  • app/src/main/res/values/strings.xml
  • user-docs
✅ Files skipped from review due to trivial changes (10)
  • user-docs
  • README.md
  • app/src/main/java/me/capcom/smsgateway/data/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessageTotals.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/repositories/IncomingMessagesRepository.kt
  • app/src/main/res/layout/item_incoming_message.xml
  • app/src/main/res/layout/fragment_incoming_messages_list.xml
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessage.kt
  • app/schemas/me.capcom.smsgateway.data.AppDatabase/19.json
🚧 Files skipped from review as they are similar to previous changes (7)
  • app/src/main/java/me/capcom/smsgateway/App.kt
  • app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/IncomingMessagesService.kt
  • app/src/main/res/values-zh/strings.xml
  • app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt
  • app/src/main/res/values/strings.xml
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt

Comment thread app/src/main/res/values-ru/strings.xml
@capcom6 capcom6 force-pushed the codex/plan-incoming-message-logs-module branch from 91167ef to 5561e4e Compare March 30, 2026 07:41
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt (1)

72-85: Consider avoiding show() / hide() for tab switching with these list fragments.

show() / hide() keeps the hidden fragment's view lifecycle active. Both MessagesListFragment.kt and IncomingMessagesListFragment.kt attach LiveData observers and RecyclerView scroll listeners in onViewCreated(), so the inactive tab continues receiving list updates and scroll events while off-screen. If that extra work isn't intentional, use replace() or gate the observers/pagination via onHiddenChanged() instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt` around lines 72
- 85, selectOutgoing currently uses childFragmentManager.commit with
show()/hide(), which keeps the hidden fragment's view lifecycle active and lets
MessagesListFragment and IncomingMessagesListFragment continue receiving
LiveData updates and scroll events; change the fragment transaction to use
replace(...) for TAG_OUTGOING (remove show/hide and add/replace the outgoing
fragment) so only the active fragment has its view lifecycle, or alternatively
modify MessagesListFragment and IncomingMessagesListFragment to gate
observer/scroll listener attachment in
onHiddenChanged()/setMenuVisibility()/onResume() so observers are only active
when visible (update selectOutgoing to stop calling hide/show and update
childFragmentManager.commit accordingly).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt`:
- Around line 72-85: selectOutgoing currently uses childFragmentManager.commit
with show()/hide(), which keeps the hidden fragment's view lifecycle active and
lets MessagesListFragment and IncomingMessagesListFragment continue receiving
LiveData updates and scroll events; change the fragment transaction to use
replace(...) for TAG_OUTGOING (remove show/hide and add/replace the outgoing
fragment) so only the active fragment has its view lifecycle, or alternatively
modify MessagesListFragment and IncomingMessagesListFragment to gate
observer/scroll listener attachment in
onHiddenChanged()/setMenuVisibility()/onResume() so observers are only active
when visible (update selectOutgoing to stop calling hide/show and update
childFragmentManager.commit accordingly).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1566948d-1614-41fe-8c7a-c247ab6c5cdd

📥 Commits

Reviewing files that changed from the base of the PR and between 91167ef and 5561e4e.

📒 Files selected for processing (23)
  • README.md
  • app/schemas/me.capcom.smsgateway.data.AppDatabase/19.json
  • app/src/main/java/me/capcom/smsgateway/App.kt
  • app/src/main/java/me/capcom/smsgateway/data/AppDatabase.kt
  • app/src/main/java/me/capcom/smsgateway/data/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/IncomingMessagesService.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessage.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessageTotals.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessagesDao.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/repositories/IncomingMessagesRepository.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt
  • app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt
  • app/src/main/java/me/capcom/smsgateway/ui/HolderFragment.kt
  • app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt
  • app/src/main/java/me/capcom/smsgateway/ui/adapters/IncomingMessagesAdapter.kt
  • app/src/main/res/layout/fragment_holder.xml
  • app/src/main/res/layout/fragment_incoming_messages_list.xml
  • app/src/main/res/layout/item_incoming_message.xml
  • app/src/main/res/values-ru/strings.xml
  • app/src/main/res/values-zh/strings.xml
  • app/src/main/res/values/strings.xml
  • user-docs
✅ Files skipped from review due to trivial changes (10)
  • user-docs
  • README.md
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/repositories/IncomingMessagesRepository.kt
  • app/src/main/res/layout/item_incoming_message.xml
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessageTotals.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/IncomingMessagesService.kt
  • app/src/main/res/layout/fragment_incoming_messages_list.xml
  • app/src/main/res/layout/fragment_holder.xml
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/db/IncomingMessage.kt
🚧 Files skipped from review as they are similar to previous changes (7)
  • app/src/main/java/me/capcom/smsgateway/App.kt
  • app/src/main/java/me/capcom/smsgateway/data/Module.kt
  • app/src/main/java/me/capcom/smsgateway/modules/incoming/vm/IncomingMessagesListViewModel.kt
  • app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt
  • app/src/main/java/me/capcom/smsgateway/ui/IncomingMessagesListFragment.kt
  • app/src/main/res/values/strings.xml
  • app/src/main/res/values-ru/strings.xml

@capcom6 capcom6 marked this pull request as ready for review March 30, 2026 07:59
@github-actions
Copy link
Copy Markdown
Contributor

🤖 Pull request artifacts

file commit
app-release.apk 5561e4e
app-release.aab 5561e4e
app-insecure.apk 5561e4e
app-insecure.aab 5561e4e

@capcom6 capcom6 changed the title Add incoming messages module with DB, service, UI, and wiring [incoming] add module for tracking incoming messages Mar 31, 2026
@capcom6 capcom6 merged commit 4d6169b into master Apr 1, 2026
5 checks passed
@capcom6 capcom6 deleted the codex/plan-incoming-message-logs-module branch April 1, 2026 01:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant