A ZoneSource now gives the device's zone to the timeline, the event form and
reschedules, so all three agree on one zone. The timeline lays its pages out
again when the device moves zones, and the form shows its local times in the
same zone it reads them in.
- A day move onto the spring-forward gap keeps its wall clock, so a weekly
series moved onto that day is still offered as a day move.
- A resize handle let go in the repeated hour takes the pass the grid draws
there, so the block settles under the finger.
- Switching to all-day and back keeps a time on its pass of the repeated
hour.
- A series anchored in the repeated hour keeps its pass through a time edit,
and an edit of the anchor occurrence carries the pass it picked.
- An end picked in the spring-forward gap moves on to the real time, as a
start does.
- The editor opens a second draft or slot on the same day too.
- The repeated-hour rule leans on java.time's own overlap handling.
Phone numbers in an event's title, location and description are now links. Tapping one opens the dialer with the number filled in. Nothing is called until you press call, so the app needs no new permission.
- Android's own matcher finds the numbers (Linkify, backed by libphonenumber and the SIM's region). Its loose setting links too much in event text, so a cleanup step drops meeting IDs and PINs after a label, dates, dotted times like "9.30 – 12.00", amounts, anything under 7 digits and digits inside URLs.
- "+49 (0)151 …" dials without the (0). An extension ("ext. 567") becomes a pause, and a Zoom-style dial-in suffix (",,850…#") is kept.
- Detection runs in the detail ViewModel while the event loads, so the links are there on the first frame and survive rotation.
- Long-pressing a field copies it even when the press lands on a link. Before, the link swallowed the press.
- PhoneLinksTest covers the cleanup on the JVM. PhoneLinksPlatformTest (androidTest) runs the real matcher and passes on an emulator with a US SIM.
Known limit: on a device without a SIM (Wi-Fi tablets, some emulators) Android gives the matcher no region, so only numbers starting with + are linked.
Closes#339
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/395
A time picked in the fall-back hour's repeat, in the form or by a resize
handle, now gets its pass from one rule. It keeps the pass the event was
opened with when it is that same time again. Otherwise it moves the way the
hand did, and stays on the right side of the event's other edge where it
can.
- The form can set an end in the hour after a second-pass start, and a time
changed and changed back is the time it was.
- Pulling a resize handle up the repeated hour shortens the event.
- A block running into the repeat from before it is drawn to the hour's
end, so a longer one never looks shorter.
- A drafted block opens the form at its own start and end instants.
- The repeated-hour zone label skips the long zone name, and the detail
screen's time lines are remembered.
The event form held its times and their UTC offsets as four separate
fields, so any change to a time had to remember to refresh or clear its
offset. A form time is now one FormTime: the wall clock plus the offset it
was read at when the fall-back hour repeats it. Setting a time sets both, so
the two can't drift apart. Wall-clock meaning is unchanged: an unpinned form
still follows the device zone, a zone switch keeps the wall clock and
all-day forms stay dates.
- A pass switch now shows up in the form itself, so saving no longer
compares the written times separately.
- The editor's view model reads the device zone through a seam, and tests
cover a draft opened across the fall-back hour and in the spring-forward
gap.
New setting under Settings → Views → Events: "Tapping an event opens" Details (default) or Edit form. The long-press toggle moves into the same group.
- Taps in the views and in search open the editor directly for events in writable calendars. Read-only events, reminder notifications and widgets always open the details.
- Installs without write access are asked for it on the tap; refusing opens the details instead.
- The editor gets a back guard: with unsaved changes it asks to save, discard or keep editing; otherwise it closes silently.
- The editor gets Duplicate, Share and Delete in an overflow menu, so skipping the details doesn't lose them.
- If the event can't be loaded, the details screen opens and shows why.
- Each editor overlay (create, edit, duplicate/import) now has its own ViewModel. Duplicating from an edited form previously reused the edit form's state.
- Deletes from the details screen, the editor and the selection bar share one error mapping. Deleting an event that's already gone counts as deleted in the details screen and the editor.
Deviation from the issue: it asked for back to save automatically. Following the discussion in the comments, auto-save isn't in this PR. Back asks only when something actually changed, and Details stays the default.
Closes#178
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/394
- A resized edge keeps its own pass of the repeated hour, else the other
edge's, so an end pulled into the hour an event started in its second pass
no longer lands before the start.
- A drafted block opens the form at its real length rather than the end it is
drawn to, so a draft ending in the repeated hour keeps its length and one
starting in the spring-forward gap no longer ends before it starts.
- A form only keeps an offset for a time in the repeated hour, so a day move
doesn't depend on the season of the occurrence it came from and a time
changed and changed back compares equal again.
- Changing the form's zone, or going all-day, drops a kept pass.
- A drag works out the event's pass once instead of every frame.
- The form caches the pinned-zone check and the time labels again.
- Tests cover the pinned event's local times again.
On the day the clocks go back, 02:00–03:00 happens twice. Event times now
remember which of the two they mean, so moving, resizing, editing and saving
keep them in the right one.
- The week and day timeline place blocks by clock time, so events after a DST
change no longer sit an hour off.
- An event ending in the repeated hour is drawn up to the end of that hour,
and its label shows its real end time.
- Drags, drops and resizes keep the event's real length across a clock
change; a resize only changes the edge being dragged.
- A drop onto a repeated time lands on the first one, unless the event is
already in the second one.
- The editor, event details and the "local time" line add the zone
abbreviation to a repeated time so the two can be told apart.
- Saving checks and writes the real start and end times, so a span ending in
the repeated hour is valid and switching between the two is saved.
Closes#387
Long press on free time in the day, 3-day and week views drops a block of the default event length there. It can be moved while still held, or dragged and resized with its handles after letting go. The tick opens the new-event form at that span; the ✕, a tap elsewhere or back discards it. The draft is a stand-in event that reuses the #141 selection, drag and resize machinery, so nothing is written until the form saves.
Also smooths the lift handover for all selected blocks: no one-frame dip when letting go, no bounce on the handles or on a block landing after a drop or resize, and the floating copy's time label now matches the block's so the text doesn't shift on pickup.
Deviations from the issue / agreed design:
- An unresized draft opens the form with the length it shows (the global default) rather than the per-calendar default, so the form never opens at a different time than the block on screen.
- A draft dropped on a day off screen snaps back instead of moving there, since nothing would draw it.
- The in-editor timeline picker from the issue isn't included; per the issue comments it isn't needed for now.
Also moves the Gitea release/beta jobs onto the light/gradle runners.
Closes#153
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/390
The agenda opens on today, and scrolling up loads earlier days 30 at a time. Past days follow the past-events setting: Dim fades them, and with Hide the agenda still starts at today.
The agenda no longer picks up the day the month or day view was on, so "Next 30 days" always counts from today. That fixes the bug from the issue comment. It also means the agenda no longer follows the shared view date from #184 when you switch to it. Backing out of a day opened from the agenda keeps your scroll position.
Not in the issue: multi-day events that are still running now dim on the days already behind you, in the agenda, month, week and day views. Before, they stayed at full strength until the whole event ended. A spanning bar dims once the last day it covers in that row has passed.
The widget is unchanged and still starts at today.
Closes#367
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/382
When you reopened the app the next morning, the month view still marked yesterday as today and still had the day you picked the evening before selected.
- New `TodaySource` gives the current date. It rechecks once a minute while a calendar is visible, and also reacts to the system's date, time and time-zone broadcasts. Month, Week/Day/3-day, Agenda and the shared date that every view opens on all use it.
- On a day change, a view still showing the old today moves to the new one, and Split clears the previous day's selection. A view you navigated elsewhere stays where it is.
- Month and Week take today from the live value instead of their cached page data.
- The issue only mentions the month view. The same stale today also affected Week, Day, Agenda and switching between views, so all of them are fixed.
- versionName 2.22.5 / versionCode 2220599, CHANGELOG, What's New in all 17 locales.
Closes#373
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/381
Promotes 2.22.4-beta.1 to stable. The app code is the same as the beta.
- versionName 2.22.4, versionCode 2220499 (new scheme)
- CHANGELOG.md: the Unreleased entries move under [2.22.4]
- What's New `changelogs/2220499.txt` in all 17 store locales
Includes #370 (re-query after own writes and on return to foreground) and #371 (widgets load only what they show).
Closes#363Closes#364
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/374
### What this changes
Adds beta releases, ported from Agendula (#38 and #40 there). Pushing a `release/*` branch whose `versionName` is `X.Y.Z-beta.N` runs the new `.gitea/workflows/beta.yaml`: unit tests, build + sign with the app key, then a **Codeberg pre-release** (APK + `.sha256`) and a Gitea pre-release (R8 mapping). F-Droid (self-hosted and official) and Play never get a beta; Obtainium only offers it with *Include prereleases* on.
- **New versionCode scheme**, derived in one place by `scripts/version_info.sh`. 2.22.3 is the last legacy version (`X*10000 + Y*100 + Z`); from **2.22.4** on it is `X*1000000 + Y*10000 + Z*100 + N` for a beta (N = 1–98) and `+ 99` for stable, so `2.22.4` → `2220499`, `2.23.0-beta.1` → `2230001`.
- **`scripts/release_gate.sh`** decides in both `detect` jobs whether the version still needs publishing. Tags are read by exact name via `git ls-remote`: Codeberg's `git/refs/tags/<name>` matches by prefix, so a beta tag would otherwise hide its stable release. A beta counts as done only once its Codeberg pre-release carries the APK (a failed publish is redone by the next push) and must be newer than the latest stable.
- **Shared scripts** `publish_codeberg_release.sh`, `publish_gitea_release.sh`, `release_notes.sh`, `write_keystore.sh`, and a local composite action `.gitea/actions/android-env` for the toolchain setup, used by both `release.yaml` and `beta.yaml`. The stable path behaves as before (Codeberg step stays best-effort).
- **Guards:** CI fails a PR whose `versionCode` doesn't match its `versionName`, or that brings a beta into `main`; `release.yaml` refuses a beta as a backstop; betas get no store What's New (`sync_changelog_to_fastlane.sh`, `check_changelog_lengths.sh`).
- **Docs:** versionCode table and "Cutting a beta" in `docs/RELEASING.md`, the Obtainium note in the README, `build.gradle.kts` comment.
- `gradle/gradle-daemon-jvm.properties` now points at JetBrains' own JBR 21.0.11 downloads instead of foojay, which dropped JetBrains 21 from its index (the pinned ids return 400, so a clean runner can't provision the daemon JVM).
### Why
To ship test builds of an upcoming version to opted-in testers before the stable release, without them reaching F-Droid or Play users.
Infra-only, so this targets `main` directly; no version bump. When cutting 2.22.4, its What's New file is `changelogs/2220499.txt`.
### Checklist
- [x] Targeting `main` (infra change, noted above)
- [x] No `values-*/strings.xml` touched
- [x] `CHANGELOG.md` not updated: release infrastructure, not a user-visible change
- [x] No planning or design documents committed
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/369
Patch release for #361. No app changes.
- `title.txt` in every store locale is now "Calendula: Calendar", with "Calendar" taken from the app's own translation of the word (`event_detail_calendar`). Since 2.22.0 the Play listing is pushed from the repo, and the repo title was only "Calendula", which replaced the suffixed name from the Play Console.
- ja has no translation for that string yet, so it uses カレンダー. zh-CN has "Calendula" as its translation of "Calendar" on Weblate, so the title uses 日历 instead. That string should be fixed on Weblate.
- F-Droid reads the same `title.txt`, so the F-Droid listing gets the suffix too.
Plus the release commit: CHANGELOG section, versionName 2.22.3 / versionCode 22203, and the 22203 changelogs for every store locale. Merging this to main triggers the release pipeline.
Closes#361
Patch release for #359. No app changes.
- The Dutch store listing moved from `fastlane/metadata/android/nl` to `nl-NL`. Play rejects plain `nl`, so the Play upload of 2.22.0 and 2.22.1 failed with `nl - Invalid request`. F-Droid accepts either code.
- `check_store_listing.py` now checks every store code in `store-locales.txt` against the languages Play accepts, so CI catches this before a release.
- The 22202 "What's New" covers the 2.22 highlights, since Play users go straight from 2.21.0 to 2.22.2, and says the app is the same as 2.22.1.
Closes#359
Patch release for #357.
- Tapping a meeting link in an event's location opened it in a maps app. Links in the location are now underlined and open in the browser, the same way description links do. Tapping the rest of the location still opens maps, so a location with both an address and a link works for both.
- Link detection moved into `WebLinks.kt` with unit tests. It now matches a capitalised scheme (`Https://`), lower-cases the scheme before launching, and leaves `<…>` out of the URL (the Outlook/Teams style). This applies to description links too.
- Links open with `CATEGORY_BROWSABLE` through one helper. A failed launch shows a "No app can open this link" toast instead of doing nothing or crashing on a `SecurityException`.
- The location's maps tap has an "Open in maps" accessibility label (new `onTapLabel` on `copyOnLongPress`).
The issue only asked for links to open in the browser. Inline links instead of switching the whole tap target came out of review: they keep the address reachable when a location holds both.
Plus the release commit: CHANGELOG section, versionName 2.22.1 / versionCode 22201, and the 22201 changelogs for every store locale. Merging this to main triggers the release pipeline.
Closes#357
Minor release rounding up the 2.21.0 milestone.
- Setup makes sure there is a calendar to write to: it offers to create a local calendar or add a syncing account when the device has none, and lists switched-off calendars so they can be turned back on (#287).
- The month grid gives its days more room: smaller week numbers in a gutter measured from the widest number, less chip padding, tighter label tracking (#189, #190, #212, #213).
- A month day shows as many event chips as its week row has height for, instead of a flat three.
- Split view draws only the rows its month spans instead of padding to six (#162).
- Short events show their title in week and day view (#289).
- A fully zoomed-out timeline no longer scrolls by its rounding leftover (#290).
- Backup & restore has loading, failure and empty states instead of a blank page (#304).
- Untitled events and unnamed calendars use the translated placeholder instead of the hardcoded German one, which the calendar editor could also save into the calendar (#321, #329).
Plus the release commit: CHANGELOG section, versionName 2.21.0 / versionCode 22100, and the en-US 22100 changelog. Merging this to main triggers the release pipeline.
Closes#287Closes#189Closes#190Closes#212Closes#213Closes#162Closes#289Closes#290Closes#304Closes#321Closes#329
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/332
Patch release rounding up the three issues in the 2.20.4 milestone.
- An event opened from a reminder or a home-screen widget came back on every
later launch — an error screen once that event had been deleted. Android
hands back the intent that started the task each time the activity is
rebuilt, and the event channels acted on it every time instead of once
(#309).
- "Today" did nothing in the Agenda once you had scrolled ahead: the agenda is
browsed by scrolling, not by moving its date, so resetting the date alone
changed nothing. It now scrolls the list back, and the button stays offered
while the list is scrolled away (#305).
- The self-hosted F-Droid repo filed the app under "Time" instead of
"Calendar & Agenda" (#294).
Plus the release commit: CHANGELOG section, versionName 2.20.4 / versionCode
22004, and the en-US 22004 changelog. Merging this to main triggers the release
pipeline.
Closes#309Closes#305Closes#294
Co-authored-by: Jean-Luc Makiola <business@jeanlucmakiola.de>
Reviewed-on: https://codeberg.org/jlmakiola/calendula/pulls/313