public async Task ExportEventsAsync(TripSketch.Core.Models.Calendar calendar, IList<Event> events) { var apiCalendar = new Calendars.Plugin.Abstractions.Calendar { Name = calendar.Name, ExternalID = calendar.ExternalID }; var calendarEvents = events.Select(e => new CalendarEvent { Name = e.Name, AllDay = e.AllDay, ExternalID = e.ExternalID, Start = e.Start, End = e.End }); if (string.IsNullOrEmpty(apiCalendar.ExternalID)) { await CrossCalendars.Current.AddOrUpdateCalendarAsync(apiCalendar).ConfigureAwait(false); calendar.ExternalID = apiCalendar.ExternalID; } foreach (var ev in events) { var calendarEvent = new CalendarEvent { Name = ev.Name, AllDay = ev.AllDay, ExternalID = ev.ExternalID, Start = ev.Start, End = ev.End }; await CrossCalendars.Current.AddOrUpdateEventAsync(apiCalendar, calendarEvent).ConfigureAwait(false); ev.ExternalID = calendarEvent.ExternalID; } }
public async void Calendars_AddOrUpdateEvent_UnspecifiedCalendarThrows() { var calendarEvent = new CalendarEvent { Name = "Bob", Start = DateTime.Today, End = DateTime.Today.AddHours(1) }; var calendar = new Calendar { Name = _calendarName }; Assert.IsTrue(await _service.AddOrUpdateEventAsync(calendar, calendarEvent).ThrowsAsync<ArgumentException>(), "Exception wasn't thrown"); }
public async void Calendars_AddOrUpdateEvents_StartAfterEndThrows() { var calendarEvent = new CalendarEvent { Name = "Bob", Start = DateTime.Today, End = DateTime.Today.AddDays(-1) }; var calendar = new Calendar { Name = _calendarName }; await _service.AddOrUpdateCalendarAsync(calendar); Assert.IsTrue(await _service.AddOrUpdateEventAsync(calendar, calendarEvent).ThrowsAsync<ArgumentException>(), "Exception wasn't thrown"); // Ensure that calendar still has no events Assert.That(await _service.GetEventsAsync(calendar, DateTime.Today.AddMonths(-1), DateTime.Today.AddMonths(1)), Is.Empty, "Calendar has event, even after throwing"); }
/// <summary> /// Removes an event from the specified calendar. /// </summary> /// <param name="calendar">Calendar to remove event from</param> /// <param name="calendarEvent">Event to remove</param> /// <returns>True if successfully removed</returns> /// <exception cref="System.ArgumentException">Calendar is read-only</exception> /// <exception cref="System.UnauthorizedAccessException">Calendar access denied</exception> /// <exception cref="Calendars.Plugin.Abstractions.PlatformException">Unexpected platform-specific error</exception> public async Task<bool> DeleteEventAsync(Calendar calendar, CalendarEvent calendarEvent) { if (string.IsNullOrEmpty(calendar.ExternalID) || string.IsNullOrEmpty(calendarEvent.ExternalID)) { return false; } await RequestCalendarAccess().ConfigureAwait(false); var deviceCalendar = _eventStore.GetCalendar(calendar.ExternalID); if (deviceCalendar == null) { return false; } var iosEvent = _eventStore.EventFromIdentifier(calendarEvent.ExternalID); if (iosEvent == null || iosEvent.Calendar.CalendarIdentifier != deviceCalendar.CalendarIdentifier) { return false; } NSError error = null; if (!_eventStore.RemoveEvent(iosEvent, EKSpan.ThisEvent, true, out error)) { // Without this, the eventStore may act like the remove succeeded. // (this obviously also resets any other changes, but since we own the eventStore // we can be pretty confident that won't be an issue) // _eventStore.Reset(); if (error.Domain == _ekErrorDomain && error.Code == (int)EKErrorCode.CalendarReadOnly) { throw new ArgumentException(error.LocalizedDescription, "calendar", new NSErrorException(error)); } else { throw new PlatformException(error.LocalizedDescription, new NSErrorException(error)); } } return true; }
/// <summary> /// Add new event to a calendar or update an existing event. /// Throws if Calendar ID is empty, calendar does not exist, or calendar is read-only. /// </summary> /// <param name="calendar">Destination calendar</param> /// <param name="calendarEvent">Event to add or update</param> /// <exception cref="System.ArgumentException">Calendar is not specified, does not exist on device, or is read-only</exception> /// <exception cref="System.UnauthorizedAccessException">Calendar access denied</exception> /// <exception cref="Calendars.Plugin.Abstractions.PlatformException">Unexpected platform-specific error</exception> public async Task AddOrUpdateEventAsync(Calendar calendar, CalendarEvent calendarEvent) { await RequestCalendarAccess().ConfigureAwait(false); EKCalendar deviceCalendar = null; if (string.IsNullOrEmpty(calendar.ExternalID)) { throw new ArgumentException("Missing calendar identifier", "calendar"); } else { deviceCalendar = _eventStore.GetCalendar(calendar.ExternalID); if (deviceCalendar == null) { throw new ArgumentException("Specified calendar not found on device"); } } EKEvent iosEvent = null; // If Event already corresponds to an existing EKEvent in the target // Calendar, then edit that instead of creating a new one. // if (!string.IsNullOrEmpty(calendarEvent.ExternalID)) { var existingEvent = _eventStore.EventFromIdentifier(calendarEvent.ExternalID); if (existingEvent.Calendar.CalendarIdentifier == deviceCalendar.CalendarIdentifier) { iosEvent = existingEvent; } } if (iosEvent == null) { iosEvent = EKEvent.FromStore(_eventStore); } iosEvent.Title = calendarEvent.Name; iosEvent.Notes = calendarEvent.Description; iosEvent.AllDay = calendarEvent.AllDay; iosEvent.StartDate = calendarEvent.Start.ToNSDate(); // If set to AllDay and given an EndDate of 12am the next day, EventKit // assumes that the event consumes two full days. // (whereas WinPhone/Android consider that one day, and thus so do we) // iosEvent.EndDate = calendarEvent.AllDay ? calendarEvent.End.AddMilliseconds(-1).ToNSDate() : calendarEvent.End.ToNSDate(); iosEvent.Calendar = deviceCalendar; NSError error = null; if (!_eventStore.SaveEvent(iosEvent, EKSpan.ThisEvent, out error)) { // Without this, the eventStore will continue to return the "updated" // event even though the save failed! // (this obviously also resets any other changes, but since we own the eventStore // we can be pretty confident that won't be an issue) // _eventStore.Reset(); // Technically, probably any ekerrordomain error would be an ArgumentException? // - but we don't necessarily know *which* argument (at least not without the code) // - for now, just focusing on the start > end scenario and translating the rest to PlatformException. Can always add more later. if (error.Domain == _ekErrorDomain && error.Code == (int)EKErrorCode.DatesInverted) { throw new ArgumentException(error.LocalizedDescription, new NSErrorException(error)); } else { throw new PlatformException(error.LocalizedDescription, new NSErrorException(error)); } } calendarEvent.ExternalID = iosEvent.EventIdentifier; }
/// <summary> /// Add new event to a calendar or update an existing event. /// </summary> /// <param name="calendar">Destination calendar</param> /// <param name="calendarEvent">Event to add or update</param> /// <exception cref="System.ArgumentException">Calendar is not specified, does not exist on device, or is read-only</exception> /// <exception cref="System.UnauthorizedAccessException">Calendar access denied</exception> /// <exception cref="Calendars.Plugin.Abstractions.PlatformException">Unexpected platform-specific error</exception> public async Task AddOrUpdateEventAsync(Calendar calendar, CalendarEvent calendarEvent) { if (string.IsNullOrEmpty(calendar.ExternalID)) { throw new ArgumentException("Missing calendar identifier", "calendar"); } else { // Verify calendar exists (Android actually allows using a nonexistent calendar ID...) // var deviceCalendar = await GetCalendarByIdAsync(calendar.ExternalID).ConfigureAwait(false); if (deviceCalendar == null) { throw new ArgumentException("Specified calendar not found on device"); } } // Validate times if (calendarEvent.End < calendarEvent.Start) { throw new ArgumentException("End time may not precede start time", "calendarEvent"); } bool updateExisting = false; long existingId = -1; await Task.Run(() => { if (long.TryParse(calendarEvent.ExternalID, out existingId)) { var calendarId = GetCalendarIdForEventId(calendarEvent.ExternalID); if (calendarId.HasValue && calendarId.Value.ToString() == calendar.ExternalID) { updateExisting = true; } } var eventValues = new ContentValues(); eventValues.Put(CalendarContract.Events.InterfaceConsts.CalendarId, calendar.ExternalID); eventValues.Put(CalendarContract.Events.InterfaceConsts.Title, calendarEvent.Name); eventValues.Put(CalendarContract.Events.InterfaceConsts.Description, calendarEvent.Description); eventValues.Put(CalendarContract.Events.InterfaceConsts.Dtstart, DateConversions.GetDateAsAndroidMS(calendarEvent.Start)); eventValues.Put(CalendarContract.Events.InterfaceConsts.Dtend, DateConversions.GetDateAsAndroidMS(calendarEvent.End)); eventValues.Put(CalendarContract.Events.InterfaceConsts.AllDay, calendarEvent.AllDay); eventValues.Put(CalendarContract.Events.InterfaceConsts.EventTimezone, Java.Util.TimeZone.Default.ID); if (!updateExisting) { calendarEvent.ExternalID = Insert(_eventsUri, eventValues); } else { Update(_eventsUri, existingId, eventValues); } }); }
public async void Calendars_DeleteEvent_NonexistentCalendarReturnsFalse() { // Create and delete a calendar and event so that we have valid IDs for nonexistent calendar/event // var calendar = await _service.CreateCalendarAsync(_calendarName); var calendarEvent = new CalendarEvent { Name = "Bob", Start = DateTime.Now.AddDays(5), End = DateTime.Now.AddDays(5).AddHours(2), AllDay = false }; await _service.AddOrUpdateEventAsync(calendar, calendarEvent); await _service.DeleteCalendarAsync(calendar); Assert.IsFalse(await _service.DeleteEventAsync(calendar, calendarEvent)); }
public async void Calendars_AddOrUpdateEvents_CopiesEventsBetweenCalendars() { var calendarEvent = new CalendarEvent { Name = "Bob", Start = DateTime.Today.AddDays(5), End = DateTime.Today.AddDays(5).AddHours(2), AllDay = false }; var calendarSource = new Calendar { Name = _calendarName }; var calendarTarget = new Calendar { Name = _calendarName + " copy destination" }; await _service.AddOrUpdateCalendarAsync(calendarSource); await _service.AddOrUpdateCalendarAsync(calendarTarget); await _service.AddOrUpdateEventAsync(calendarSource, calendarEvent); var sourceEvents = await _service.GetEventsAsync(calendarSource, DateTime.Today, DateTime.Today.AddDays(30)); //await _service.AddOrUpdateEventsAsync(calendarTarget, sourceEvents); foreach (var cev in sourceEvents) { await _service.AddOrUpdateEventAsync(calendarTarget, cev); } var targetEvents = await _service.GetEventsAsync(calendarTarget, DateTime.Today, DateTime.Today.AddDays(30)); // Requery source events, just to be extra sure sourceEvents = await _service.GetEventsAsync(calendarSource, DateTime.Today, DateTime.Today.AddDays(30)); // Make sure the events are the same... Assert.That(targetEvents, Is.EqualTo(sourceEvents).Using<CalendarEvent>(_eventComparer)); // ...except for their IDs! (i.e., they are actually unique copies) Assert.That(targetEvents.Select(e => e.ExternalID).ToList(), Is.Not.EqualTo(sourceEvents.Select(e => e.ExternalID).ToList())); }
public async Task Calendars_AddOrUpdateEvents_CopiesEventsBetweenCalendars() { var calendarEvent = new CalendarEvent { Name = "Bob", Start = DateTime.Today.AddDays(5), End = DateTime.Today.AddDays(5).AddHours(2), AllDay = false }; var calendarSource = new Calendar { Name = _calendarName }; var calendarTarget = new Calendar { Name = _calendarName + " copy destination" }; await _service.AddOrUpdateCalendarAsync(calendarSource); await _service.AddOrUpdateCalendarAsync(calendarTarget); await _service.AddOrUpdateEventAsync(calendarSource, calendarEvent); var sourceEvents = await _service.GetEventsAsync(calendarSource, DateTime.Today, DateTime.Today.AddDays(30)); await _service.AddOrUpdateEventAsync(calendarTarget, calendarEvent); var targetEvents = await _service.GetEventsAsync(calendarTarget, DateTime.Today, DateTime.Today.AddDays(30)); // Requery source events, just to be extra sure sourceEvents = await _service.GetEventsAsync(calendarSource, DateTime.Today, DateTime.Today.AddDays(30)); // Make sure the events are the same... CollectionAssert.AreEqual((ICollection)sourceEvents, (ICollection)targetEvents, _eventComparer); // ...except for their IDs! (i.e., they are actually unique copies) CollectionAssert.AreNotEqual(sourceEvents.Select(e => e.ExternalID).ToList(), targetEvents.Select(e => e.ExternalID).ToList()); }
public async Task Calendars_AddOrUpdateEvent_ReadonlyCalendarThrows() { var calendarEvent = new CalendarEvent { Name = "Bob", Start = DateTime.Today, End = DateTime.Today.AddHours(1) }; var calendars = await _service.GetCalendarsAsync(); var readonlyCalendars = calendars.Where(c => !c.CanEditEvents).ToList(); var readonlyCalendar = readonlyCalendars.First(); // TODO: Handle more specific exception Assert.IsTrue(await _service.AddOrUpdateEventAsync(readonlyCalendar, calendarEvent).ThrowsAsync<ArgumentException>(), "Exception wasn't thrown"); }
/// <summary> /// Not supported for Windows Store apps /// </summary> public Task<bool> DeleteEventAsync(Calendar calendar, CalendarEvent cev) { throw new NotSupportedException(); }
/// <summary> /// Not supported for Windows Store apps /// </summary> public Task AddOrUpdateEventAsync(Calendar calendar, CalendarEvent calendarEvent) { throw new NotSupportedException(); }
/// <summary> /// Removes an event from the specified calendar. /// </summary> /// <param name="calendar">Calendar to remove event from</param> /// <param name="calendarEvent">Event to remove</param> /// <returns>True if successfully removed</returns> /// <exception cref="System.ArgumentException">Calendar is read-only</exception> /// <exception cref="System.UnauthorizedAccessException">Calendar access denied</exception> /// <exception cref="Calendars.Plugin.Abstractions.PlatformException">Unexpected platform-specific error</exception> public async Task<bool> DeleteEventAsync(Calendar calendar, CalendarEvent calendarEvent) { if (string.IsNullOrEmpty(calendar.ExternalID) || string.IsNullOrEmpty(calendarEvent.ExternalID)) { return false; } await EnsureInitializedAsync().ConfigureAwait(false); bool deleted = false; //var appCalendar = await _localApptStore.GetAppointmentCalendarAsync(calendar.ExternalID).ConfigureAwait(false); var appCalendar = await GetLocalCalendarAsync(calendar.ExternalID).ConfigureAwait(false); if (appCalendar != null) { // Verify that event actually exists on calendar // var appt = await appCalendar.GetAppointmentAsync(calendarEvent.ExternalID).ConfigureAwait(false); // The second check is because AppointmentCalendar.GetAppointmentAsync will apparently still // return events if they are associated with a different calendar... // if (appt != null && appt.CalendarId == appCalendar.LocalId) { // Sometimes DeleteAppointmentAsync throws UnauthorizedException if the event doesn't exist? // And sometimes it just fails silently? // Well, hopefully the above check will help avoid either case... // await appCalendar.DeleteAppointmentAsync(calendarEvent.ExternalID).ConfigureAwait(false); deleted = true; } } else { // Check for calendar from non-local appt store // If we get it from there, then error that it's not writeable // else, it just doesn't exist, so return false appCalendar = await _apptStore.GetAppointmentCalendarAsync(calendar.ExternalID).ConfigureAwait(false); if (appCalendar != null) { throw new ArgumentException("Cannot delete event from readonly calendar", "calendar"); } } return deleted; }
/// <summary> /// Add new event to a calendar or update an existing event. /// Throws if Calendar ID is empty, calendar does not exist, or calendar is read-only. /// </summary> /// <param name="calendar">Destination calendar</param> /// <param name="calendarEvent">Event to add or update</param> /// <exception cref="System.ArgumentException">Calendar is not specified, does not exist on device, or is read-only</exception> /// <exception cref="System.UnauthorizedAccessException">Calendar access denied</exception> /// <exception cref="Calendars.Plugin.Abstractions.PlatformException">Unexpected platform-specific error</exception> public async Task AddOrUpdateEventAsync(Calendar calendar, CalendarEvent calendarEvent) { await EnsureInitializedAsync().ConfigureAwait(false); AppointmentCalendar appCalendar = null; if (string.IsNullOrEmpty(calendar.ExternalID)) { throw new ArgumentException("Missing calendar identifier", "calendar"); } else { appCalendar = await GetAndValidateLocalCalendarAsync(calendar.ExternalID).ConfigureAwait(false); } Appointment appt = null; // If Event already corresponds to an existing Appointment in the target // Calendar, then edit that instead of creating a new one. // if (!string.IsNullOrEmpty(calendarEvent.ExternalID)) { var existingAppt = await _localApptStore.GetAppointmentAsync(calendarEvent.ExternalID); if (existingAppt != null && existingAppt.CalendarId == appCalendar.LocalId) { appt = existingAppt; } } if (appt == null) { appt = new Appointment(); } appt.Subject = calendarEvent.Name; appt.Details = calendarEvent.Description ?? string.Empty; appt.StartTime = calendarEvent.Start; appt.Duration = calendarEvent.End - calendarEvent.Start; appt.AllDay = calendarEvent.AllDay; await appCalendar.SaveAppointmentAsync(appt); calendarEvent.ExternalID = appt.LocalId; }
/// <summary> /// Removes an event from the specified calendar. /// </summary> /// <param name="calendar">Calendar to remove event from</param> /// <param name="calendarEvent">Event to remove</param> /// <returns>True if successfully removed</returns> /// <exception cref="System.ArgumentException">Calendar is read-only</exception> /// <exception cref="System.UnauthorizedAccessException">Calendar access denied</exception> /// <exception cref="Calendars.Plugin.Abstractions.PlatformException">Unexpected platform-specific error</exception> public async Task<bool> DeleteEventAsync(Calendar calendar, CalendarEvent calendarEvent) { long existingId = -1; // Even though a Calendar was passed-in, we get this to both verify // that the calendar exists and to make sure we have accurate permissions // (rather than trusting the permissions that were passed to us...) // var existingCal = await GetCalendarByIdAsync(calendar.ExternalID).ConfigureAwait(false); if (existingCal == null) { return false; } else if (!existingCal.CanEditEvents) { throw new ArgumentException("Cannot delete event from readonly calendar", "calendar"); } if (long.TryParse(calendarEvent.ExternalID, out existingId)) { return await Task.Run<bool>(() => { var calendarId = GetCalendarIdForEventId(calendarEvent.ExternalID); if (calendarId.HasValue && calendarId.Value.ToString() == calendar.ExternalID) { var eventsUri = CalendarContract.Events.ContentUri; return Delete(eventsUri, existingId); } return false; }); } return false; }
public async void Calendars_AddOrUpdateEvent_NonexistentCalendarThrows() { var calendarEvent = new CalendarEvent { Name = "Bob", Start = DateTime.Today, End = DateTime.Today.AddHours(1) }; var calendar = new Calendar { Name = _calendarName }; // Create/delete calendar so we have a valid ID for a nonexistent calendar // await _service.AddOrUpdateCalendarAsync(calendar); await _service.DeleteCalendarAsync(calendar); Assert.IsTrue(await _service.AddOrUpdateEventAsync(calendar, calendarEvent).ThrowsAsync<ArgumentException>(), "Exception wasn't thrown"); }
public async void Calendars_AddOrUpdateEvent_ReadonlyCalendarThrows() { var calendarEvent = new CalendarEvent { Name = "Bob", Start = DateTime.Today, End = DateTime.Today.AddHours(1) }; var calendars = await _service.GetCalendarsAsync(); var readonlyCalendars = calendars.Where(c => !c.CanEditEvents).ToList(); // For iOS we can rely on the built-in read-only Birthdays calendar // var readonlyCalendar = readonlyCalendars.First(c => c.Name == "Birthdays"); Assert.IsTrue(await _service.AddOrUpdateEventAsync(readonlyCalendar, calendarEvent).ThrowsAsync<PlatformException>(), "Exception wasn't thrown"); // Ensure that calendar does not contain the event Assert.IsFalse((await _service.GetEventsAsync(readonlyCalendar, DateTime.Today.AddMonths(-1), DateTime.Today.AddMonths(1))).Any(e => e.Name == "Bob"), "Calendar contains the new event, even after throwing"); }
public async Task Calendars_DeleteEvent_NonexistentEventReturnsFalse() { var calendar = await _service.CreateCalendarAsync(_calendarName); // Create and delete an event so that we have a valid event ID for a nonexistent event // var calendarEvent = new CalendarEvent { Name = "Bob", Start = DateTime.Now.AddDays(5), End = DateTime.Now.AddDays(5).AddHours(2), AllDay = false }; await _service.AddOrUpdateEventAsync(calendar, calendarEvent); await _service.DeleteEventAsync(calendar, calendarEvent); Assert.IsFalse(await _service.DeleteEventAsync(calendar, calendarEvent)); }
public async void Calendars_DeleteEvent_DeletesExistingEvent() { var calendarEvent = new CalendarEvent { Name = "Bob", Start = DateTime.Now.AddDays(5), End = DateTime.Now.AddDays(5).AddHours(2), AllDay = false }; var calendar = await _service.CreateCalendarAsync(_calendarName); await _service.AddOrUpdateEventAsync(calendar, calendarEvent); Assert.IsTrue(await _service.DeleteEventAsync(calendar, calendarEvent)); Assert.IsNull(await _service.GetEventByIdAsync(calendarEvent.ExternalID)); }
public async void Calendars_AddOrUpdateEvents_StartAfterEndThrows() { var calendarEvent = new CalendarEvent { Name = "Bob", Start = DateTime.Today, End = DateTime.Today.AddDays(-1) }; var calendar = new Calendar { Name = _calendarName }; await _service.AddOrUpdateCalendarAsync(calendar); Assert.IsTrue(await _service.AddOrUpdateEventAsync(calendar, calendarEvent).ThrowsAsync<ArgumentException>(), "Exception wasn't thrown"); }
public async void Calendars_DeleteEvent_WrongCalendarReturnsFalse() { var calendar1 = await _service.CreateCalendarAsync(_calendarName); var calendar2 = await _service.CreateCalendarAsync(_calendarName + "2"); var calendarEvent = new CalendarEvent { Name = "Bob", Start = DateTime.Now.AddDays(5), End = DateTime.Now.AddDays(5).AddHours(2), AllDay = false }; await _service.AddOrUpdateEventAsync(calendar1, calendarEvent); Assert.IsFalse(await _service.DeleteEventAsync(calendar2, calendarEvent)); }
/// <summary> /// Gets a single calendar event by platform-specific ID. /// </summary> /// <param name="externalId">Platform-specific calendar event identifier</param> /// <returns>The corresponding calendar event, or null if not found</returns> /// <exception cref="System.UnauthorizedAccessException">Calendar access denied</exception> /// <exception cref="Calendars.Plugin.Abstractions.PlatformException">Unexpected platform-specific error</exception> public Task<CalendarEvent> GetEventByIdAsync(string externalId) { // Note that this is slightly different from the GetEvents projection // due to the Instances API vs Events API (specifically, IDs) // string[] eventsProjection = { CalendarContract.Events.InterfaceConsts.Id, CalendarContract.Events.InterfaceConsts.Title, CalendarContract.Events.InterfaceConsts.Description, CalendarContract.Events.InterfaceConsts.Dtstart, CalendarContract.Events.InterfaceConsts.Dtend, CalendarContract.Events.InterfaceConsts.AllDay }; return Task.Run<CalendarEvent>(() => { CalendarEvent calendarEvent = null; var cursor = Query( ContentUris.WithAppendedId(_eventsUri, long.Parse(externalId)), eventsProjection); try { if (cursor.MoveToFirst()) { calendarEvent = new CalendarEvent { Name = cursor.GetString(CalendarContract.Events.InterfaceConsts.Title), ExternalID = cursor.GetString(CalendarContract.Events.InterfaceConsts.Id), Description = cursor.GetString(CalendarContract.Events.InterfaceConsts.Description), Start = cursor.GetDateTime(CalendarContract.Events.InterfaceConsts.Dtstart), End = cursor.GetDateTime(CalendarContract.Events.InterfaceConsts.Dtend), AllDay = cursor.GetBoolean(CalendarContract.Events.InterfaceConsts.AllDay) }; } } catch (Java.Lang.Exception ex) { throw new PlatformException(ex.Message, ex); } finally { cursor.Close(); } return calendarEvent; }); }