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;
            }
        }
        /// <summary>
        /// Gets a single calendar by platform-specific ID.
        /// </summary>
        /// <param name="externalId">Platform-specific calendar identifier</param>
        /// <returns>The corresponding calendar, 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 <Calendar> GetCalendarByIdAsync(string externalId)
        {
            long calendarId = -1;

            if (!long.TryParse(externalId, out calendarId))
            {
                return(null);
            }

            return(Task.Run <Calendar>(() =>
            {
                Calendar calendar = null;
                var cursor = Query(
                    ContentUris.WithAppendedId(_calendarsUri, calendarId),
                    _calendarsProjection);

                try
                {
                    if (cursor.MoveToFirst())
                    {
                        calendar = GetCalendar(cursor);
                    }
                }
                catch (Java.Lang.Exception ex)
                {
                    throw new PlatformException(ex.Message, ex);
                }
                finally
                {
                    cursor.Close();
                }

                return calendar;
            }));
        }
        public async Task<ObservableCollection<Event>> GetEventsAsync(TripSketch.Core.Models.Calendar calendar, DateTime start, DateTime end)
        {
            var apiCalendar = new Calendars.Plugin.Abstractions.Calendar { Name = calendar.Name, ExternalID = calendar.ExternalID };
            var events = await CrossCalendars.Current.GetEventsAsync(apiCalendar, start, end).ConfigureAwait(false);

            return new ObservableCollection<Event>(events.Select(e => new Event
                {
                    Name = e.Name,
                    AllDay = e.AllDay,
                    ExternalID = e.ExternalID,
                    Start = e.Start,
                    End = e.End
                }));
        }
        /// <summary>
        /// Creates a new calendar with the specified name and optional color.
        /// (just a convenience wrapper around AddOrUpdateCalendarAsync)
        /// </summary>
        /// <param name="api">ICalendars instance to extend</param>
        /// <param name="calendarName">Calendar name</param>
        /// <param name="color">Preferred color, or null to accept default</param>
        /// <returns>The created calendar</returns>
        public static async Task<Calendar> CreateCalendarAsync(this ICalendars api, string calendarName, string color = null)
        {
            var calendar = new Calendar
            {
                Name = calendarName,
                Color = color,
                CanEditCalendar = true,
                CanEditEvents = true
            };

            await api.AddOrUpdateCalendarAsync(calendar).ConfigureAwait(false);

            return calendar;
        }
        /// <summary>
        /// Removes a calendar and all its events from the system.
        /// </summary>
        /// <param name="calendar">Calendar to delete</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> DeleteCalendarAsync(Calendar calendar)
        {
            var existing = await GetCalendarByIdAsync(calendar.ExternalID).ConfigureAwait(false);

            if (existing == null)
            {
                return(false);
            }
            else if (!existing.CanEditCalendar)
            {
                throw new ArgumentException("Cannot delete calendar (probably because it's non-local)", "calendar");
            }

            return(await Task.Run(() => Delete(_calendarsUri, long.Parse(calendar.ExternalID))).ConfigureAwait(false));
        }
        /// <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);
        }
Example #7
0
        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()));
        }
Example #8
0
        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");
        }
Example #9
0
        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;
        }
