Skip to content

Ignore corrupted notes on load + offer cleanup + raw database import/export#1045

Open
Crustack wants to merge 8 commits into
mainfrom
feat/raw-db-export
Open

Ignore corrupted notes on load + offer cleanup + raw database import/export#1045
Crustack wants to merge 8 commits into
mainfrom
feat/raw-db-export

Conversation

@Crustack
Copy link
Copy Markdown
Owner

@Crustack Crustack commented May 30, 2026

Relates to #1029

  • When backup export fails on Error view, user can export raw database sqlite file
  • Raw database file can be imported just like other backups, but they of course do not contain file attachments
  • When getting a JSON converison error while reading notes from database, the corrupted fields will be reset, but all other notes will be loaded properly

Summary by CodeRabbit

  • New Features

    • Raw database export/import option in crash UI and settings; reimport flow with user prompts.
    • Centralized converter error reporting with dialog-driven cleanup action.
    • New database icon and strings for recovery/export flows.
  • Bug Fixes

    • Skips, logs and reports corrupted import rows; shows skipped counts.
    • Ensures cached database instance is closed/cleared before reimport.
  • Improvements

    • Unified progress reporting across importers and clearer import-result messages.
    • Bulk note update support and safer converter fallbacks.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 30, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

Unifies importer progress types to Progress; adds raw-database SAF export/import and readBaseNotes; centralizes converter error reporting (ConverterErrorReporter) and makes converters return safe defaults on failure; tracks corrupted import rows and threads corruption counts through import flows; updates UI, dialogs, and DAOs for import/export and cleanup.

Raw DB import/export & resilience

