Skip to content

Fix bug when the background wasn't loaded correctly on pdf change#254

Merged
Ethran merged 1 commit into
mainfrom
dev
Apr 28, 2026
Merged

Fix bug when the background wasn't loaded correctly on pdf change#254
Ethran merged 1 commit into
mainfrom
dev

Conversation

@Ethran

@Ethran Ethran commented Apr 28, 2026

Copy link
Copy Markdown
Owner

…en using observe pdf.

Page Management & Backgrounds

  • PageDataManager:
    • Added a safety check in setBackground to prevent writing null bitmaps to the cache and emit a GenericError event if encountered.
    • Enhanced observeBackgroundFile to handle file deletion and recreation more robustly. It now re-registers the FileObserver when a file is replaced.
    • Optimized invalidateFileFlow emissions to trigger only on CLOSE_WRITE or MOVED_TO events, ensuring file stability before background refreshes.
  • renderFromFile: Added bitmap state information to the "loaded background" performance log.

File Operations

  • FileUtils:
    • Improved waitForFileAvailable with detailed logging for file existence and size updates during the polling period.

Infrastructure

  • Removed an unused coroutineScope import in PageDataManager.

…en using observe pdf.

### Page Management & Backgrounds
- **PageDataManager**:
    - Added a safety check in `setBackground` to prevent writing null bitmaps to the cache and emit a `GenericError` event if encountered.
    - Enhanced `observeBackgroundFile` to handle file deletion and recreation more robustly. It now re-registers the `FileObserver` when a file is replaced.
    - Optimized `invalidateFileFlow` emissions to trigger only on `CLOSE_WRITE` or `MOVED_TO` events, ensuring file stability before background refreshes.
- **renderFromFile**: Added bitmap state information to the "loaded background" performance log.

### File Operations
- **FileUtils**:
    - Improved `waitForFileAvailable` with detailed logging for file existence and size updates during the polling period.

### Infrastructure
- Removed an unused `coroutineScope` import in `PageDataManager`.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes cases where page backgrounds (notably PDFs) fail to refresh correctly when the underlying PDF/background file is replaced/rewritten, by hardening background caching and file observation behavior.

Changes:

  • Add null-bitmap guard in PageDataManager.setBackground and improve background file observation (delete/recreate handling + emit only when file is stable).
  • Add additional logging to waitForFileAvailable to aid diagnosing file availability races.
  • Add bitmap info to the “loaded background” timing log.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
app/src/main/java/com/ethran/notable/io/renderFromFile.kt Expands the performance timing log for background loading.
app/src/main/java/com/ethran/notable/io/FileUtils.kt Adds debug logging around file availability polling.
app/src/main/java/com/ethran/notable/data/PageDataManager.kt Prevents caching null backgrounds and improves FileObserver handling for background updates.

Comment on lines +669 to +675
val msg = "setBackground: skipping cache write, bitmap is null (id=${background.id})"
log.w(msg)
appEventBus.tryEmit(
AppEvent.GenericError(
msg
)
)

Copilot AI Apr 28, 2026

Copy link

Choose a reason for hiding this comment

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

setBackground() emits AppEvent.GenericError when background.bitmap is null. Since GenericError is user-facing (snackbar) and setBackground() can be hit repeatedly during rendering/zoom while a file is temporarily unavailable, this risks spamming users for transient conditions. Consider downgrading this to logging (or a throttled/once-per-file ActionHint) and only emitting a user-visible error after repeated/persistent failures.

Suggested change
val msg = "setBackground: skipping cache write, bitmap is null (id=${background.id})"
log.w(msg)
appEventBus.tryEmit(
AppEvent.GenericError(
msg
)
)
log.w("setBackground: skipping cache write, bitmap is null (id=${background.id})")

Copilot uses AI. Check for mistakes.
Comment on lines 756 to 765
if (event == IN_IGNORED)
return@launch
val eventString = fileObserverEventNames(event)

log.d("Background file changed: $filePath [event=$eventString]")

// HANDLE DELETION / RE-REGISTRATION
if (event == DELETE || event == DELETE_SELF) {
log.d("Background file deleted.")
log.d("Background file deleted. Waiting for recreation...")
synchronized(fileObservers) {

Copilot AI Apr 28, 2026

Copy link

Choose a reason for hiding this comment

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

FileObserver.onEvent's event is a bitmask; comparing with == can miss events when extra flags are present. The checks like event == IN_IGNORED and event == DELETE/DELETE_SELF should use bitwise tests (e.g., event and DELETE != 0) to be reliable.

Copilot uses AI. Check for mistakes.
Comment on lines +788 to +792
// Only emit when the file is ready to be read (CLOSE_WRITE or MOVED_TO)
if (event == CLOSE_WRITE || event == MOVED_TO) {
log.d("File is stable. Invalidating flow for: $filePath")
invalidateFileFlow.emit(filePath)
}

Copilot AI Apr 28, 2026

Copy link

Choose a reason for hiding this comment

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

Same bitmask issue for the stability gate: event == CLOSE_WRITE || event == MOVED_TO should use bitwise checks, otherwise a combined event value may skip invalidation and the background won't refresh.

Copilot uses AI. Check for mistakes.
else
renderPdfPageAndroid(file, pageNumber, targetWidth.toInt(), resolutionModifier = 1.2f)
timer.end("loaded background")
timer.end("loaded background, newBitmap: $newBitmap")

Copilot AI Apr 28, 2026

Copy link

Choose a reason for hiding this comment

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

Timing.end()'s label is now interpolating the Bitmap object ($newBitmap). This will produce a different log line every time (and usually only an object id), which makes timing logs harder to aggregate/grep and adds avoidable string allocation. Keep the timing label stable (e.g., "loaded background") and log bitmap details (size/config/isRecycled/null) in a separate log statement if needed.

Copilot uses AI. Check for mistakes.
@Ethran Ethran merged commit 59dace1 into main Apr 28, 2026
8 checks passed
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.

2 participants