Example #11
0
        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");
        }
        /// <summary>
        /// Removes a calendar and all its events from the system.
        /// </summary>
        /// <param name="calendar">Calendar to delete</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> DeleteCalendarAsync(Calendar calendar)
        {
            if (string.IsNullOrEmpty(calendar.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)
            {
                // Perform the delete and return true
                await appCalendar.DeleteAsync().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 read-only 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>
        /// Creates a new calendar or updates the name and color of an existing one.
        /// </summary>
        /// <param name="calendar">The calendar to create/update</param>
        /// <exception cref="System.ArgumentException">Calendar 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 AddOrUpdateCalendarAsync(Calendar calendar)
        {
            await EnsureInitializedAsync().ConfigureAwait(false);

            AppointmentCalendar existingCalendar = null;

            if (!string.IsNullOrEmpty(calendar.ExternalID))
            {
                existingCalendar = await GetAndValidateLocalCalendarAsync(calendar.ExternalID).ConfigureAwait(false);
            }

            // Note: DisplayColor is read-only, we cannot set/update it.

            if (existingCalendar == null)
            {
                // Create new calendar
                //
                var appCalendar = await CreateAppCalendarAsync(calendar.Name).ConfigureAwait(false);

                calendar.ExternalID = appCalendar.LocalId;
                calendar.Color = appCalendar.DisplayColor.ToString();
            }
            else
            {
                // Edit existing calendar
                //
                existingCalendar.DisplayName = calendar.Name;

                await existingCalendar.SaveAsync().ConfigureAwait(false);
            }
        }
        /// <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;
        }
        /// <summary>
        /// Removes a calendar and all its events from the system.
        /// </summary>
        /// <param name="calendar">Calendar to delete</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> DeleteCalendarAsync(Calendar calendar)
        {
            var existing = await GetCalendarByIdAsync(calendar.ExternalID).ConfigureAwait(false);

            if (existing == null)
            {
                return false;
            }
            else if (!existing.CanEditCalendar)
            {
                throw new ArgumentException("Cannot delete calendar (probably because it's non-local)", "calendar");
            }

            return await Task.Run(() => Delete(_calendarsUri, long.Parse(calendar.ExternalID))).ConfigureAwait(false);
        }
        /// <summary>
        /// Gets all events for a calendar within the specified time range.
        /// </summary>
        /// <param name="calendar">Calendar containing events</param>
        /// <param name="start">Start of event range</param>
        /// <param name="end">End of event range</param>
        /// <returns>Calendar events</returns>
        /// <exception cref="System.ArgumentException">Calendar does not exist on device</exception>
        /// <exception cref="System.UnauthorizedAccessException">Calendar access denied</exception>
        /// <exception cref="Calendars.Plugin.Abstractions.PlatformException">Unexpected platform-specific error</exception>
        public async Task <IList <CalendarEvent> > GetEventsAsync(Calendar calendar, DateTime start, DateTime end)
        {
            var deviceCalendar = await GetCalendarByIdAsync(calendar.ExternalID).ConfigureAwait(false);

            if (deviceCalendar == null)
            {
                throw new ArgumentException("Specified calendar not found on device");
            }

            var eventsUriBuilder = CalendarContract.Instances.ContentUri.BuildUpon();

            // Note that this is slightly different from the GetEventById projection
            // due to the Instances API vs. Event 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,
                CalendarContract.Instances.EventId
            };

            ContentUris.AppendId(eventsUriBuilder, DateConversions.GetDateAsAndroidMS(start));
            ContentUris.AppendId(eventsUriBuilder, DateConversions.GetDateAsAndroidMS(end));
            var eventsUri = eventsUriBuilder.Build();
            var events    = new List <CalendarEvent>();

            await Task.Run(() =>
            {
                var cursor = Query(eventsUri, eventsProjection,
                                   string.Format("{0} = {1}", CalendarContract.Events.InterfaceConsts.CalendarId, calendar.ExternalID),
                                   null, CalendarContract.Events.InterfaceConsts.Dtstart + " ASC");

                try
                {
                    if (cursor.MoveToFirst())
                    {
                        do
                        {
                            events.Add(new CalendarEvent
                            {
                                Name        = cursor.GetString(CalendarContract.Events.InterfaceConsts.Title),
                                ExternalID  = cursor.GetString(CalendarContract.Instances.EventId),
                                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)
                            });
                        } while (cursor.MoveToNext());
                    }
                }
                catch (Java.Lang.Exception ex)
                {
                    throw new PlatformException(ex.Message, ex);
                }
                finally
                {
                    cursor.Close();
                }
            });

            return(events);
        }
        /// <summary>
        /// Creates a new calendar or updates the name and color of an existing one.
        /// </summary>
        /// <param name="calendar">The calendar to create/update</param>
        /// <exception cref="System.ArgumentException">Calendar 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 AddOrUpdateCalendarAsync(Calendar calendar)
        {
            bool updateExisting = false;
            long existingId     = -1;

            if (long.TryParse(calendar.ExternalID, out existingId))
            {
                var existingCalendar = await GetCalendarByIdAsync(calendar.ExternalID).ConfigureAwait(false);

                if (existingCalendar != null)
                {
                    if (!existingCalendar.CanEditCalendar)
                    {
                        throw new ArgumentException("Destination calendar is not writeable");
                    }

                    updateExisting = true;
                }
                else
                {
                    throw new ArgumentException("Specified calendar does not exist on device", "calendar");
                }
            }

            var values = new ContentValues();

            values.Put(CalendarContract.Calendars.InterfaceConsts.CalendarDisplayName, calendar.Name);
            values.Put(CalendarContract.Calendars.Name, calendar.Name);

            // Unlike iOS/WinPhone, Android does not automatically assign a color for us,
            // so we use our own default of blue.
            //
            int colorInt = unchecked ((int)0xFF0000FF);

            if (!string.IsNullOrEmpty(calendar.Color))
            {
                int.TryParse(calendar.Color.Trim('#'), NumberStyles.HexNumber,
                             CultureInfo.InvariantCulture, out colorInt);
            }

            values.Put(CalendarContract.Calendars.InterfaceConsts.CalendarColor, colorInt);

            if (!updateExisting)
            {
                values.Put(CalendarContract.Calendars.InterfaceConsts.CalendarAccessLevel, (int)CalendarAccess.AccessOwner);
                values.Put(CalendarContract.Calendars.InterfaceConsts.AccountName, AccountName);
                values.Put(CalendarContract.Calendars.InterfaceConsts.OwnerAccount, OwnerAccount);
                values.Put(CalendarContract.Calendars.InterfaceConsts.Visible, true);

                values.Put(CalendarContract.Calendars.InterfaceConsts.AccountType, CalendarContract.AccountTypeLocal);
            }

            await Task.Run(() =>
            {
                if (updateExisting)
                {
                    Update(_calendarsUri, existingId, values);
                }
                else
                {
                    var uri = _calendarsUri.BuildUpon()
                              .AppendQueryParameter(CalendarContract.CallerIsSyncadapter, "true")
                              .AppendQueryParameter(CalendarContract.Calendars.InterfaceConsts.AccountName, AccountName)
                              .AppendQueryParameter(CalendarContract.Calendars.InterfaceConsts.AccountType, CalendarContract.AccountTypeLocal)
                              .Build();

                    calendar.ExternalID = Insert(uri, values);

                    calendar.CanEditCalendar = true;
                    calendar.CanEditEvents   = true;
                    calendar.Color           = "#" + colorInt.ToString("x8");
                }
            }).ConfigureAwait(false);
        }