Layer / File(s) Summary
Progress type unification (importers)
app/src/main/java/.../imports/*
All importers now accept MutableLiveData<Progress>? instead of MutableLiveData<ImportProgress>? while still posting ImportProgress updates.
readBaseNotes & importZip/importRawDatabase
app/src/main/java/com/philkes/notallyx/utils/backup/ImportExtensions.kt
Adds readBaseNotes, BaseNotesImport, importRawDatabase, threads Progress through ZIP import and resets progress in finally.
NotesImporter & CommonDao corrupted-note tracking
app/src/main/java/.../imports/NotesImporter.kt, .../dao/CommonDao.kt
Per-note try/catch to skip corrupted rows, propagate corrupted counts, and extend ImportResult and importBackup(...) signatures.
Converters & error reporting
app/src/main/java/com/philkes/notallyx/data/model/ConverterErrorReporter.kt, .../Converters.kt
Adds ConverterErrorReporter singleton and wraps Room converters with try/catch to report errors and return safe defaults.
Raw DB export helper
app/src/main/java/com/philkes/notallyx/utils/backup/ExportExtensions.kt
Adds exportRawDatabase(fileUri) that writes a DB copy to SAF and validates OpenableColumns.SIZE.
ErrorActivity raw export + reimport
app/src/main/java/com/philkes/notallyx/utils/ErrorActivity.kt
Adds SAF export action on crash dialog, exports DB to SAF, then offers reimport flow that clears DB singleton and imports the selected raw DB.
Settings / ViewModel / UI wiring
app/src/main/java/.../fragment/settings/SettingsFragment.kt, .../presentation/viewmodel/BaseNoteModel.kt, .../utils/AndroidExtensions.kt
Wires raw-database pickers, adds importRawDatabase(...), switches importProgress to Progress, adds toMessage(ImportResult) and extended showErrorDialog neutral button.
MainActivity error collection & cleanup
app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt
Collects ConverterErrorReporter.errors, shows dialog with neutral cleanup which runs BaseNoteModel.cleanupDatabase(...) and shows completion toast.
DB helpers & resources
app/src/main/java/com/philkes/notallyx/data/NotallyDatabase.kt, .../dao/BaseNoteDao.kt, app/src/main/res/*
Adds NotallyDatabase.clearInstance(...), registers Migration11, adds BaseNoteDao.updateAll(...), adds drawable, strings, plurals, and layout color fixes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

"A rabbit hopped with a USB key tight,
Saved the database under moonlight.
It patched the imports, caught the snags,
Logged the bugs and fixed the crags.
Hooray — clean imports, sleep well tonight!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main changes: handling corrupted notes, offering cleanup, and raw database import/export functionality.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

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

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
Contributor

@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: 10

🧹 Nitpick comments (4)
app/src/main/java/com/philkes/notallyx/presentation/viewmodel/BaseNoteModel.kt (2)

945-951: 💤 Low value

Verify that cleanupDatabase achieves intended corruption cleanup.

The method reads all notes and immediately writes them back via updateAll. The cleanup behavior appears to rely on implicit side effects during the write operation (e.g., converter error handling that resets corrupted fields). Consider adding a clarifying comment or renaming to reprocessAllNotes to make the intent clearer.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/src/main/java/com/philkes/notallyx/presentation/viewmodel/BaseNoteModel.kt`
around lines 945 - 951, The current cleanupDatabase function simply reads all
notes via baseNoteDao.getAllSync() and writes them back with
baseNoteDao.updateAll(allNotes), relying on implicit side effects to fix
corrupted fields; either rename the function to reprocessAllNotes to reflect
that it reserializes/deserializes every record, or keep the name and add a
precise comment above cleanupDatabase explaining that the intent is to trigger
converter/DAO-side repair by re-saving all entities (mentioning
baseNoteDao.getAllSync and baseNoteDao.updateAll), so callers understand it does
not perform explicit validation but forces reprocessing.

413-424: ⚡ Quick win

Consider more specific error message for raw database imports.

The error toast displays R.string.invalid_backup, but when importing a raw database, "invalid database" might be clearer than "invalid backup" since the user is selecting a .sqlite file, not a backup archive.

💬 Suggested message refinement

Consider adding a dedicated string resource for raw database import errors, or using a more generic message:

         val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
             app.log(TAG, throwable = throwable)
-            app.showToast("${app.getString(R.string.invalid_backup)}: ${throwable.message}")
+            app.showToast("${app.getString(R.string.error)}: ${throwable.message}")
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/src/main/java/com/philkes/notallyx/presentation/viewmodel/BaseNoteModel.kt`
around lines 413 - 424, The exception handler in importRawDatabase uses
R.string.invalid_backup which is misleading for raw .sqlite imports; update the
handler to use a more specific message (e.g., create and use
R.string.invalid_database or a generic R.string.invalid_file) and call
app.showToast with that string (via app.getString(...)) so users see "invalid
database" for importRawDatabase failures; update any references in the
CoroutineExceptionHandler block (importRawDatabase, app.showToast,
R.string.invalid_backup) accordingly.
app/src/main/java/com/philkes/notallyx/utils/ErrorActivity.kt (1)

199-221: 🏗️ Heavy lift

Consider restoring the safety copy on reimport failure.

The reimport flow creates a database copy (_BEFORE_REIMPORT) at line 204 but never restores it if the reimport fails. Users experiencing reimport failures are left with a deleted database and must manually recover.

🔄 Suggested safety improvement

Restore the safety copy in the reimport exception handler:

lifecycleScope.launch(exceptionHandler) {
    val importResult =
        withContext(Dispatchers.IO) {
            val (_, databaseCopy) = copyDatabase(suffix = "_BEFORE_REIMPORT")
            try {
                deleteDatabase(NotallyDatabase.DATABASE_NAME)
                NotallyDatabase.clearInstance()
                application.importRawDatabase(uri, exportBackupProgress)
            } catch (e: Exception) {
                // Restore safety copy before propagating error
                databaseCopy.copyToLarge(
                    NotallyDatabase.getCurrentDatabaseFile(application),
                    overwrite = true
                )
                throw e
            }
        }
    // ... show success dialog
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/philkes/notallyx/utils/ErrorActivity.kt` around lines
199 - 221, The reimport routine creates a safety copy via copyDatabase(suffix =
"_BEFORE_REIMPORT") but never restores it on failure; modify the
withContext(Dispatchers.IO) block so that after creating val (_, databaseCopy)
you wrap the
deleteDatabase(NotallyDatabase.DATABASE_NAME)/NotallyDatabase.clearInstance()/application.importRawDatabase(uri,
exportBackupProgress) sequence in a try/catch, and in the catch restore the
safety copy by calling
databaseCopy.copyToLarge(NotallyDatabase.getCurrentDatabaseFile(application),
overwrite = true) (or equivalent restore method) before rethrowing the
exception; this ensures the original DB is restored if importRawDatabase fails.
app/src/main/java/com/philkes/notallyx/utils/backup/ImportExtensions.kt (1)

138-138: 💤 Low value

Remove or clarify outdated TODO comment.

The comment suggests error handling is missing, but the toList function (lines 447-451) already wraps each row conversion in try-catch and counts failures. Either remove this TODO or clarify what additional handling is needed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/philkes/notallyx/utils/backup/ImportExtensions.kt` at
line 138, The TODO comment "// TODO: add error handling if single note fails" is
outdated because the toList function already wraps each row conversion in
try-catch and counts failures; either remove that TODO or replace it with a
clarifying comment that references the existing toList behavior (e.g., "per-note
failures are caught and counted in toList") so future readers know where
per-item errors are handled; update the comment near ImportExtensions.kt (the
earlier TODO) to either be deleted or changed to explicitly point to the toList
implementation and any additional desired behavior if more handling is required.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/com/philkes/notallyx/data/dao/BaseNoteDao.kt`:
- Around line 132-135: Two DAO methods duplicate the same query and return type:
getAll() and getAllSync() in BaseNoteDao; consolidate to one method (pick a
single name, e.g., getAll()) and remove the other, then update all call sites
(e.g., change usages in CleanupMissingAttachmentsWorker.kt and BaseNoteModel.kt
to the chosen method name). Make sure the DAO method signature and return type
remain List<BaseNote> and adjust imports or references if necessary.

In `@app/src/main/java/com/philkes/notallyx/data/dao/CommonDao.kt`:
- Line 103: The code initializes a local mutable counter corrupted from
readCorrupted and increments corrupted when insertions fail, but the function
returns readCorrupted instead, discarding the increments; update the
return/result assembly to use the mutated corrupted variable (not readCorrupted)
so corruptedNotes count reflects skipped/failed inserts. Locate the usages in
CommonDao.kt where corrupted is declared/updated (the corrupted/readCorrupted
pair) and change the returned value(s) at the end of those import/processing
functions (also repeat the same fix in the second similar block around the other
occurrence) to return the local corrupted variable.

In `@app/src/main/java/com/philkes/notallyx/data/imports/NotesImporter.kt`:
- Around line 109-113: The duplicate count is currently computed as
totalCandidates - insertedCount which incorrectly includes corrupted notes;
change the computation where ImportResult(...) is returned (in
NotesImporter/ImportResult creation) to subtract corruptedCount as well, i.e.
set duplicates = totalCandidates - insertedCount - corruptedCount so duplicates
reflect only actual duplicate notes (use the existing insertedCount,
corruptedCount and totalCandidates symbols).

In `@app/src/main/java/com/philkes/notallyx/data/NotallyDatabase.kt`:
- Around line 117-119: clearInstance() must remove the forever LiveData
observers and close the old RoomDatabase before nulling the singleton: obtain
the current instance.value (the RoomDatabase created by createInstance), call
preferences.biometricLock.removeObserver(biometricLockObserver) and
preferences.dataInPublicFolder.removeObserver(dataInPublicFolderObserver)
(matching the observers registered via
preferences.biometricLock.observeForeverSkipFirst /
preferences.dataInPublicFolder.observeForeverSkipFirst), then call
instance.value.close() to release DB resources (so
importRawDatabase/ErrorActivity won’t leak file handles), and only after
removing observers and closing the DB set instance = null; ensure you reference
the existing fields biometricLockObserver and dataInPublicFolderObserver and
perform null-safe checks around instance/value and the observers.

In
`@app/src/main/java/com/philkes/notallyx/presentation/activity/main/fragment/settings/SettingsFragment.kt`:
- Around line 567-597: The selection index is offset by two display items, so
assigned selectedImportSource = ImportSource.entries[which - 1] is wrong; change
the assignment to use which - 2 (i.e., selectedImportSource =
ImportSource.entries[which - 2]) and add a guard to ensure which >= 2 before
indexing to avoid out-of-bounds; update the code around the when(...) block
where selectedImportSource is set (referencing selectedImportSource and
ImportSource.entries) to apply this fix.

In `@app/src/main/java/com/philkes/notallyx/utils/backup/ExportExtensions.kt`:
- Around line 321-328: In ContextWrapper.exportRawDatabase, handle the case
where contentResolver.openOutputStream(fileUri) returns null and report errors:
check the nullable result before use, log or surface an error (via
Timber/processLogger/throw an exception or return a failure result) if the
OutputStream is null so the caller/user is notified, and also catch IOExceptions
from FileInputStream(databaseCopy) and inputStream.copyToLarge to log/report the
specific failure; update callers of exportRawDatabase or the function signature
if needed to propagate the error instead of silently succeeding.
- Around line 329-335: Replace the fragile assert-based size check with explicit
validation: inside the contentResolver.query(...) use block (the cursor handling
that reads OpenableColumns.SIZE) verify cursor.moveToFirst() and read sizeIndex;
if the size is missing or <= 0 then throw or return a descriptive exception
(e.g., IOException or IllegalStateException) instead of assert so it works in
production. Also ensure the temporary file referenced as databaseCopy is cleaned
up: wrap the code that creates/uses databaseCopy in a try/finally (or use
Kotlin’s use-block equivalent) and call databaseCopy.delete() (or
deleteRecursively() if a directory) in the finally to guarantee deletion even on
error. Include these changes around the contentResolver.query block and any code
that constructs/uses databaseCopy.

In `@app/src/main/java/com/philkes/notallyx/utils/backup/ImportExtensions.kt`:
- Around line 95-110: The importRawDatabase function currently leaks the
InputStream and leaves the temporary tempDbFile behind; fix by opening the
InputStream with Kotlin's use (e.g.,
contentResolver.openInputStream(dbFileUri).use { stream -> ... }) so the stream
is always closed, and ensure tempDbFile.delete() is called in a finally block
(or use try { ... } finally { tempDbFile.delete() }) after readBaseNotes/import
so the temp file is removed regardless of success or exception; apply these
changes around the calls in importRawDatabase (symbols: importRawDatabase,
stream, tempDbFile, readBaseNotes, import, importProgress).
- Around line 119-146: The opened SQLiteDatabase in ContextWrapper.readBaseNotes
(variable database) is never closed; wrap the database usage in a safe close
pattern (e.g., database.use { ... } or try { ... } finally { database.close() })
so the DB is always closed even on exceptions, and also ensure labelCursor and
baseNoteCursor are closed (use cursor.use { ... } or close them in finally)
while keeping the existing logic that builds BaseNotesImport (baseNotes,
originalIds, labels, corrupted).

In `@app/src/main/java/com/philkes/notallyx/utils/ErrorActivity.kt`:
- Line 196: The TODO should be resolved by using the reimport failure's
stacktrace rather than the original crash stacktrace: inside ErrorActivity's
reimport error handler (the code path that catches the reimport Throwable, e.g.,
in the method handling reimport failures), capture the reimport Throwable's
stacktrace (for example via throwable.stackTraceToString() or
Log.getStackTraceString(reimportThrowable)) and pass that string (or the
reimport Throwable itself if the receiver accepts a Throwable) instead of the
prior "stacktrace" variable so the logged/returned stacktrace reflects the
reimport error.

---

Nitpick comments:
In
`@app/src/main/java/com/philkes/notallyx/presentation/viewmodel/BaseNoteModel.kt`:
- Around line 945-951: The current cleanupDatabase function simply reads all
notes via baseNoteDao.getAllSync() and writes them back with
baseNoteDao.updateAll(allNotes), relying on implicit side effects to fix
corrupted fields; either rename the function to reprocessAllNotes to reflect
that it reserializes/deserializes every record, or keep the name and add a
precise comment above cleanupDatabase explaining that the intent is to trigger
converter/DAO-side repair by re-saving all entities (mentioning
baseNoteDao.getAllSync and baseNoteDao.updateAll), so callers understand it does
not perform explicit validation but forces reprocessing.
- Around line 413-424: The exception handler in importRawDatabase uses
R.string.invalid_backup which is misleading for raw .sqlite imports; update the
handler to use a more specific message (e.g., create and use
R.string.invalid_database or a generic R.string.invalid_file) and call
app.showToast with that string (via app.getString(...)) so users see "invalid
database" for importRawDatabase failures; update any references in the
CoroutineExceptionHandler block (importRawDatabase, app.showToast,
R.string.invalid_backup) accordingly.

In `@app/src/main/java/com/philkes/notallyx/utils/backup/ImportExtensions.kt`:
- Line 138: The TODO comment "// TODO: add error handling if single note fails"
is outdated because the toList function already wraps each row conversion in
try-catch and counts failures; either remove that TODO or replace it with a
clarifying comment that references the existing toList behavior (e.g., "per-note
failures are caught and counted in toList") so future readers know where
per-item errors are handled; update the comment near ImportExtensions.kt (the
earlier TODO) to either be deleted or changed to explicitly point to the toList
implementation and any additional desired behavior if more handling is required.

In `@app/src/main/java/com/philkes/notallyx/utils/ErrorActivity.kt`:
- Around line 199-221: The reimport routine creates a safety copy via
copyDatabase(suffix = "_BEFORE_REIMPORT") but never restores it on failure;
modify the withContext(Dispatchers.IO) block so that after creating val (_,
databaseCopy) you wrap the
deleteDatabase(NotallyDatabase.DATABASE_NAME)/NotallyDatabase.clearInstance()/application.importRawDatabase(uri,
exportBackupProgress) sequence in a try/catch, and in the catch restore the
safety copy by calling
databaseCopy.copyToLarge(NotallyDatabase.getCurrentDatabaseFile(application),
overwrite = true) (or equivalent restore method) before rethrowing the
exception; this ensures the original DB is restored if importRawDatabase fails.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: cd547302-30d1-49e1-a75e-0eacad4edb97

📥 Commits

Reviewing files that changed from the base of the PR and between cd2a7b6 and dcc7a95.

⛔ Files ignored due to path filters (1)
  • app/translations.xlsx is excluded by !**/*.xlsx
