The com.codename1.calendar package provides scheduling rather than just an
"add event" shortcut. It represents calendars, events, task/reminders,
recurrence, attendees, alarms, free/busy intervals, attachments, conference
links, paging, incremental synchronization, and explicit edit conflicts.
The API is capability based. Don’t branch on the platform name. Ask the source what it can do:
LocalCalendarSource local = LocalCalendarSource.getInstance();
CalendarCapabilities capabilities = local.getCapabilities();
if (capabilities.supports(CalendarCapability.READ_EVENTS)) {
local.requestAuthorization(CalendarAccess.EVENTS_READ_ONLY).ready(status -> {
if (status == CalendarAuthorizationStatus.FULL) {
local.queryEvents(new CalendarQuery()
.setCalendarId("primary")
.setStartTime(Instant.now()))
.ready(page -> page.getItems().forEach(System.out::println));
}
});
}
LocalCalendarSource is implemented with Android’s Calendar Provider and
Apple EventKit on iOS and macOS. The simulator supplies an isolated,
in-memory source so tests never modify the developer’s real calendar. A port
that has no local calendar service returns an empty capability set. Online
sources and .ics import/export remain available on every port, including
JavaScript and Linux.
| Target | Built-in local source | Portable alternatives |
|---|---|---|
Android | Calendar Provider events | Google, Microsoft, CalDAV, and |
iOS and macOS | EventKit events and reminders | Google, Microsoft, CalDAV, and |
Codename One simulator | Isolated in-memory source | Google, Microsoft, CalDAV, and |
Win32, native macOS desktop, Linux, and JavaScript | No built-in local source; the capability set is empty | Google, Microsoft, CalDAV, and |
This API is a clean replacement for the old CN1Calendar integration. New
code shouldn’t mix objects or identifiers from the two APIs.
Creating an event
All-day values use CalendarDateTime.allDay(LocalDate). Timed values use
ZonedDateTime (or the Instant and ZoneId convenience factory). Alarms use
Duration, and query ranges and absolute timestamps use Instant. The API
uses java.time instead of defining parallel date, time-zone, or duration
types. Keeping all-day and timed values separate avoids shifting an
all-day event when it crosses a time-zone boundary.
CalendarEvent event = new CalendarEvent()
.setCalendarId(calendarId)
.setTitle("Architecture review")
.setStart(CalendarDateTime.instant(start, ZoneId.of("Europe/Paris")))
.setEnd(CalendarDateTime.instant(end, ZoneId.of("Europe/Paris")))
.setRecurrence(new CalendarRecurrenceRule()
.setFrequency(CalendarRecurrenceRule.Frequency.WEEKLY)
.addDayOfWeek(2))
.addAttendee(new CalendarAttendee()
.setName("Ari")
.setEmail("[email protected]"))
.addAlarm(new CalendarAlarm().setTimeBefore(Duration.ofMinutes(15)));
local.saveEvent(event, CalendarMutationScope.ALL).ready(saved -> {
Log.p("Created " + saved.getId());
});
The version returned by a source is an optimistic-concurrency token. Pass
the returned object to saveEvent() or its version to a delete method.
A stale token produces CalendarError.CONFLICT instead of overwriting a
newer server edit.
Online sources
Online sources work on all ports and use the application’s OAuth client. They never embed a client secret and never persist credentials implicitly.
OidcClient.discover("https://accounts.google.com").ready(oidc -> {
oidc.setClientId(clientId)
.setRedirectUri(redirectUri)
.setScopes(GoogleCalendarSource.SCOPE_CALENDAR,
GoogleCalendarSource.SCOPE_TASKS)
.authorize().ready(tokens -> {
CalendarSource google = new GoogleCalendarSource(
new OidcCalendarTokenProvider(oidc, tokens));
CalendarManager.getInstance().registerSource(google);
});
});
GoogleCalendarSource covers Google Calendar and Google Tasks.
MicrosoftCalendarSource covers Microsoft Graph calendars and Microsoft To
Do. Both expose provider paging and delta tokens through CalendarPage and
retry once with a refreshed token after HTTP 401.
For CalDAV, pass the account’s calendar-home URL and an authentication strategy:
CalendarSource caldav = new CalDavCalendarSource(
"work", "Work CalDAV", calendarHomeUrl,
CalDavAuthentication.digest(username, password));
CalDavAuthentication.basic(), bearer(), and digest() are supported.
Prefer Bearer or Digest over Basic unless Basic is protected by HTTPS. Calendar
discovery filters collections by their advertised VEVENT and VTODO
component support. Check the result of each asynchronous operation as servers
can restrict individual collections further.
Import and export
ICalendarCodec reads and writes RFC 5545 VEVENT, VTODO, VALARM,
attendees, recurrence rules, URI attachments, conference URIs, all-day values,
and time zones. Unknown X- properties are retained in provider data.
String ics = ICalendarCodec.writeEvent(event);
CalendarEvent imported = ICalendarCodec.readEvent(ics);
Change notifications and offline synchronization
The cross-platform guarantee is intentionally pull based:
Local sources can emit
CalendarChangethroughaddChangeListener()when the operating system reports a local store change.Online providers return a sync/delta token. Store it and pass it in the next
CalendarQuery. The API doesn’t register provider webhooks.
Listeners run on the Codename One EDT. Treat a RESET change as an instruction
to query again; it doesn’t promise one callback per underlying OS edit.
Offline mutation storage is opt-in. Supply a CalendarCache to
CalendarSyncEngine, queue edits, and call sync() yourself or from the
platform’s background-work callback. StorageCalendarCache stores calendar
data and pending mutations, never credentials. Conflicts remain paused until
the application chooses KEEP_LOCAL, KEEP_REMOTE, or MERGED.
CalendarSyncEngine sync = new CalendarSyncEngine(
google, new StorageCalendarCache("google-account-1"));
sync.queueEventSave(event, CalendarMutationScope.ALL);
sync.sync().ready(result -> {
for (CalendarConflict conflict : result.getConflicts()) {
// Present both versions, then call resolveConflict(...).
}
});
Permissions and build hints
Calling LocalCalendarSource.getInstance(),
CalendarManager.getLocalSource(), CalendarManager.getSources(), or
Display.getLocalCalendarSource() is the builder signal for local calendar
integration. Android builds add
READ_CALENDAR and WRITE_CALENDAR; iOS and macOS builds add EventKit
and calendar/reminder privacy strings. You can replace the default user-facing
strings with:
ios.NSCalendarsFullAccessUsageDescription=Explain why events are needed ios.NSCalendarsWriteOnlyAccessUsageDescription=Explain why creating events is needed ios.NSRemindersFullAccessUsageDescription=Explain why reminders are needed
The built-in Win32 source currently reports unavailable. An
application-supplied native integration can explicitly opt into the standard
MSIX uap:appointments capability with:
windows.msix=true windows.calendar.restrictedCapability=true
Always check getCapabilities() and getAuthorizationStatus() at runtime.
Manifest declarations make a request possible; they don’t mean the user or
provider granted it.