Example #19
0
        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());
        }
        /// <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>
        /// 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.
        /// </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);
                }
            });
        }
        /// <summary>
        /// Creates a new calendar or updates the name and color of an existing one.
        /// </summary>
        /// <param name="calendar">The calendar to create/update</param>
        /// <exception cref="System.ArgumentException">Calendar 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 AddOrUpdateCalendarAsync(Calendar calendar)
        {
            await RequestCalendarAccess().ConfigureAwait(false);

            EKCalendar deviceCalendar = null;

            if (!string.IsNullOrEmpty(calendar.ExternalID))
            {
                deviceCalendar = _eventStore.GetCalendar(calendar.ExternalID);

                if (deviceCalendar == null)
                {
                    throw new ArgumentException("Specified calendar does not exist on device", "calendar");
                }
            }

            if (deviceCalendar == null)
            {
                deviceCalendar = CreateEKCalendar(calendar.Name, calendar.Color);
                calendar.ExternalID = deviceCalendar.CalendarIdentifier;

                // Update color in case iOS assigned one
                calendar.Color = ColorConversion.ToHexColor(deviceCalendar.CGColor);
            }
            else
            {
                deviceCalendar.Title = calendar.Name;

                if (!string.IsNullOrEmpty(calendar.Color))
                {
                    deviceCalendar.CGColor = ColorConversion.ToCGColor(calendar.Color);
                }

                NSError error = null;
                if (!_eventStore.SaveCalendar(deviceCalendar, true, out error))
                {
                    // Without this, the eventStore will continue to return the "updated"
                    // calendar 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();

                    if (error.Domain == _ekErrorDomain && error.Code == (int)EKErrorCode.CalendarIsImmutable)
                    {
                        throw new ArgumentException(error.LocalizedDescription, new NSErrorException(error));
                    }
                    else
                    {
                        throw new PlatformException(error.LocalizedDescription, new NSErrorException(error));
                    }
                }
            }
        }
        /// <summary>
        /// Gets all events for a calendar within the specified time range.
        /// </summary>
        /// <param name="calendar">Calendar containing events</param>
        /// <param name="start">Start of event range</param>
        /// <param name="end">End of event range</param>
        /// <returns>Calendar events</returns>
        /// <exception cref="System.ArgumentException">Calendar does not exist on device</exception>
        /// <exception cref="System.UnauthorizedAccessException">Calendar access denied</exception>
        /// <exception cref="Calendars.Plugin.Abstractions.PlatformException">Unexpected platform-specific error</exception>
        public async Task<IList<CalendarEvent>> GetEventsAsync(Calendar calendar, DateTime start, DateTime end)
        {
            await EnsureInitializedAsync().ConfigureAwait(false);

            AppointmentCalendar deviceCalendar = null;

            try
            {
                deviceCalendar = await _apptStore.GetAppointmentCalendarAsync(calendar.ExternalID).ConfigureAwait(false);
            }
            catch (ArgumentException ex)
            {
                throw new ArgumentException("Specified calendar not found on device", ex);
            }

            // Not all properties are populated by default
            //
            var options = new FindAppointmentsOptions { IncludeHidden = false };
            options.FetchProperties.Add(AppointmentProperties.Subject);
            options.FetchProperties.Add(AppointmentProperties.Details);
            options.FetchProperties.Add(AppointmentProperties.StartTime);
            options.FetchProperties.Add(AppointmentProperties.Duration);
            options.FetchProperties.Add(AppointmentProperties.AllDay);

            var appointments = await deviceCalendar.FindAppointmentsAsync(start, end - start, options).ConfigureAwait(false);
            var events = appointments.Select(a => a.ToCalendarEvent()).ToList();

            return events;
        }
        /// <summary>
        /// Removes a calendar and all its events from the system.
        /// </summary>
        /// <param name="calendar">Calendar to delete</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> DeleteCalendarAsync(Calendar calendar)
        {
            if (string.IsNullOrEmpty(calendar.ExternalID))
            {
                return false;
            }

            await RequestCalendarAccess().ConfigureAwait(false);

            var deviceCalendar = _eventStore.GetCalendar(calendar.ExternalID);

            if (deviceCalendar == null)
            {
                return false;
            }

            NSError error = null;
            if (!_eventStore.RemoveCalendar(deviceCalendar, 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();
                
                throw new ArgumentException(error.LocalizedDescription, "calendar", new NSErrorException(error));
            }

            return true;
        }
 /// <summary>
 /// Not supported for Windows Store apps
 /// </summary>
 public Task<IList<CalendarEvent>> GetEventsAsync(Calendar calendar, DateTime start, DateTime end)
 {
     throw new NotSupportedException();
 }
        /// <summary>
        /// Gets all events for a calendar within the specified time range.
        /// </summary>
        /// <param name="calendar">Calendar containing events</param>
        /// <param name="start">Start of event range</param>
        /// <param name="end">End of event range</param>
        /// <returns>Calendar events</returns>
        /// <exception cref="System.ArgumentException">Calendar does not exist on device</exception>
        /// <exception cref="System.UnauthorizedAccessException">Calendar access denied</exception>
        /// <exception cref="Calendars.Plugin.Abstractions.PlatformException">Unexpected platform-specific error</exception>
        public async Task<IList<CalendarEvent>> GetEventsAsync(Calendar calendar, DateTime start, DateTime end)
        {
            await RequestCalendarAccess().ConfigureAwait(false);

            var deviceCalendar = _eventStore.GetCalendar(calendar.ExternalID);

            if (deviceCalendar == null)
            {
                throw new ArgumentException("Specified calendar not found on device");
            }

            var query = _eventStore.PredicateForEvents(start.ToNSDate(), end.ToNSDate(), new EKCalendar[] { deviceCalendar });
            var events = await Task.Run(() =>
            {
                var iosEvents = _eventStore.EventsMatching(query);
                return iosEvents == null ? new List<CalendarEvent>() : iosEvents.Select(e => e.ToCalendarEvent()).ToList();
            }).ConfigureAwait(false);

            return events;
        }
 /// <summary>
 /// Not supported for Windows Store apps
 /// </summary>
 public Task AddOrUpdateCalendarAsync(Calendar calendar)
 {
     throw new NotSupportedException();
 }
Example #29
0
        public async void Calendars_AddOrUpdateEvents_AddsEvents()
        {
            var events = new List<CalendarEvent> {
                new CalendarEvent { Name = "Bob", Description = "Bob's event", Start = DateTime.Today.AddDays(5), End = DateTime.Today.AddDays(5).AddHours(2), AllDay = false },
                new CalendarEvent { Name = "Steve", Description = "Steve's event", Start = DateTime.Today.AddDays(7), End = DateTime.Today.AddDays(8), AllDay = true },
                new CalendarEvent { Name = "Wheeee", Description = "Fun times", Start = DateTime.Today.AddDays(13), End = DateTime.Today.AddDays(15), AllDay = true }
            };
            var calendar = new Calendar { Name = _calendarName };

            await _service.AddOrUpdateCalendarAsync(calendar);

            //await _service.AddOrUpdateEventsAsync(calendar, events);
            foreach (var cev in events)
            {
                await _service.AddOrUpdateEventAsync(calendar, cev);
            }

            var eventResults = await _service.GetEventsAsync(calendar, DateTime.Today, DateTime.Today.AddDays(30));

            Assert.That(eventResults, Is.EqualTo(events).Using<CalendarEvent>(_eventComparer));

            // Extra check that DateTime.Kinds are local
            Assert.AreEqual(DateTimeKind.Local, eventResults.Select(e => e.Start.Kind).Distinct().Single());
            Assert.AreEqual(DateTimeKind.Local, eventResults.Select(e => e.End.Kind).Distinct().Single());
        }
 /// <summary>
 /// Not supported for Windows Store apps
 /// </summary>
 public Task AddOrUpdateEventAsync(Calendar calendar, CalendarEvent calendarEvent)
 {
     throw new NotSupportedException();
 }
Example #31
0
        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");
        }
 /// <summary>
 /// Not supported for Windows Store apps
 /// </summary>
 public Task<bool> DeleteCalendarAsync(Calendar calendar)
 {
     throw new NotSupportedException();
 }