📒 Files selected for processing (21)
  • app/src/main/java/com/philkes/notallyx/data/NotallyDatabase.kt
  • app/src/main/java/com/philkes/notallyx/data/dao/BaseNoteDao.kt
  • app/src/main/java/com/philkes/notallyx/data/dao/CommonDao.kt
  • app/src/main/java/com/philkes/notallyx/data/imports/ExternalImporter.kt
  • app/src/main/java/com/philkes/notallyx/data/imports/NotesImporter.kt
  • app/src/main/java/com/philkes/notallyx/data/imports/evernote/EvernoteImporter.kt
  • app/src/main/java/com/philkes/notallyx/data/imports/google/GoogleKeepImporter.kt
  • app/src/main/java/com/philkes/notallyx/data/imports/json/JsonImporter.kt
  • app/src/main/java/com/philkes/notallyx/data/imports/quillpad/QuillpadImporter.kt
  • app/src/main/java/com/philkes/notallyx/data/imports/txt/PlainTextImporter.kt
  • app/src/main/java/com/philkes/notallyx/data/model/ConverterErrorReporter.kt
  • app/src/main/java/com/philkes/notallyx/data/model/Converters.kt
  • app/src/main/java/com/philkes/notallyx/presentation/activity/main/fragment/settings/SettingsFragment.kt
  • app/src/main/java/com/philkes/notallyx/presentation/viewmodel/BaseNoteModel.kt
  • app/src/main/java/com/philkes/notallyx/utils/AndroidExtensions.kt
  • app/src/main/java/com/philkes/notallyx/utils/ErrorActivity.kt
  • app/src/main/java/com/philkes/notallyx/utils/backup/ExportExtensions.kt
  • app/src/main/java/com/philkes/notallyx/utils/backup/ImportExtensions.kt
  • app/src/main/res/drawable/database.xml
  • app/src/main/res/layout/dialog_error.xml
  • app/src/main/res/values/strings.xml
