| 232 | } |
| 233 | |
| 234 | void EpubReaderActivity::loop() { |
| 235 | if (!epub) { |
| 236 | // Should never happen |
| 237 | finish(); |
| 238 | return; |
| 239 | } |
| 240 | |
| 241 | // End-of-Book screen reached (currentSpineIndex == spine count) means the book is |
| 242 | // finished. Two independent finished-book features key off this same condition. |
| 243 | const bool atEndOfBook = currentSpineIndex > 0 && currentSpineIndex >= epub->getSpineItemsCount(); |
| 244 | |
| 245 | // Drop this book from the Recent Books list; if the reader then pages back into the book, |
| 246 | // re-add it. So removal only sticks if the reader leaves while still on the End-of-Book |
| 247 | // screen. Acts only on the transition (guarded by recentsEntryRemoved) — no per-frame writes. |
| 248 | if (SETTINGS.removeReadBooksFromRecents) { |
| 249 | if (atEndOfBook && !recentsEntryRemoved) { |
| 250 | // Only treat the book as "removed by us" if it was actually in the list, so the |
| 251 | // re-add branch below doesn't insert a book the feature never removed. |
| 252 | recentsEntryRemoved = RECENT_BOOKS.removeByPath(epub->getPath()); |
| 253 | } else if (!atEndOfBook && recentsEntryRemoved) { |
| 254 | // Re-add (goes to front of the list via addBook — accepted ordering side effect). |
| 255 | RECENT_BOOKS.addBook(epub->getPath(), epub->getTitle(), epub->getAuthor(), epub->getThumbBmpPath()); |
| 256 | recentsEntryRemoved = false; |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | // Arm the move here so ANY exit path (Back, Home, file browser) relocates the book into |
| 261 | // /Read/ in onExit(); paging back off the end screen disarms it (book not actually |
| 262 | // finished). If removeReadBooksFromRecents also fired, RecentBooksStore::updatePath in the |
| 263 | // move path becomes a safe no-op since the entry was already removed. |
| 264 | if (atEndOfBook) { |
| 265 | pendingReadFolderMove = SETTINGS.moveFinishedToReadFolder && !isInReadFolder(epub->getPath()); |
| 266 | } else { |
| 267 | pendingReadFolderMove = false; |
| 268 | } |
| 269 | |
| 270 | if (automaticPageTurnActive) { |
| 271 | if (mappedInput.wasReleased(MappedInputManager::Button::Confirm) || |
| 272 | mappedInput.wasReleased(MappedInputManager::Button::Back)) { |
| 273 | automaticPageTurnActive = false; |
| 274 | // updates chapter title space to indicate page turn disabled |
| 275 | requestUpdate(); |
| 276 | return; |
| 277 | } |
| 278 | |
| 279 | if (!section) { |
| 280 | requestUpdate(); |
| 281 | return; |
| 282 | } |
| 283 | |
| 284 | // Skips page turn if renderingMutex is busy |
| 285 | if (RenderLock::peek()) { |
| 286 | lastPageTurnTime = millis(); |
| 287 | return; |
| 288 | } |
| 289 | |
| 290 | if ((millis() - lastPageTurnTime) >= pageTurnDuration) { |
| 291 | pageTurn(true); |
nothing calls this directly
no test coverage detected