From 9e28fa4981d7f04fb8afbae93bf4f19a3067cc26 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Fri, 24 Jul 2026 17:37:59 +0200 Subject: [PATCH] fix(intents): accept item-typed INSERT / INSERT_OR_EDIT for "Add to calendar" (#74) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Other apps' "Add to calendar" / "Save to calendar" actions commonly fire the canonical insert intent — ACTION_INSERT with setType("vnd.android.cursor.item/ event") — the singular *item* MIME type (Android's own docs example; used by e.g. DB Navigator). Calendula advertised INSERT only on the *dir* MIME type, so it never matched: absent from the chooser, and a silent no-op when it was the only calendar app installed. Add the item-typed INSERT to the events filter, plus ACTION_INSERT_OR_EDIT (the third "add to calendar" action AOSP and Google Calendar register). The runtime parser already treats any ACTION_INSERT as create; teach insertFormOrNull / editEventKeyOrNull about INSERT_OR_EDIT so an id-less one is a create and an id-carrying one opens the edit form. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 7 ++++ app/src/main/AndroidManifest.xml | 37 ++++++++++++++----- .../jeanlucmakiola/calendula/MainActivity.kt | 13 ++++--- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49f8808..8aafac8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Now a bigger widget gets bigger, more readable type and roomier rows, while the default size looks exactly as before — no new setting; it follows the size you already chose ([#51]). +- Calendula now appears under other apps' "Add to calendar" / "Save to calendar" + actions. Some apps (e.g. DB Navigator) fire the widely-used "insert event" + intent with the singular `vnd.android.cursor.item/event` type, which Calendula + didn't advertise — so it was left out of the chooser, and if it was your only + calendar app the save silently did nothing. It now accepts that form, plus the + `INSERT_OR_EDIT` action, and opens the new event prefilled for review ([#74]). ## [2.16.0] — 2026-07-17 @@ -1079,3 +1085,4 @@ automatically, with zero telemetry and no internet permission. [#53]: https://codeberg.org/jlmakiola/calendula/issues/53 [#60]: https://codeberg.org/jlmakiola/calendula/issues/60 [#65]: https://codeberg.org/jlmakiola/calendula/issues/65 +[#74]: https://codeberg.org/jlmakiola/calendula/issues/74 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7f73919..3de3eac 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -123,11 +123,13 @@ + to the same prefilled create form. (The far more common *item*- + typed INSERT — the form the Android docs' example and apps like + DB Navigator use — is the item filter below, issue #74.) --> @@ -135,14 +137,31 @@ - + + + diff --git a/app/src/main/java/de/jeanlucmakiola/calendula/MainActivity.kt b/app/src/main/java/de/jeanlucmakiola/calendula/MainActivity.kt index 1177165..0a0c20f 100644 --- a/app/src/main/java/de/jeanlucmakiola/calendula/MainActivity.kt +++ b/app/src/main/java/de/jeanlucmakiola/calendula/MainActivity.kt @@ -222,10 +222,12 @@ class MainActivity : AppCompatActivity() { * [buildInsertEventForm]. */ private fun Intent.insertFormOrNull(): EventForm? { - // ACTION_EDIT on an existing event routes to the edit form instead - // ([editEventKeyOrNull]); only an id-less EDIT is a create. + // ACTION_EDIT / ACTION_INSERT_OR_EDIT on an existing event route to the + // edit form instead ([editEventKeyOrNull]); an id-less one is a create, + // as is any plain ACTION_INSERT. val isCreate = action == Intent.ACTION_INSERT || - (action == Intent.ACTION_EDIT && editEventKeyOrNull() == null) + ((action == Intent.ACTION_EDIT || action == Intent.ACTION_INSERT_OR_EDIT) && + editEventKeyOrNull() == null) if (!isCreate) return null return buildInsertEventForm( beginMillis = longExtraOrNull(CalendarContract.EXTRA_EVENT_BEGIN_TIME), @@ -344,10 +346,11 @@ class MainActivity : AppCompatActivity() { * occurrence's times as `EXTRA_EVENT_BEGIN_TIME` / `EXTRA_EVENT_END_TIME` * when it has them, otherwise we carry [NO_OCCURRENCE_TIME] and * [EventEditViewModel.openForEdit] falls back to the event row's own - * DTSTART/DTEND. An id-less `ACTION_EDIT` is a create instead ([insertFormOrNull]). + * DTSTART/DTEND. An id-less `ACTION_EDIT` — or `ACTION_INSERT_OR_EDIT`, which + * some apps fire — is a create instead ([insertFormOrNull]). */ private fun Intent.editEventKeyOrNull(): LongArray? { - if (action != Intent.ACTION_EDIT) return null + if (action != Intent.ACTION_EDIT && action != Intent.ACTION_INSERT_OR_EDIT) return null val uri = data ?: return null if (uri.host != CALENDAR_PROVIDER_HOST) return null val segments = uri.pathSegments