Example #33
0
        public async void Calendars_AddOrUpdateEvents_UpdatesEvents()
        {
            // TODO: Test description

            var originalEvents = new List<CalendarEvent> {
                new CalendarEvent { Name = "Bob", Description = "Bob's event", Start = DateTime.Today.AddDays(5), End = DateTime.Today.AddDays(5).AddHours(2), AllDay = false },
                new CalendarEvent { Name = "Steve", Description = "Steve's event", Start = DateTime.Today.AddDays(7), End = DateTime.Today.AddDays(8), AllDay = true },
                new CalendarEvent { Name = "Wheeee", Description = "Fun times", Start = DateTime.Today.AddDays(13), End = DateTime.Today.AddDays(15), AllDay = true }
            };
            var editedEvents = new List<CalendarEvent> {
                new CalendarEvent { Name = "Bob (edited)", Description = "Bob's edited event", Start = DateTime.Today.AddDays(5).AddHours(-2), End = DateTime.Today.AddDays(5).AddHours(1), AllDay = false },
                new CalendarEvent { Name = "Steve (edited)", Description = "Steve's edited event", Start = DateTime.Today.AddDays(6), End = DateTime.Today.AddDays(7).AddHours(-1), AllDay = false },
                new CalendarEvent { Name = "Yay (edited)", Description = "Edited fun times", Start = DateTime.Today.AddDays(12), End = DateTime.Today.AddDays(13), AllDay = true }
            };
            var calendar = new Calendar { Name = _calendarName };
            var queryStartDate = DateTime.Today;
            var queryEndDate = queryStartDate.AddDays(30);

            await _service.AddOrUpdateCalendarAsync(calendar);

            //await _service.AddOrUpdateEventsAsync(calendar, originalEvents);
            foreach (var cev in originalEvents)
            {
                await _service.AddOrUpdateEventAsync(calendar, cev);
            }

            var eventResults = await _service.GetEventsAsync(calendar, queryStartDate, queryEndDate);

            Assert.That(eventResults, Is.EqualTo(originalEvents).Using<CalendarEvent>(_eventComparer));


            for (int i = 0; i < eventResults.Count; i++)
            {
                editedEvents.ElementAt(i).ExternalID = eventResults.ElementAt(i).ExternalID;
            }

            //await _service.AddOrUpdateEventsAsync(calendar, editedEvents);
            foreach (var cev in editedEvents)
            {
                await _service.AddOrUpdateEventAsync(calendar, cev);
            }

            var editedEventResults = await _service.GetEventsAsync(calendar, queryStartDate, queryEndDate);

            Assert.That(editedEventResults, Is.EqualTo(editedEvents).Using<CalendarEvent>(_eventComparer));
        }
 /// <summary>
 /// Not supported for Windows Store apps
 /// </summary>
 public Task<bool> DeleteEventAsync(Calendar calendar, CalendarEvent cev)
 {
     throw new NotSupportedException();
 }
