Bitmap.createBitmap returns the same immutable instance when the center-crop covers the whole source, which happens for covers that decode to exactly 2:3. writeThumbnail then recycled that instance before createScaledBitmap used it, crashing with "cannot use a recycled source in createBitmap". Guard the recycle the same way the scaled vs cropped case is already guarded, and add an instrumented regression test. Also bundles a pending widget debugging note and a regenerated fastlane README that were staged in the working tree. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.0 KiB
name, description, metadata
| name | description | metadata | ||||||
|---|---|---|---|---|---|---|---|---|
| mobile-reading-widgets | Home-screen reading widgets (#1602, PR |
|
Mobile home-screen reading widgets (issue #1602, merged PR #4842). Code lives in the native-bridge plugin: src-tauri/plugins/tauri-plugin-native-bridge/{android,ios}/ (Android ReadingWidgetProvider.kt + res/; iOS writer ReadingWidgetWriter.swift) and the iOS WidgetKit extension at src-tauri/gen/apple/ReadestWidget/. App publishes a snapshot + downsized cover thumbnails via the update_reading_widget command to iOS App Group group.com.bilingify.readest / Android SharedPreferences. Widget hook: src/hooks/useReadingWidget.ts; payload builder src/services/widget/readingWidget.ts; tap opens readest://book/{hash} via useOpenBookLink.ts.
Durable, non-obvious gotchas (each cost a debugging round):
- iOS widget missing from gallery = stale
.xcodeproj.gen/apple/project.ymldefines theReadestWidgettarget, but Tauri's iOS build does NOT re-run xcodegen, so a newly-added target is silently omitted from the build. Fix:cd src-tauri/gen/apple && xcodegen generate. Also: iOS builds from the MAIN repo/Users/chrox/dev/readest(complete gen/apple), NOT thepnpm worktree:newworktree (its gen/apple is incomplete — missingSources/,Assets.xcassets,Externals,LaunchScreen.storyboard— so xcodegen fails there). - Android RemoteViews allow only @RemoteView widgets. Plain
<View>(and<Space>) is NOT allowed → launcher inflate fails → "Can't load widget". Use an emptyFrameLayoutfor spacers. Covers: badge + progress bar are baked into the bitmap (Canvas inwriteThumbnail) because RemoteViews can't clip/overlay reliably; shown viafitCenter. Responsive sizing by grid cells:n = (minWidthDp + 30) / 70(Android cell formula); one book per column, cap 3. - Background TTS progress freeze.
book.progress(libraryStore) ANDreaderProgressStoreare both written by the samesetProgress, insidecommitRelocate→requestAnimationFrame, which Android pauses for a backgrounded WebView → both freeze during background TTS. No store-only fix (page-based progress needs rendering). Fix: inFoliateViewer.progressRelocateHandler, commit synchronously whendocument.visibilityState === 'hidden'(relocate still fires; only the rAF commit was deferred). Confirmed working on device. - Android crash: "cannot use a recycled source in createBitmap" (exact-2:3 covers). In
ReadingWidgetStore.writeThumbnail,Bitmap.createBitmap(src, x, y, w, h)returns the SAME instance when the crop covers the whole immutable source (decodeFilebitmaps are immutable) — which happens when the cover decodes to exactly 2:3 (height==width*3/2), making the center-crop a no-op. The old code then didbitmap.recycle(), recyclingcroppedtoo, so the nextcreateScaledBitmap(cropped, …)threw. Fix:if (cropped !== bitmap) bitmap.recycle()— mirror theif (scaled !== cropped) cropped.recycle()guard already 4 lines below. Trace was R8-obfuscated + ran insideupdate_reading_widget'spluginScope.launch { withContext(Dispatchers.IO) }, surfacing asFATAL EXCEPTION: mainwith aDispatchers.Maincancelled-coroutine suppressed frame. iOS is unaffected —ReadingWidgetWriter.writeThumbnailuses ARC-managed immutableUIImage+UIGraphicsImageRenderer, no manual recycle/aliasing. - iOS TTS controls deferred — interactive widget buttons need iOS 17 App Intents; widget min target is iOS 15 (15/16 widgets can only deep-link, no buttons). Android uses
MediaButtonReceiver.buildMediaButtonPendingIntent(any version). Follow-up only. .superpowers/is NOT gitignored in this repo → a subagent'sgit addcan sweep SDD scratch (*-report.md) into a commit; checkgit ls-files '.superpowers/*'before squashing/pushing.
Related: android-nativefile-remotefile-io · tts-fixes · build/worktree feedback_use_worktree