diff --git a/README.md b/README.md index 463b8d7d..771286a9 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,95 @@ window.geofence.getWatched().then(function (geofencesJson) { }); ``` +## Frequency Limiting + +```javascript +notification: { + frequency: Number(600), //Seconds required between retriggering a fence. +} +``` + +## Using On/Off Schedule Data + +ScheduleData is an array consisting of a start and end time for each day. Array begins on Sunday. All times are local to the device. + +```javascript +notification: { + scheduleData: [ + { + "on": { + "hour": "0", + "minute": "0" + }, + "off": { + "hour": "23", + "minute": "59" + } + }, + { + "on": { + "hour": "0", + "minute": "0" + }, + "off": { + "hour": "23", + "minute": "59" + } + }, + { + "on": { + "hour": "0", + "minute": "0" + }, + "off": { + "hour": "23", + "minute": "59" + } + }, + { + "on": { + "hour": "0", + "minute": "0" + }, + "off": { + "hour": "23", + "minute": "59" + } + }, + { + "on": { + "hour": "0", + "minute": "0" + }, + "off": { + "hour": "23", + "minute": "59" + } + }, + { + "on": { + "hour": "0", + "minute": "0" + }, + "off": { + "hour": "23", + "minute": "59" + } + }, + { + "on": { + "hour": "0", + "minute": "0" + }, + "off": { + "hour": "23", + "minute": "59" + } + } + ] +} +``` + ## Listening for geofence transitions ```javascript diff --git a/plugin.xml b/plugin.xml index f2034ad2..cbba8fdd 100644 --- a/plugin.xml +++ b/plugin.xml @@ -44,10 +44,14 @@ + + - + + + diff --git a/src/android/GeoNotification.java b/src/android/GeoNotification.java index 24b13f06..a985bc35 100644 --- a/src/android/GeoNotification.java +++ b/src/android/GeoNotification.java @@ -1,5 +1,8 @@ package com.cowbell.cordova.geofence; +import android.util.Log; +import android.text.format.Time; + import com.google.android.gms.location.Geofence; import com.google.gson.annotations.Expose; @@ -14,12 +17,42 @@ public class GeoNotification { public GeoNotification() { } + + public boolean isScheduled(){ + Schedule schedule = new Schedule(this.notification.getScheduleDataJson()); + + if(schedule.week == null) + return true; + + Time time = new Time(); + time.setToNow(); + + Schedule.WeekDay day = schedule.week[time.weekDay]; + + if(day == null) + return true; + + if(time.hour >= day.on.hour && time.hour <= day.off.hour){ + if(time.minute >= day.on.minute && time.minute <= day.off.minute){ + Log.i("Geofence", "GeoFence is active."); + return true; + } + } + Log.i("Geofence", "GeoFence is inactive."); + return false; + } + + public boolean isFrequencyOk(){ + Time time = new Time(); + time.setToNow(); + return (this.notification.lastTriggered + this.notification.frequency * 1000 < time.toMillis(false)); + } public Geofence toGeofence() { return new Geofence.Builder().setRequestId(id) .setTransitionTypes(transitionType) .setCircularRegion(latitude, longitude, radius) - .setExpirationDuration(Long.MAX_VALUE).build(); + .setExpirationDuration(Geofence.NEVER_EXPIRE).build(); } public String toJson() { diff --git a/src/android/GeoNotificationManager.java b/src/android/GeoNotificationManager.java index e22942e3..2c68796a 100644 --- a/src/android/GeoNotificationManager.java +++ b/src/android/GeoNotificationManager.java @@ -8,6 +8,7 @@ import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.GoogleApiAvailability; import com.google.android.gms.location.Geofence; import com.google.android.gms.location.LocationRequest; @@ -58,14 +59,15 @@ public List getWatched() { private boolean areGoogleServicesAvailable() { // Check that Google Play services is available - int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(context); - - // If Google Play services is available - if (ConnectionResult.SUCCESS == resultCode) { - return true; - } else { - return false; - } + int resultCode = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this.context); + + + // If Google Play services is available + if (ConnectionResult.SUCCESS == resultCode) { + return true; + } else { + return false; + } } public void addGeoNotifications(List geoNotifications, diff --git a/src/android/GeofencePlugin.java b/src/android/GeofencePlugin.java index 6d4c1aa7..bc540373 100644 --- a/src/android/GeofencePlugin.java +++ b/src/android/GeofencePlugin.java @@ -128,6 +128,7 @@ private void deviceReady() { if (data == null) { Log.d(TAG, "No notifications clicked."); } else { + intent.removeExtra("geofence.notification.data"); webView.sendJavascript(js); } } diff --git a/src/android/Notification.java b/src/android/Notification.java index a1be5de6..3b56253e 100644 --- a/src/android/Notification.java +++ b/src/android/Notification.java @@ -3,6 +3,7 @@ import android.content.Context; import android.graphics.Bitmap; import android.net.Uri; +import android.text.format.Time; import com.google.gson.annotations.Expose; @@ -18,6 +19,9 @@ public class Notification { @Expose public String smallIcon = ""; @Expose public Object data; @Expose public boolean openAppOnClick; + @Expose public Object scheduleData; + @Expose public int frequency; + @Expose public long lastTriggered = 0; public void setContext(Context context) { this.context = context; @@ -66,6 +70,20 @@ public String getDataJson() { public long[] getVibrate() { return concat(new long[] {0}, vibrate); } + + public String getScheduleDataJson() { + if (this.scheduleData == null) { + return ""; + } + + return Gson.get().toJson(this.scheduleData); + } + + public void setLastTriggered(){ + Time time = new Time(); + time.setToNow(); + this.lastTriggered = time.toMillis(false); + } public String toString() { return "Notification title: " + getTitle() diff --git a/src/android/ReceiveTransitionsIntentService.java b/src/android/ReceiveTransitionsIntentService.java index 6e6a9f6a..3cf72c79 100644 --- a/src/android/ReceiveTransitionsIntentService.java +++ b/src/android/ReceiveTransitionsIntentService.java @@ -70,7 +70,11 @@ protected void onHandleIntent(Intent intent) { if (geoNotification != null) { if (geoNotification.notification != null) { - notifier.notify(geoNotification.notification); + if(geoNotification.isScheduled() && geoNotification.isFrequencyOk()){ + geoNotification.notification.setLastTriggered(); + store.setGeoNotification(geoNotification); + notifier.notify(geoNotification.notification); + } } geoNotification.transitionType = transitionType; geoNotifications.add(geoNotification); diff --git a/src/android/Schedule.java b/src/android/Schedule.java new file mode 100644 index 00000000..bc59997e --- /dev/null +++ b/src/android/Schedule.java @@ -0,0 +1,32 @@ +package com.cowbell.cordova.geofence; + +import com.google.gson.annotations.Expose; + +public class Schedule { + public class WeekDay{ + public class On{ + @Expose public int hour; + @Expose public int minute; + } + public class Off{ + @Expose public int hour; + @Expose public int minute; + } + + @Expose public On on; + @Expose public Off off; + + public WeekDay(String json){ + on = Gson.get().fromJson(json, On.class); + off = Gson.get().fromJson(json, Off.class); + } + } + + @Expose public WeekDay[] week; + + public Schedule(String json){ + week = Gson.get().fromJson(json, WeekDay[].class); + } + + +} diff --git a/src/ios/GeofencePlugin.swift b/src/ios/GeofencePlugin.swift index eab02270..64c753ad 100644 --- a/src/ios/GeofencePlugin.swift +++ b/src/ios/GeofencePlugin.swift @@ -153,6 +153,7 @@ func log(messages: [String]) { func didReceiveLocalNotification (notification: NSNotification) { log("didReceiveLocalNotification") + if UIApplication.sharedApplication().applicationState != UIApplicationState.Active { var data = "undefined" if let uiNotification = notification.object as? UILocalNotification { @@ -391,13 +392,55 @@ class GeoNotificationManager : NSObject, CLLocationManagerDelegate { if var geoNotification = store.findById(region.identifier) { geoNotification["transitionType"].int = transitionType + isScheduled(geoNotification) + isFrequencyOk(geoNotification) if geoNotification["notification"].isExists() { - notifyAbout(geoNotification) + if(isScheduled(geoNotification) && isFrequencyOk(geoNotification)){ + notifyAbout(geoNotification) + } } NSNotificationCenter.defaultCenter().postNotificationName("handleTransition", object: geoNotification.rawString(NSUTF8StringEncoding, options: [])) } } + + func isScheduled(geo: JSON) -> Bool{ + + let date = NSDate() + let components = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)?.components([NSCalendarUnit.Weekday, NSCalendarUnit.Hour, NSCalendarUnit.Minute], fromDate: date) + + if(geo["notification"]["scheduleData"][(components?.weekday)!] != nil){ + + let day = geo["notification"]["scheduleData"][((components?.weekday)!-1)] + if(((components?.hour)! as Int) >= (Int(day["on"]["hour"].string!)) && ((components?.hour)! as Int) <= (Int(day["off"]["hour"].string!))){ + if(((components?.minute)! as Int) >= (Int(day["on"]["minute"].string!)) && ((components?.minute)! as Int) <= (Int(day["off"]["minute"].string!))){ + log("GeoFence scheduled as active.") + return true + } + } + log("GeoFence scheduled as inactive.") + return false + } + log("GeoFence has no schedule today.") + return true + + } + + func isFrequencyOk(var geo: JSON) -> Bool{ + let store = GeoNotificationStore() + + if(geo["notification"]["lastTriggered"] != nil){ + if(Int(NSDate().timeIntervalSince1970) < geo["notification"]["lastTriggered"].int! + geo["notification"]["frequency"].int!){ + log("GeoFence triggered before frequency limit elapsed.") + return false + } + } + geo["notification"]["lastTriggered"] = JSON(NSDate().timeIntervalSince1970) + log("New Geo Obj: \(geo["notification"])") + store.update(geo) + + return true + } func notifyAbout(geo: JSON) { log("Creating notification")