Example #35
0
        public async void Calendars_GetEvents_NonexistentCalendarThrows()
        {
            var calendar = new Calendar { Name = "Bob", ExternalID = "42" };

            Assert.IsTrue(await _service.GetEventsAsync(calendar, DateTime.Today, DateTime.Today.AddDays(30)).ThrowsAsync<ArgumentException>(), "Exception wasn't thrown");
        }
Example #36
0
        public async Task Calendars_AddOrUpdateEvents_UpdatesEvents()
        {
            var originalEvents = new List<CalendarEvent> {
                new CalendarEvent { Name = "Bob", Description = "Bob's event", Start = DateTime.Today.AddDays(5), End = DateTime.Today.AddDays(5).AddHours(2), AllDay = false },
                new CalendarEvent { Name = "Steve", Description = "Steve's event", Start = DateTime.Today.AddDays(7), End = DateTime.Today.AddDays(8), AllDay = true },
                new CalendarEvent { Name = "Wheeee", Description = "Fun times", Start = DateTime.Today.AddDays(13), End = DateTime.Today.AddDays(15), AllDay = true }
            };
            var editedEvents = new List<CalendarEvent> {
                new CalendarEvent { Name = "Bob (edited)", Description = "Bob's edited event", Start = DateTime.Today.AddDays(5).AddHours(-2), End = DateTime.Today.AddDays(5).AddHours(1), AllDay = false },
                new CalendarEvent { Name = "Steve (edited)", Description = "Steve's edited event", Start = DateTime.Today.AddDays(6), End = DateTime.Today.AddDays(7).AddHours(-1), AllDay = false },
                new CalendarEvent { Name = "Yay (edited)", Description = "Edited fun times", Start = DateTime.Today.AddDays(12), End = DateTime.Today.AddDays(13), AllDay = true }
            };
            var calendar = new Calendar { Name = _calendarName };
            var queryStartDate = DateTime.Today;
            var queryEndDate = queryStartDate.AddDays(30);

            await _service.AddOrUpdateCalendarAsync(calendar);

            foreach (var cev in originalEvents)
            {
                await _service.AddOrUpdateEventAsync(calendar, cev);
            }

            var eventResults = await _service.GetEventsAsync(calendar, queryStartDate, queryEndDate);
            Assert.IsNotNull(eventResults);

            CollectionAssert.AreEqual((ICollection)originalEvents, (ICollection)eventResults, _eventComparer);

            for (int i = 0; i < eventResults.Count; i++)
            {
                editedEvents.ElementAt(i).ExternalID = eventResults.ElementAt(i).ExternalID;
            }

            foreach (var cev in editedEvents)
            {
                await _service.AddOrUpdateEventAsync(calendar, cev);
            }

            var editedEventResults = await _service.GetEventsAsync(calendar, queryStartDate, queryEndDate);
            Assert.IsNotNull(editedEventResults);

            CollectionAssert.AreEqual((ICollection)editedEvents, (ICollection)editedEventResults, _eventComparer);
        }