diff --git a/README.md b/README.md index 2095be71..c9bddb17 100644 --- a/README.md +++ b/README.md @@ -186,8 +186,21 @@ Basic operations, you'll want to copy-paste this for testing purposes: calOptions.url = "https://www.google.com"; // on iOS the success handler receives the event ID (since 4.3.6) + //To add meeting attendees jut add a JSON object similar to the following structure to the calOptionsParamater: + /* + + { + "attendeesList": { + "ID": { + "firstName": "SOME", + "lastName": "ONE", + "emailAddress": "SOME_ONE@SOME_DOMAIN.com" + }, + } + + */ window.plugins.calendar.createEventWithOptions(title,eventLocation,notes,startDate,endDate,calOptions,success,error); - + // create an event interactively window.plugins.calendar.createEventInteractively(title,eventLocation,notes,startDate,endDate,success,error); diff --git a/src/android/nl/xservices/plugins/Calendar.java b/src/android/nl/xservices/plugins/Calendar.java index 29e2ca5b..153b9cda 100644 --- a/src/android/nl/xservices/plugins/Calendar.java +++ b/src/android/nl/xservices/plugins/Calendar.java @@ -26,9 +26,6 @@ import java.util.Date; import java.util.TimeZone; -import java.text.SimpleDateFormat; - -import static android.provider.CalendarContract.Events; public class Calendar extends CordovaPlugin { private static final String HAS_READ_PERMISSION = "hasReadPermission"; @@ -222,17 +219,15 @@ private void listCalendars() { cordova.getThreadPool().execute(new Runnable() { @Override public void run() { + JSONArray jsonObject = new JSONArray(); try { - JSONArray activeCalendars = Calendar.this.getCalendarAccessor().getActiveCalendars(); - if (activeCalendars == null) { - activeCalendars = new JSONArray(); - } - PluginResult res = new PluginResult(PluginResult.Status.OK, activeCalendars); - callback.sendPluginResult(res); + jsonObject = Calendar.this.getCalendarAccessor().getActiveCalendars(); } catch (JSONException e) { System.err.println("Exception: " + e.getMessage()); callback.error(e.getMessage()); } + PluginResult res = new PluginResult(PluginResult.Status.OK, jsonObject); + callback.sendPluginResult(res); } }); } @@ -312,19 +307,6 @@ public void run() { calIntent.putExtra("description", description); calIntent.putExtra("calendar_id", argOptionsObject.optInt("calendarId", 1)); - //set recurrence - String recurrence = getPossibleNullString("recurrence", argOptionsObject); - Long recurrenceEndTime = argOptionsObject.isNull("recurrenceEndTime") ? null : argOptionsObject.optLong("recurrenceEndTime"); - int recurrenceInterval = argOptionsObject.optInt("recurrenceInterval"); - if (recurrence != null) { - if (recurrenceEndTime == null) { - calIntent.putExtra(Events.RRULE, "FREQ=" + recurrence.toUpperCase() + ";INTERVAL=" + recurrenceInterval); - } else { - final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'hhmmss'Z'"); - calIntent.putExtra(Events.RRULE, "FREQ=" + recurrence.toUpperCase() + ";INTERVAL=" + recurrenceInterval + ";UNTIL=" + sdf.format(new Date(recurrenceEndTime))); - } - } - Calendar.this.cordova.startActivityForResult(Calendar.this, calIntent, RESULT_CODE_CREATE); } }); @@ -443,19 +425,19 @@ private void createEvent(JSONArray args) { public void run() { try { final String createdEventID = getCalendarAccessor().createEvent( - null, - getPossibleNullString("title", argObject), - argObject.getLong("startTime"), - argObject.getLong("endTime"), - getPossibleNullString("notes", argObject), - getPossibleNullString("location", argObject), - argOptionsObject.optLong("firstReminderMinutes", -1), - argOptionsObject.optLong("secondReminderMinutes", -1), - getPossibleNullString("recurrence", argOptionsObject), - argOptionsObject.optInt("recurrenceInterval"), - argOptionsObject.optLong("recurrenceEndTime"), - argOptionsObject.optInt("calendarId", 1), - getPossibleNullString("url", argOptionsObject)); + null, + getPossibleNullString("title", argObject), + argObject.getLong("startTime"), + argObject.getLong("endTime"), + getPossibleNullString("notes", argObject), + getPossibleNullString("location", argObject), + argOptionsObject.optLong("firstReminderMinutes"), + argOptionsObject.optLong("secondReminderMinutes"), + getPossibleNullString("recurrence", argOptionsObject), + argOptionsObject.optInt("recurrenceInterval"), + argOptionsObject.optLong("recurrenceEndTime"), + argOptionsObject.optInt("calendarId", 1), + getPossibleNullString("url", argOptionsObject), argOptionsObject.optJSONObject("attendees")); callback.success(createdEventID); } catch (JSONException e) { e.printStackTrace(); @@ -471,7 +453,7 @@ public void run() { private static String getPossibleNullString(String param, JSONObject from) { return from.isNull(param) || "null".equals(from.optString(param)) ? null : from.optString(param); } - + private void listEventsInRange(JSONArray args) { // note that if the dev didn't call requestReadPermission before calling this method and calendarPermissionGranted returns false, // the app will ask permission and this method needs to be invoked again (done for backward compat). @@ -481,7 +463,7 @@ private void listEventsInRange(JSONArray args) { return; } try { - final JSONObject jsonFilter = args.getJSONObject(0); + final JSONObject jsonFilter = args.getJSONObject(0); JSONArray result = new JSONArray(); long input_start_date = jsonFilter.optLong("startTime"); long input_end_date = jsonFilter.optLong("endTime"); @@ -492,7 +474,7 @@ private void listEventsInRange(JSONArray args) { } else { l_eventUri = Uri.parse("content://calendar/instances/when/" + String.valueOf(input_start_date) + "/" + String.valueOf(input_end_date)); } - + cordova.getThreadPool().execute(new Runnable() { @Override public void run() { @@ -513,7 +495,7 @@ public void run() { calendar_end.setTime(date_end); //projection of DB columns - String[] l_projection = new String[]{"calendar_id", "title", "begin", "end", "eventLocation", "allDay", "_id"}; + String[] l_projection = new String[]{"calendar_id", "title", "begin", "end", "eventLocation", "allDay"}; //actual query Cursor cursor = contentResolver.query( @@ -537,7 +519,6 @@ public void run() { i++, new JSONObject() .put("calendar_id", cursor.getString(cursor.getColumnIndex("calendar_id"))) - .put("event_id", cursor.getString(cursor.getColumnIndex("_id"))) .put("title", cursor.getString(cursor.getColumnIndex("title"))) .put("dtstart", cursor.getLong(cursor.getColumnIndex("begin"))) .put("dtend", cursor.getLong(cursor.getColumnIndex("end"))) @@ -559,7 +540,7 @@ public void run() { callback.error(e.getMessage()); } } - + public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == RESULT_CODE_CREATE) { if (resultCode == Activity.RESULT_OK || resultCode == Activity.RESULT_CANCELED) { diff --git a/src/android/nl/xservices/plugins/accessor/AbstractCalendarAccessor.java b/src/android/nl/xservices/plugins/accessor/AbstractCalendarAccessor.java index 18dcdf36..d25741e9 100644 --- a/src/android/nl/xservices/plugins/accessor/AbstractCalendarAccessor.java +++ b/src/android/nl/xservices/plugins/accessor/AbstractCalendarAccessor.java @@ -262,9 +262,6 @@ public final JSONArray getActiveCalendars() throws JSONException { }, this.getKey(KeyIndex.CALENDARS_VISIBLE) + "=1", null, null ); - if (cursor == null) { - return null; - } JSONArray calendarsWrapper = new JSONArray(); if (cursor.moveToFirst()) { do { @@ -432,7 +429,8 @@ public boolean deleteEvent(Uri eventsUri, long startFrom, long startTo, String t public String createEvent(Uri eventsUri, String title, long startTime, long endTime, String description, String location, Long firstReminderMinutes, Long secondReminderMinutes, - String recurrence, int recurrenceInterval, Long recurrenceEndTime, Integer calendarId, String url) { + String recurrence, int recurrenceInterval, Long recurrenceEndTime, Integer calendarId, String url, + JSONObject attendeesList) throws JSONException{ ContentResolver cr = this.cordova.getActivity().getContentResolver(); ContentValues values = new ContentValues(); final boolean allDayEvent = isAllDayEvent(new Date(startTime), new Date(endTime)); @@ -457,7 +455,7 @@ public String createEvent(Uri eventsUri, String title, long startTime, long endT } } values.put(Events.DESCRIPTION, description); - values.put(Events.HAS_ALARM, firstReminderMinutes > -1 || secondReminderMinutes > -1 ? 1 : 0); + values.put(Events.HAS_ALARM, (firstReminderMinutes == null && secondReminderMinutes == null) ? 0 : 1); values.put(Events.CALENDAR_ID, calendarId); values.put(Events.EVENT_LOCATION, location); @@ -465,18 +463,36 @@ public String createEvent(Uri eventsUri, String title, long startTime, long endT if (recurrenceEndTime == null) { values.put(Events.RRULE, "FREQ=" + recurrence.toUpperCase() + ";INTERVAL=" + recurrenceInterval); } else { - final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'hhmmss'Z'"); - values.put(Events.RRULE, "FREQ=" + recurrence.toUpperCase() + ";INTERVAL=" + recurrenceInterval + ";UNTIL=" + sdf.format(new Date(recurrenceEndTime))); + final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); + values.put(Events.RRULE, "FREQ=" + recurrence.toUpperCase() + ";INTERVAL=" + recurrenceInterval + ";UNTIL=" + sdf.format(new Date(recurrenceEndTime))+"T000000Z"); } } + + // runtime exceptions are dealt with by the caller Uri uri = cr.insert(eventsUri, values); String createdEventID = uri.getLastPathSegment(); + + if(attendeesList != null){ + Iterator keys = attendeesList.keys(); + values.clear(); + values.put(CalendarContract.Attendees.EVENT_ID, createdEventID); + values.put(CalendarContract.Attendees.ATTENDEE_TYPE, CalendarContract.Attendees.TYPE_REQUIRED); + + while(keys.hasNext()){ + JSONObject attendee = attendeesList.getJSONObject(keys.next().toString()); + values.put(CalendarContract.Attendees.ATTENDEE_NAME, attendee.getString("firstName") + " " + attendee.getString("lastName")); + values.put(CalendarContract.Attendees.ATTENDEE_EMAIL, attendee.getString("emailAddress")); + cr.insert(CalendarContract.Attendees.CONTENT_URI, values); + } + + } + Log.d(LOG_TAG, "Created event with ID " + createdEventID); try { - if (firstReminderMinutes > -1) { + if (firstReminderMinutes != null) { ContentValues reminderValues = new ContentValues(); reminderValues.put("event_id", Long.parseLong(uri.getLastPathSegment())); reminderValues.put("minutes", firstReminderMinutes); @@ -484,7 +500,7 @@ public String createEvent(Uri eventsUri, String title, long startTime, long endT cr.insert(Uri.parse(CONTENT_PROVIDER + CONTENT_PROVIDER_PATH_REMINDERS), reminderValues); } - if (secondReminderMinutes > -1) { + if (secondReminderMinutes != null) { ContentValues reminderValues = new ContentValues(); reminderValues.put("event_id", Long.parseLong(uri.getLastPathSegment())); reminderValues.put("minutes", secondReminderMinutes); diff --git a/src/android/nl/xservices/plugins/accessor/CalendarProviderAccessor.java b/src/android/nl/xservices/plugins/accessor/CalendarProviderAccessor.java index 691b0ce9..8d484103 100644 --- a/src/android/nl/xservices/plugins/accessor/CalendarProviderAccessor.java +++ b/src/android/nl/xservices/plugins/accessor/CalendarProviderAccessor.java @@ -40,6 +40,8 @@ import android.provider.CalendarContract.Events; import android.provider.CalendarContract.Instances; import org.apache.cordova.CordovaInterface; +import org.json.JSONException; +import org.json.JSONObject; import java.lang.Integer; import java.util.EnumMap; @@ -122,9 +124,9 @@ public boolean deleteEvent(Uri eventsUri, long startFrom, long startTo, String t public String createEvent(Uri eventsUri, String title, long startTime, long endTime, String description, String location, Long firstReminderMinutes, Long secondReminderMinutes, String recurrence, int recurrenceInterval, Long recurrenceEndTime, Integer calendarId, - String url) { + String url, JSONObject attendeesList) throws JSONException{ eventsUri = eventsUri == null ? Uri.parse(CONTENT_PROVIDER + CONTENT_PROVIDER_PATH_EVENTS) : eventsUri; return super.createEvent(eventsUri, title, startTime, endTime, description, location, - firstReminderMinutes, secondReminderMinutes, recurrence, recurrenceInterval, recurrenceEndTime, calendarId, url); + firstReminderMinutes, secondReminderMinutes, recurrence, recurrenceInterval, recurrenceEndTime, calendarId, url, attendeesList); } } diff --git a/src/android/nl/xservices/plugins/accessor/LegacyCalendarAccessor.java b/src/android/nl/xservices/plugins/accessor/LegacyCalendarAccessor.java index 27c17252..489e50aa 100644 --- a/src/android/nl/xservices/plugins/accessor/LegacyCalendarAccessor.java +++ b/src/android/nl/xservices/plugins/accessor/LegacyCalendarAccessor.java @@ -36,6 +36,8 @@ import android.net.Uri; import android.os.Build; import org.apache.cordova.CordovaInterface; +import org.json.JSONException; +import org.json.JSONObject; import java.util.EnumMap; @@ -124,10 +126,10 @@ public boolean deleteEvent(Uri eventsUri, long startFrom, long startTo, String t public String createEvent(Uri eventsUri, String title, long startTime, long endTime, String description, String location, Long firstReminderMinutes, Long secondReminderMinutes, String recurrence, int recurrenceInterval, Long recurrenceEndTime, Integer calendarId, - String url) { + String url, JSONObject attendeesList) throws JSONException{ eventsUri = eventsUri == null ? Uri.parse(CONTENT_PROVIDER_PRE_FROYO + CONTENT_PROVIDER_PATH_EVENTS) : eventsUri; return super.createEvent(eventsUri, title, startTime, endTime, description, location, - firstReminderMinutes, secondReminderMinutes, recurrence, recurrenceInterval, recurrenceEndTime, calendarId, url); + firstReminderMinutes, secondReminderMinutes, recurrence, recurrenceInterval, recurrenceEndTime, calendarId, url, attendeesList); } } diff --git a/src/ios/Calendar.m b/src/ios/Calendar.m index fb3fdfb6..2ba5df03 100644 --- a/src/ios/Calendar.m +++ b/src/ios/Calendar.m @@ -377,7 +377,6 @@ - (NSMutableArray*) eventsToDataArray: (NSArray*)matchingEvents { event.calendar.title, @"calendar", [df stringFromDate:event.startDate], @"startDate", [df stringFromDate:event.endDate], @"endDate", - [df stringFromDate:event.lastModifiedDate], @"lastModifiedDate", nil]; // optional fields if (event.location != nil) { @@ -459,6 +458,7 @@ - (void)createEventWithOptions:(CDVInvokedUrlCommand*)command { NSNumber* startTime = [options objectForKey:@"startTime"]; NSNumber* endTime = [options objectForKey:@"endTime"]; + NSDictionary* calOptions = [options objectForKey:@"options"]; NSNumber* firstReminderMinutes = [calOptions objectForKey:@"firstReminderMinutes"]; NSNumber* secondReminderMinutes = [calOptions objectForKey:@"secondReminderMinutes"]; @@ -467,6 +467,7 @@ - (void)createEventWithOptions:(CDVInvokedUrlCommand*)command { NSNumber* recurrenceIntervalAmount = [calOptions objectForKey:@"recurrenceInterval"]; NSString* calendarName = [calOptions objectForKey:@"calendarName"]; NSString* url = [calOptions objectForKey:@"url"]; + NSDictionary* attendeesList = [calOptions objectForKey:@"attendees"]; [self.commandDelegate runInBackground: ^{ EKEvent *myEvent = [EKEvent eventWithEventStore: self.eventStore]; @@ -484,7 +485,7 @@ - (void)createEventWithOptions:(CDVInvokedUrlCommand*)command { myEvent.location = location; myEvent.notes = notes; myEvent.startDate = myStartDate; - + int duration = _endInterval - _startInterval; int moduloDay = duration % (60*60*24); if (moduloDay == 0) { @@ -493,6 +494,7 @@ - (void)createEventWithOptions:(CDVInvokedUrlCommand*)command { } else { myEvent.endDate = [NSDate dateWithTimeIntervalSince1970:_endInterval]; } + EKCalendar* calendar = nil; CDVPluginResult *pluginResult = nil; @@ -545,8 +547,29 @@ - (void)createEventWithOptions:(CDVInvokedUrlCommand*)command { } [myEvent addRecurrenceRule:rule]; } + + if (attendeesList != (id)[NSNull null]) { + NSMutableArray *attendees = [NSMutableArray new]; + for(id attendee in attendeesList){ + //Initialize a EKAttendee object, which is not accessible and inherits from EKParticipant + Class className = NSClassFromString(@"EKAttendee"); + id attendeeObject = [className new]; + //Set the properties of this attendee using setValue:forKey: + [attendeeObject setValue:[[attendeesList objectForKey:attendee] objectForKey:@"firstName"] forKey:@"firstName"]; + [attendeeObject setValue:[[attendeesList objectForKey:attendee] objectForKey:@"lastName"] forKey:@"lastName"]; + [attendeeObject setValue:[[attendeesList objectForKey:attendee] objectForKey:@"emailAddress"] forKey:@"emailAddress"]; + [attendees addObject:attendeeObject]; + } + + //Finally, add the invitees to the event + [myEvent setValue:attendees forKey:@"attendees"]; + } + + NSError *error = nil; + + [self.eventStore saveEvent:myEvent span:EKSpanThisEvent error:&error]; if (error) { @@ -660,9 +683,7 @@ - (void) createEventInteractively:(CDVInvokedUrlCommand*)command { controller.event = myEvent; controller.eventStore = self.eventStore; controller.editViewDelegate = self; - dispatch_async(dispatch_get_main_queue(), ^{ - [self.viewController presentViewController:controller animated:YES completion:nil]; - }); + [self.viewController presentViewController:controller animated:YES completion:nil]; }]; }