Skip to content

Commit

Permalink
sync upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
EddyVerbruggen committed Nov 1, 2016
2 parents a3ac7f4 + 63c6bae commit 3b79959
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 51 deletions.
3 changes: 1 addition & 2 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
demo
test
demo
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# PhoneGap Calendar plugin
# PhoneGap Calendar plugin

for iOS and Android, by [Eddy Verbruggen](http://www.x-services.nl)

Expand Down Expand Up @@ -37,7 +37,11 @@ This plugin allows you to add events to the Calendar of the mobile device.
* Supported methods: `find`, `create`, `modify`, `delete`, ..
* All methods work without showing the native calendar. Your app never loses control.
* Tested on iOS 6+.
* On iOS 10+ you need to provide a reason to the user for Calendar access. This plugin adds an empty `NSCalendarsUsageDescription` key to the /platforms/ios/*-Info.plist file which you can override with your custom string [per Apple's guideline](https://developer.apple.com/library/prerelease/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW15).
* On iOS 10+ you need to provide a reason to the user for Calendar access. This plugin adds an empty `NSCalendarsUsageDescription` key to the /platforms/ios/*-Info.plist file which you can override with your custom string. To do so, pass the following variable when installing the plugin:

```
cordova plugin add cordova-plugin-calendar --variable CALENDAR_USAGE_DESCRIPTION="This app uses your calendar"
```

### Android specifics
* Supported methods on Android 4: `find`, `create` (silent and interactive), `delete`, ..
Expand Down Expand Up @@ -137,11 +141,11 @@ findEvent | | yes | yes
findEventWithOptions | | yes | yes
listEventsInRange | | | yes
listCalendars | | yes | yes
findAllEventsInNamedCalendars | | yes |
modifyEvent | | yes |
modifyEventWithOptions | | yes |
findAllEventsInNamedCalendars | | yes |
modifyEvent | | yes |
modifyEventWithOptions | | yes |
deleteEvent | | yes | yes
deleteEventFromNamedCalendar | | yes |
deleteEventFromNamedCalendar | | yes |
openCalendar | | yes | yes

Basic operations, you'll want to copy-paste this for testing purposes:
Expand All @@ -168,7 +172,7 @@ Basic operations, you'll want to copy-paste this for testing purposes:

// create an event silently (on Android < 4 an interactive dialog is shown)
window.plugins.calendar.createEvent(title,eventLocation,notes,startDate,endDate,success,error);

// create an event silently (on Android < 4 an interactive dialog is shown which doesn't use this options) with options:
var calOptions = window.plugins.calendar.getCalendarOptions(); // grab the defaults
calOptions.firstReminderMinutes = 120; // default is 60, pass in null for no reminder (alarm)
Expand Down Expand Up @@ -204,7 +208,7 @@ Basic operations, you'll want to copy-paste this for testing purposes:
// if you need to find events in a specific calendar, use this one. All options are currently ignored when finding events, except for the calendarName.
var calOptions = window.plugins.calendar.getCalendarOptions();
calOptions.calendarName = "MyCreatedCalendar"; // iOS only
calOptions.id = "D9B1D85E-1182-458D-B110-4425F17819F1"; // iOS only, get it from createEventWithOptions (if not found, we try matching against title, etc)
calOptions.id = "D9B1D85E-1182-458D-B110-4425F17819F1"; // if not found, we try matching against title, etc
window.plugins.calendar.findEventWithOptions(title,eventLocation,notes,startDate,endDate,calOptions,success,error);

// list all events in a date range (only supported on Android for now)
Expand Down
5 changes: 3 additions & 2 deletions demo/www/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ <h1>Apache Cordova</h1>
<button onclick="createCalendarEventInteractivelyWithOptions()">create event interactively with options</button><br/><br/>
<button onclick="deleteEvent()">delete event</button><br/><br/>
<button onclick="findEventWithFilter()">find event with filter</button><br/><br/>
<button onclick="findEventNoFilter()">find event no filter</button>
<button onclick="findEventNoFilter()">find event no filter</button><br/><br/>
<button onclick="listEventsInRange()">list events in range</button>
</div>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/index.js"></script>
<script type="text/javascript" src="js/demo.js"></script>
</body>
</html>
</html>
7 changes: 6 additions & 1 deletion demo/www/js/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ function findEventNoFilter() {
window.plugins.calendar.findEvent(null, null, null, startDate, endDate, onSuccess, onError);
}

function listEventsInRange() {
startDate.setHours(startDate.getHours() - 12);
window.plugins.calendar.listEventsInRange(startDate, endDate, onSuccess, onError);
}

window.onerror = function(msg, file, line) {
alert(msg + '; ' + file + '; ' + line);
};
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cordova-plugin-calendar",
"version": "4.5.2",
"version": "4.5.5",
"description": "This plugin allows you to manipulate the native calendar.",
"cordova": {
"id": "cordova-plugin-calendar",
Expand Down
9 changes: 5 additions & 4 deletions plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="nl.x-services.plugins.calendar"
version="4.5.2">
version="4.5.5">

<name>Calendar</name>

Expand All @@ -29,8 +29,8 @@
<clobbers target="Calendar"/>
</js-module>

<js-module src="test/tests.js" name="tests">
</js-module>
<!-- <js-module src="test/tests.js" name="tests">
</js-module> -->

<!-- ios -->
<platform name="ios">
Expand All @@ -50,8 +50,9 @@
</array>
</config-file>
<!-- Usage description of the Calendar for iOS 6+, mandatory since iOS 10 -->
<preference name="CALENDAR_USAGE_DESCRIPTION" default=" " />
<config-file target="*-Info.plist" parent="NSCalendarsUsageDescription">
<string/>
<string>$CALENDAR_USAGE_DESCRIPTION</string>
</config-file>
<header-file src="src/ios/Calendar.h"/>
<source-file src="src/ios/Calendar.m"/>
Expand Down
17 changes: 14 additions & 3 deletions src/android/nl/xservices/plugins/Calendar.java
Original file line number Diff line number Diff line change
Expand Up @@ -474,13 +474,16 @@ private void findEvents(JSONArray args) {

try {
final JSONObject jsonFilter = args.getJSONObject(0);
final JSONObject argOptionsObject = jsonFilter.getJSONObject("options");

cordova.getThreadPool().execute(new Runnable() {
@Override
public void run() {
JSONArray jsonEvents = getCalendarAccessor().findEvents(
getPossibleNullString("id", argOptionsObject),
getPossibleNullString("title", jsonFilter),
getPossibleNullString("location", jsonFilter),
getPossibleNullString("notes", jsonFilter),
jsonFilter.optLong("startTime"),
jsonFilter.optLong("endTime"));

Expand Down Expand Up @@ -523,7 +526,11 @@ public void run() {
argOptionsObject.optLong("recurrenceEndTime"),
argOptionsObject.optInt("calendarId", 1),
getPossibleNullString("url", argOptionsObject));
callback.success(createdEventID);
if (createdEventID != null) {
callback.success(createdEventID);
} else {
callback.error("Fail to create an event");
}
} catch (JSONException e) {
e.printStackTrace();
}
Expand Down Expand Up @@ -578,7 +585,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", "_id", "rrule", "rdate", "exdate", "event_id"};

//actual query
Cursor cursor = contentResolver.query(
Expand All @@ -603,7 +610,11 @@ public void run() {
i++,
new JSONObject()
.put("calendar_id", cursor.getString(cursor.getColumnIndex("calendar_id")))
.put("event_id", cursor.getString(cursor.getColumnIndex("_id")))
.put("id", cursor.getString(cursor.getColumnIndex("_id")))
.put("event_id", cursor.getString(cursor.getColumnIndex("event_id")))
.put("rrule", cursor.getString(cursor.getColumnIndex("rrule")))
.put("rdate", cursor.getString(cursor.getColumnIndex("rdate")))
.put("exdate", cursor.getString(cursor.getColumnIndex("exdate")))
.put("title", cursor.getString(cursor.getColumnIndex("title")))
.put("dtstart", cursor.getLong(cursor.getColumnIndex("begin")))
.put("dtend", cursor.getLong(cursor.getColumnIndex("end")))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ protected static class Event {
public JSONObject toJSONObject() {
JSONObject obj = new JSONObject();
try {
obj.put("id", this.id);
obj.put("id", this.eventId);
obj.putOpt("message", this.message);
obj.putOpt("location", this.location);
obj.putOpt("title", this.title);
Expand Down Expand Up @@ -150,7 +150,7 @@ protected abstract Cursor queryEventInstances(long startFrom, long startTo,
String[] projection, String selection, String[] selectionArgs,
String sortOrder);

private Event[] fetchEventInstances(String title, String location, long startFrom, long startTo) {
private Event[] fetchEventInstances(String eventId, String title, String location, String notes, long startFrom, long startTo) {
String[] projection = {
this.getKey(KeyIndex.INSTANCES_ID),
this.getKey(KeyIndex.INSTANCES_EVENT_ID),
Expand All @@ -165,21 +165,36 @@ private Event[] fetchEventInstances(String title, String location, long startFro
String selection = "";
List<String> selectionList = new ArrayList<String>();

if (title != null) {
//selection += Events.TITLE + "=?";
selection += Events.TITLE + " LIKE ?";
selectionList.add("%" + title + "%");
}
if (location != null && !location.equals("")) {
if (!"".equals(selection)) {
selection += " AND ";
if (eventId != null) {
selection += CalendarContract.Instances.EVENT_ID + " = ?";
selectionList.add(eventId);
} else {
if (title != null) {
//selection += Events.TITLE + "=?";
selection += Events.TITLE + " LIKE ?";
selectionList.add("%" + title + "%");
}
if (location != null && !location.equals("")) {
if (!"".equals(selection)) {
selection += " AND ";
}
selection += Events.EVENT_LOCATION + " LIKE ?";
selectionList.add("%" + location + "%");
}
if (notes != null && !notes.equals("")) {
if (!"".equals(selection)) {
selection += " AND ";
}
selection += Events.DESCRIPTION + " LIKE ?";
selectionList.add("%" + notes + "%");
}
selection += Events.EVENT_LOCATION + " LIKE ?";
selectionList.add("%" + location + "%");
}

String[] selectionArgs = new String[selectionList.size()];
Cursor cursor = queryEventInstances(startFrom, startTo, projection, selection, selectionList.toArray(selectionArgs), sortOrder);
if (cursor == null) {
return null;
}
Event[] instances = null;
if (cursor.moveToFirst()) {
int idCol = cursor.getColumnIndex(this.getKey(KeyIndex.INSTANCES_ID));
Expand All @@ -201,7 +216,13 @@ private Event[] fetchEventInstances(String title, String location, long startFro
i += 1;
} while (cursor.moveToNext());
}
return instances;

// if we don't find the event by id, try again by title etc - inline with iOS logic
if ((instances == null || instances.length == 0) && eventId != null) {
return fetchEventInstances(null, title, location, notes, startFrom, startTo);
} else {
return instances;
}
}

private String[] getActiveCalendarIds() {
Expand Down Expand Up @@ -355,10 +376,10 @@ private Map<String, ArrayList<Attendee>> fetchAttendeesForEventsAsMap(
return attendeeMap;
}

public JSONArray findEvents(String title, String location, long startFrom, long startTo) {
public JSONArray findEvents(String eventId, String title, String location, String notes, long startFrom, long startTo) {
JSONArray result = new JSONArray();
// Fetch events from the instance table.
Event[] instances = fetchEventInstances(title, location, startFrom, startTo);
Event[] instances = fetchEventInstances(eventId, title, location, notes, startFrom, startTo);
if (instances == null) {
return result;
}
Expand Down Expand Up @@ -388,7 +409,7 @@ public JSONArray findEvents(String title, String location, long startFrom, long

public boolean deleteEvent(Uri eventsUri, long startFrom, long startTo, String title, String location) {
ContentResolver resolver = this.cordova.getActivity().getApplicationContext().getContentResolver();
Event[] events = fetchEventInstances(title, location,startFrom, startTo);
Event[] events = fetchEventInstances(null, title, location, "", startFrom, startTo);
int nrDeletedRecords = 0;
if (events != null) {
for (Event event : events) {
Expand Down Expand Up @@ -439,12 +460,12 @@ public String createEvent(Uri eventsUri, String title, long startTime, long endT
}
}

// runtime exceptions are dealt with by the caller
Uri uri = cr.insert(eventsUri, values);
String createdEventID = uri.getLastPathSegment();
Log.d(LOG_TAG, "Created event with ID " + createdEventID);

String createdEventID = null;
try {
Uri uri = cr.insert(eventsUri, values);
createdEventID = uri.getLastPathSegment();
Log.d(LOG_TAG, "Created event with ID " + createdEventID);

if (firstReminderMinutes > -1) {
ContentValues reminderValues = new ContentValues();
reminderValues.put("event_id", Long.parseLong(uri.getLastPathSegment()));
Expand Down Expand Up @@ -506,9 +527,8 @@ public String createCalendar(String calendarName, String calendarColor) {
if (created != null) {
return created.getLastPathSegment();
}
} catch (Throwable t) {
System.err.println(t.getMessage());
t.printStackTrace();
} catch (Exception e) {
Log.e(LOG_TAG, "Creating calendar failed.", e);
}
return null;
};
Expand Down
20 changes: 14 additions & 6 deletions src/ios/Calendar.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ - (void) initEventStoreWithCalendarCapabilities {
} else { // we're on iOS 5 or older
accessGranted = YES;
}

if (accessGranted) {
self.eventStore = eventStoreCandidate;
}
Expand Down Expand Up @@ -338,10 +338,13 @@ - (NSArray*) findEKEventsWithTitle: (NSString *)title
}

- (EKCalendar*) findEKCalendar: (NSString *)calendarName {
for (EKCalendar *thisCalendar in [self.eventStore calendarsForEntityType:EKEntityTypeEvent]){
NSLog(@"Calendar: %@", thisCalendar.title);
if ([thisCalendar.title isEqualToString:calendarName]) {
return thisCalendar;
NSArray<EKCalendar *> *calendars = [self.eventStore calendarsForEntityType:EKEntityTypeEvent];
if (calendars != nil && calendars.count > 0) {
for (EKCalendar *thisCalendar in calendars) {
NSLog(@"Calendar: %@", thisCalendar.title);
if ([thisCalendar.title isEqualToString:calendarName]) {
return thisCalendar;
}
}
}
NSLog(@"No match found for calendar with name: %@", calendarName);
Expand Down Expand Up @@ -375,6 +378,7 @@ - (NSMutableArray*) eventsToDataArray: (NSArray*)matchingEvents {
NSMutableDictionary *entry = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
event.title, @"title",
event.calendar.title, @"calendar",
event.eventIdentifier, @"id",
[df stringFromDate:event.startDate], @"startDate",
[df stringFromDate:event.endDate], @"endDate",
[df stringFromDate:event.lastModifiedDate], @"lastModifiedDate",
Expand Down Expand Up @@ -406,6 +410,10 @@ - (NSMutableArray*) eventsToDataArray: (NSArray*)matchingEvents {
[entry setObject:attendees forKey:@"attendees"];
}

if (event.recurrenceRules != nil) {
// [entry setObject:event.recurrenceRules forKey:@"rrule"];
}

[entry setObject:event.calendarItemIdentifier forKey:@"id"];
[results addObject:entry];
}
Expand Down Expand Up @@ -891,7 +899,7 @@ - (void) eventEditViewController:(EKEventEditViewController*)controller didCompl
}


/* There is no distingtion between read and write access in iOS */
/* There is no distinction between read and write access in iOS */
- (void)hasReadPermission:(CDVInvokedUrlCommand*)command {
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:(self.eventStore != nil)];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
Expand Down

0 comments on commit 3b79959

Please sign in to comment.