💤 Files with no reviewable changes (1)
  • app/src/main/res/layout/dialog_error.xml

Comment thread app/src/main/java/com/philkes/notallyx/data/dao/BaseNoteDao.kt Outdated
Comment thread app/src/main/java/com/philkes/notallyx/data/dao/CommonDao.kt
Comment thread app/src/main/java/com/philkes/notallyx/data/NotallyDatabase.kt Outdated
Comment thread app/src/main/java/com/philkes/notallyx/utils/ErrorActivity.kt Outdated
Copy link
Copy Markdown
Contributor

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt (1)

268-272: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Ensure EditNoteActivity import exists in MainActivity.kt (Lines 268-272)

  • setupFAB() references EditNoteActivity::class.java, but EditNoteActivity is declared in com.philkes.notallyx.presentation.activity.note, so MainActivity.kt must import com.philkes.notallyx.presentation.activity.note.EditNoteActivity.

    🐛 Restore the import
     import com.philkes.notallyx.presentation.activity.note.EditListActivity
    +import com.philkes.notallyx.presentation.activity.note.EditNoteActivity
  • Collecting ConverterErrorReporter.errors via a plain lifecycleScope.launch { collect { ... } } is not lifecycle-state aware; prefer repeatOnLifecycle(STARTED) to avoid showing dialogs when the activity isn’t in a started state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt`
around lines 268 - 272, MainActivity.kt is missing an import for
EditNoteActivity used in setupFAB: add import
com.philkes.notallyx.presentation.activity.note.EditNoteActivity so setupFAB can
reference EditNoteActivity::class.java and continue calling
prepareNewNoteIntent(intent); additionally replace the plain
lifecycleScope.launch { ConverterErrorReporter.errors.collect { ... } }
collection with a lifecycle-aware pattern using lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
ConverterErrorReporter.errors.collect { ... } } } so error dialogs are only
shown while the activity is in STARTED state (reference symbols: setupFAB,
prepareNewNoteIntent, EditNoteActivity, ConverterErrorReporter.errors,
lifecycleScope.launch, repeatOnLifecycle(STARTED)).
🧹 Nitpick comments (1)
app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt (1)

101-118: ⚡ Quick win

Collect lifecycle-aware to avoid showing dialogs while the activity is stopped.

lifecycleScope.launch { errors.collect { ... } } keeps collecting from STARTED through STOPPED (it is only cancelled at onDestroy). If a converter error arrives while the activity is in the background, showErrorDialog will attempt to show a dialog on a non-resumed activity, risking a window leak / IllegalStateException after state has been saved. Wrap the collection in repeatOnLifecycle(Lifecycle.State.STARTED) so it only runs while visible.

♻️ Lifecycle-aware collection
-        lifecycleScope.launch {
-            ConverterErrorReporter.errors.collect { throwable ->
-                ...
-            }
-        }
+        lifecycleScope.launch {
+            repeatOnLifecycle(Lifecycle.State.STARTED) {
+                ConverterErrorReporter.errors.collect { throwable ->
+                    ...
+                }
+            }
+        }

Add imports:

+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
Kotlin androidx lifecycle repeatOnLifecycle recommended pattern for collecting flows in an Activity
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt`
around lines 101 - 118, The current lifecycleScope.launch collecting
ConverterErrorReporter.errors (lifecycleScope.launch {
ConverterErrorReporter.errors.collect { ... } }) can emit while the Activity is
stopped and cause dialogs to be shown on a non-resumed Activity; change this to
use lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) so collection only runs
while the Activity is visible—wrap the collection block in lifecycleScope.launch
{ repeatOnLifecycle(Lifecycle.State.STARTED) {
ConverterErrorReporter.errors.collect { ... } } }, keep the existing handling
(application.log, showErrorDialog, ConverterErrorReporter.registerDialog,
baseModel.cleanupDatabase) unchanged, and add the necessary import for
repeatOnLifecycle/Lifecycle.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt`:
- Around line 101-118: Multiple emitted converter errors currently spawn a
dialog per emission; coalesce them by changing the collector on
ConverterErrorReporter.errors to either use flow operators (e.g.,
ConverterErrorReporter.errors.conflate() or .debounce(300L)) or gate showing
with a local flag/activeDialog check so only one dialog is shown at a time;
implement by wrapping the existing lifecycleScope.launch collector (the block
that calls showErrorDialog, ConverterErrorReporter.registerDialog and
baseModel.cleanupDatabase) so before calling showErrorDialog you return early if
a dialog is already active, set the flag when you register the dialog and clear
it when dismissing (or hook into ConverterErrorReporter.dismissAllDialogs) to
ensure only a single cleanup prompt appears.

---

Outside diff comments:
In
`@app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt`:
- Around line 268-272: MainActivity.kt is missing an import for EditNoteActivity
used in setupFAB: add import
com.philkes.notallyx.presentation.activity.note.EditNoteActivity so setupFAB can
reference EditNoteActivity::class.java and continue calling
prepareNewNoteIntent(intent); additionally replace the plain
lifecycleScope.launch { ConverterErrorReporter.errors.collect { ... } }
collection with a lifecycle-aware pattern using lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
ConverterErrorReporter.errors.collect { ... } } } so error dialogs are only
shown while the activity is in STARTED state (reference symbols: setupFAB,
prepareNewNoteIntent, EditNoteActivity, ConverterErrorReporter.errors,
lifecycleScope.launch, repeatOnLifecycle(STARTED)).

---

Nitpick comments:
In
`@app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt`:
- Around line 101-118: The current lifecycleScope.launch collecting
ConverterErrorReporter.errors (lifecycleScope.launch {
ConverterErrorReporter.errors.collect { ... } }) can emit while the Activity is
stopped and cause dialogs to be shown on a non-resumed Activity; change this to
use lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) so collection only runs
while the Activity is visible—wrap the collection block in lifecycleScope.launch
{ repeatOnLifecycle(Lifecycle.State.STARTED) {
ConverterErrorReporter.errors.collect { ... } } }, keep the existing handling
(application.log, showErrorDialog, ConverterErrorReporter.registerDialog,
baseModel.cleanupDatabase) unchanged, and add the necessary import for
repeatOnLifecycle/Lifecycle.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: dbd41fd2-fa79-46ab-b615-b37c56ec3a2b

📥 Commits

Reviewing files that changed from the base of the PR and between 1c3842a and e973cbe.

📒 Files selected for processing (2)
  • app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt
  • app/src/main/java/com/philkes/notallyx/utils/backup/ExportExtensions.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/com/philkes/notallyx/utils/backup/ExportExtensions.kt

Comment thread app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt Outdated
Copy link
Copy Markdown
Contributor

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/com/philkes/notallyx/data/model/ConverterErrorReporter.kt`:
- Line 11: Make the mutable set activeDialogs private to prevent external
mutation: rename/replace the public mutable property activeDialogs with a
private mutableSet (e.g., _activeDialogs) and expose a read-only view via a
public val activeDialogs: Set<Dialog> get() = _activeDialogs.toSet(); update any
usages in ConverterErrorReporter (including registerDialog/unregisterDialog or
other methods that mutate the set) to reference _activeDialogs so external
callers can only read the snapshot but must use the class' register/unregister
APIs to change membership.
- Line 11: Replace the non-thread-safe mutableSetOf used by the activeDialogs
field with a thread-safe collection to prevent races from registerDialog,
dismissAllDialogs and dismiss listeners; change the activeDialogs declaration to
a synchronized set (e.g. Collections.synchronizedSet(...)) and add the import
java.util.Collections, and also make activeDialogs private to improve
encapsulation so only the class methods (registerDialog, dismissAllDialogs,
etc.) mutate it.
- Line 9: Change the mutable reference to the AtomicBoolean so it cannot be
reassigned: replace the declaration "var enabled = AtomicBoolean(true)" with an
immutable property (val enabled = AtomicBoolean(true)) in ConverterErrorReporter
(so callers mutate state via enabled.set(...) instead of reassigning the
AtomicBoolean reference).
- Around line 25-29: dismissAllDialogs() currently iterates activeDialogs while
each dialog's dismiss listener may remove entries, causing
ConcurrentModificationException; fix by iterating over a snapshot (e.g.,
activeDialogs.toList()) when calling dismiss(), and if activeDialogs is a
synchronized set guard the snapshot creation plus activeDialogs.clear() and
errors.postValue(null) in the same synchronized block to ensure consistency
between dismissal and clearing; references: dismissAllDialogs(), activeDialogs,
errors, and the dialog dismiss listener that mutates activeDialogs.
- Line 14: The check reading LiveData.errors.value from a background thread in
ConverterErrorReporter (the if using enabled.get() && errors.value == null
inside reportError) is unsafe; replace that value read with a thread-safe flag
such as an AtomicReference<Throwable?> or AtomicBoolean that tracks whether an
error is already set, update that atomic from reportError (before calling
errors.postValue) and clear it in dismissAllDialogs, and keep using
errors.postValue for the LiveData update so no direct LiveData.value reads occur
off the main thread.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: fa0de967-3593-465f-be60-5ab241b18eea

📥 Commits

Reviewing files that changed from the base of the PR and between 1c1acaf and a7b3fbe.

⛔ Files ignored due to path filters (1)
  • app/translations.xlsx is excluded by !**/*.xlsx
📒 Files selected for processing (5)
  • app/src/main/java/com/philkes/notallyx/data/model/ConverterErrorReporter.kt
  • app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt
  • app/src/main/java/com/philkes/notallyx/utils/backup/ExportExtensions.kt
  • app/src/main/java/com/philkes/notallyx/utils/backup/ImportExtensions.kt
  • app/src/main/res/values/strings.xml
✅ Files skipped from review due to trivial changes (1)
  • app/src/main/res/values/strings.xml
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/src/main/java/com/philkes/notallyx/utils/backup/ExportExtensions.kt
  • app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt
  • app/src/main/java/com/philkes/notallyx/utils/backup/ImportExtensions.kt

Comment thread app/src/main/java/com/philkes/notallyx/data/model/ConverterErrorReporter.kt Outdated
Comment thread app/src/main/java/com/philkes/notallyx/data/model/ConverterErrorReporter.kt Outdated
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