Skip to content

Modernize build environment and fix stability issues for Android API 24+#227

Open
Dehumanizer77 wants to merge 8 commits intotomboy-notes:masterfrom
Dehumanizer77:master
Open

Modernize build environment and fix stability issues for Android API 24+#227
Dehumanizer77 wants to merge 8 commits intotomboy-notes:masterfrom
Dehumanizer77:master

Conversation

@Dehumanizer77
Copy link
Copy Markdown

This captures:

  • AGP/Gradle/SDK version upgrades
  • API 24+ FileProvider fix (replacing Uri.fromFile)
  • AndroidX migration pieces
  • Cursor lifecycle crash fixes
  • Runtime permission handling (API 23-28)

This is my very first real use of Claude, sorry if I missed anything. I also made Codex to review the code.

Dehumanizer77 and others added 7 commits February 24, 2026 16:16
- Replace all managedQuery/startManagingCursor with ContentResolver.query()
  to eliminate cursor lifecycle race condition that caused random sync crashes
  when the user navigated away mid-sync (Activity.onStop deactivated cursors
  while the background thread was still reading them)
- Add null guards after getNoteByGuid() calls in SyncService to prevent NPE
  when a note is unexpectedly absent from the database during sync
- Fix null cursor NPEs throughout NoteManager (cursor.close() on null cursor)
- Fix Html.fromHtml() deprecation for API 24+
- Replace Apache HttpClient with legacy library support (useLibrary +
  uses-library in manifest) so web sync works on Android 9+ (API 28+)
- Fix FileUriExposedException in Send.java: replace Uri.fromFile() and
  MODE_WORLD_READABLE with FileProvider + FLAG_GRANT_READ_URI_PERMISSION
- Add runtime WRITE_EXTERNAL_STORAGE permission request before SD card sync
  on API 23-28, with automatic sync resume on grant
- Update build: AGP 0.5 -> 7.4.2, Gradle 1.6 -> 7.6.4, compileSdk/targetSdk
  raised to 34/33, add AndroidX Core 1.9.0, add settings.gradle
- Add android:requestLegacyExternalStorage for Android 10 compatibility
- Add android:exported attributes (required on API 31+)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Drop compileSdk from 34 to 33 to match AGP 7.4.2 tested range
- Add gradle.properties enabling android.useAndroidX and android.enableJetifier,
  required since we now depend on androidx.core (for FileProvider)
- Add .gitignore for build/, .gradle/, local.properties and APK artifacts
- Fix AAPT2-rejected style parent refs: style/Foo -> @style/Foo in styles.xml
- Fix malformed ListView IDs rejected by AAPT2:
    file_picker_content_view.xml: @+id/android:list -> @android:id/list
    shortcuts_list.xml: @android:id/android:list -> @android:id/list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- onResume: move super.onResume() to top and add null guard for getCurrentService()
- onCreateDialog/onPrepareDialog: null-safe getCurrentService().getDescription()
- startSyncing: early return if no sync service configured (null getCurrentService)
- SyncMessageHandler: replace showDialog() with safeShowDialog() to guard against
  BadTokenException when activity window is not attached; null-safe currentService
- Add safeShowDialog() helper that checks isFinishing/isDestroyed before showing
- onActivityResult: null check before calling resolvedConflict()
- onCreateContextMenu: null check on dialogNote before accessing getTags()
- SyncManager.cancel(): null check on service field to prevent NPE on destroy
- ActionBarHelperHoneycomb/ICS.onPostCreate(): null check on getActionBar() to
  prevent NPE when theme does not provide a native ActionBar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On Samsung Android builds the framework registers the list adapter's
cursor in Activity.mManagedCursors via internal paths that bypass
startManagingCursor() overrides. When the user navigates back from a
note (triggering onRestart()), performRestart() tries to requery() the
cursor after it has been closed and throws IllegalStateException.

Fix strategy (belt-and-suspenders):
- Override startManagingCursor() as a no-op to block direct registration
- Override onStop() to clear mManagedCursors via reflection after the
  framework has already stopped the activity; performRestart() then
  finds an empty list and skips the requery loop entirely
- Properly close the list adapter cursor in updateNotesList() and
  onDestroy() so leaked cursors don't accumulate
- Close localGuids cursor in SyncService.prepareSyncableNotes() which
  was never closed after the loop (cursor leak on every sync)
- Close ShortcutActivity adapter cursor in onDestroy()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…crash

Fix crash in performRestart() caused by closed managed cursor
* Apply Codex suggested fixes

* Fix potential NPE in SnowySyncService finally block

ContentResolver.query() can return null, so newLocalNotes must be
null-checked before calling close() in the finally block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Apply Codex suggested fixes

* Fix potential NPE in SnowySyncService finally block

ContentResolver.query() can return null, so newLocalNotes must be
null-checked before calling close() in the finally block.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@obilodeau
Copy link
Copy Markdown
Member

As I said, in #224. I'm willing to accept this, I reviewed it for obvious backdoors and didn't find anything but I'm not cutting a release for the play store. I mean I haven't worked on this project for years...

* Prepare release 0.8.0-dehumanizer77

Update versionName to 0.8.0-dehumanizer77, add release signing
config (password supplied via -P gradle properties at build time),
and add keystore files to .gitignore.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix sync conflict resolution and harden push error handling

- Fix pushNotes() to check for null/empty server responses before JSON parsing
- Reset syncProgress to 100 on all error paths to prevent sync from getting stuck
- Guard against null tags from database in NoteManager and SyncService
- Fix cursor null check in SyncService.prepareSyncableNotes (same pattern as #4)
- Fix "chose remote" in differentNotes conflict to push delete command for local
  note to server, preventing the conflict from reappearing on next sync

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
@Dehumanizer77
Copy link
Copy Markdown
Author

I have added one more fix, I have noticed errors after a conflict appeared, the last commit fixes it.

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