Commit 6e0cb626 by Sebastián Katzer

WIP Android

parent 8366689e
{ {
"name": "cordova-plugin-local-notifications", "name": "cordova-plugin-local-notifications",
"version": "0.0.0", "version": "0.0.9",
"description": "Schedules and queries for local notifications", "description": "Schedules and queries for local notifications",
"cordova": { "cordova": {
"id": "cordova-plugin-local-notifications", "id": "cordova-plugin-local-notifications",
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" <plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
id="cordova-plugin-local-notifications" id="cordova-plugin-local-notifications"
version="0.0.0"> version="0.9.0">
<name>LocalNotification</name> <name>LocalNotification</name>
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
<engine name="cordova-plugman" version=">=4.3.0" /> <engine name="cordova-plugman" version=">=4.3.0" />
<engine name="cordova-windows" version=">=4.2.0" /> <engine name="cordova-windows" version=">=4.2.0" />
<engine name="apple-ios" version=">=10.0.0" /> <engine name="apple-ios" version=">=10.0.0" />
<engine name="cordova-android" version=">=6.0.0" />
</engines> </engines>
<!-- dependencies --> <!-- dependencies -->
...@@ -66,7 +67,6 @@ ...@@ -66,7 +67,6 @@
<!-- ios --> <!-- ios -->
<platform name="ios"> <platform name="ios">
<config-file target="config.xml" parent="/*"> <config-file target="config.xml" parent="/*">
<feature name="LocalNotification"> <feature name="LocalNotification">
<param name="ios-package" value="APPLocalNotification" onload="true" /> <param name="ios-package" value="APPLocalNotification" onload="true" />
...@@ -91,13 +91,11 @@ ...@@ -91,13 +91,11 @@
<header-file src="src/ios/UNNotificationRequest+APPLocalNotification.h" /> <header-file src="src/ios/UNNotificationRequest+APPLocalNotification.h" />
<source-file src="src/ios/UNNotificationRequest+APPLocalNotification.m" /> <source-file src="src/ios/UNNotificationRequest+APPLocalNotification.m" />
</platform> </platform>
<!-- android --> <!-- android -->
<!-- <platform name="android"> <platform name="android">
<framework src="com.android.support:support-v4:26.+" value="gradle" />
<framework src="com.android.support:support-v4:+" value="gradle" />
<config-file target="res/xml/config.xml" parent="/*"> <config-file target="res/xml/config.xml" parent="/*">
<feature name="LocalNotification"> <feature name="LocalNotification">
...@@ -106,7 +104,6 @@ ...@@ -106,7 +104,6 @@
</config-file> </config-file>
<config-file target="AndroidManifest.xml" parent="/manifest/application"> <config-file target="AndroidManifest.xml" parent="/manifest/application">
<receiver <receiver
android:name="de.appplant.cordova.plugin.localnotification.TriggerReceiver" android:name="de.appplant.cordova.plugin.localnotification.TriggerReceiver"
android:exported="false" /> android:exported="false" />
...@@ -118,33 +115,19 @@ ...@@ -118,33 +115,19 @@
<activity <activity
android:name="de.appplant.cordova.plugin.localnotification.ClickActivity" android:name="de.appplant.cordova.plugin.localnotification.ClickActivity"
android:launchMode="singleInstance" android:launchMode="singleInstance"
android:theme="@android:style/Theme.NoDisplay" android:theme="@android:style/Theme.Translucent"
android:exported="false" />
<receiver
android:name="de.appplant.cordova.plugin.notification.TriggerReceiver"
android:exported="false" />
<receiver
android:name="de.appplant.cordova.plugin.notification.ClearReceiver"
android:exported="false" /> android:exported="false" />
<!--
<receiver android:name="de.appplant.cordova.plugin.localnotification.RestoreReceiver" android:exported="false" > <receiver android:name="de.appplant.cordova.plugin.localnotification.RestoreReceiver" android:exported="false" >
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
-->
<activity
android:name="de.appplant.cordova.plugin.notification.ClickActivity"
android:launchMode="singleInstance"
android:theme="@android:style/Theme.NoDisplay"
android:exported="false" />
</config-file> </config-file>
<config-file target="AndroidManifest.xml" parent="/manifest"> <config-file target="AndroidManifest.xml" parent="/manifest">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <!-- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</config-file> </config-file>
...@@ -163,41 +146,57 @@ ...@@ -163,41 +146,57 @@
<source-file <source-file
src="src/android/ClearReceiver.java" src="src/android/ClearReceiver.java"
target-dir="src/de/appplant/cordova/plugin/localnotification" /> target-dir="src/de/appplant/cordova/plugin/localnotification" />
<!--
<source-file <source-file
src="src/android/RestoreReceiver.java" src="src/android/RestoreReceiver.java"
target-dir="src/de/appplant/cordova/plugin/localnotification" /> target-dir="src/de/appplant/cordova/plugin/localnotification" />
-->
<source-file
src="src/android/notification/activity/AbstractClickActivity.java"
target-dir="src/de/appplant/cordova/plugin/notification/activity" />
<source-file <source-file
src="src/android/notification/AbstractClearReceiver.java" src="src/android/notification/activity/ClickActivity.java"
target-dir="src/de/appplant/cordova/plugin/notification" /> target-dir="src/de/appplant/cordova/plugin/notification/activity" />
<source-file <source-file
src="src/android/notification/AbstractClickActivity.java" src="src/android/notification/receiver/AbstractClearReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification" /> target-dir="src/de/appplant/cordova/plugin/notification/receiver" />
<!--
<source-file
src="src/android/notification/receiver/AbstractRestoreReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification/receiver" />
-->
<source-file
src="src/android/notification/receiver/AbstractTriggerReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification/receiver" />
<source-file <source-file
src="src/android/notification/AbstractRestoreReceiver.java" src="src/android/notification/receiver/ClearReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification" /> target-dir="src/de/appplant/cordova/plugin/notification/receiver" />
<source-file <source-file
src="src/android/notification/AbstractTriggerReceiver.java" src="src/android/notification/receiver/TriggerReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification" /> target-dir="src/de/appplant/cordova/plugin/notification/receiver" />
<source-file <source-file
src="src/android/notification/AssetUtil.java" src="src/android/notification/trigger/DateTrigger.java"
target-dir="src/de/appplant/cordova/plugin/notification" /> target-dir="src/de/appplant/cordova/plugin/notification/trigger" />
<source-file <source-file
src="src/android/notification/Builder.java" src="src/android/notification/trigger/IntervalTrigger.java"
target-dir="src/de/appplant/cordova/plugin/notification" /> target-dir="src/de/appplant/cordova/plugin/notification/trigger" />
<source-file <source-file
src="src/android/notification/ClearReceiver.java" src="src/android/notification/trigger/MatchTrigger.java"
target-dir="src/de/appplant/cordova/plugin/notification" /> target-dir="src/de/appplant/cordova/plugin/notification/trigger" />
<source-file <source-file
src="src/android/notification/ClickActivity.java" src="src/android/notification/util/AssetUtil.java"
target-dir="src/de/appplant/cordova/plugin/notification/util" />
<source-file
src="src/android/notification/Builder.java"
target-dir="src/de/appplant/cordova/plugin/notification" /> target-dir="src/de/appplant/cordova/plugin/notification" />
<source-file <source-file
...@@ -213,17 +212,12 @@ ...@@ -213,17 +212,12 @@
target-dir="src/de/appplant/cordova/plugin/notification" /> target-dir="src/de/appplant/cordova/plugin/notification" />
<source-file <source-file
src="src/android/notification/TriggerReceiver.java" src="src/android/notification/Request.java"
target-dir="src/de/appplant/cordova/plugin/notification" /> target-dir="src/de/appplant/cordova/plugin/notification" />
</platform>
</platform> -->
<!-- windows --> <!-- windows -->
<platform name="windows"> <platform name="windows">
<config-file target="config.xml" parent="/*" >
<preference name="WindowsToastCapable" value="true" />
</config-file>
<framework src="src/windows/lib.UW/ARM/LocalNotificationProxy.winmd" target-dir="x86" arch="x86" custom="true"/> <framework src="src/windows/lib.UW/ARM/LocalNotificationProxy.winmd" target-dir="x86" arch="x86" custom="true"/>
<framework src="src/windows/lib.UW/ARM/LocalNotificationProxy.winmd" target-dir="x64" arch="x64" custom="true"/> <framework src="src/windows/lib.UW/ARM/LocalNotificationProxy.winmd" target-dir="x64" arch="x64" custom="true"/>
<framework src="src/windows/lib.UW/ARM/LocalNotificationProxy.winmd" target-dir="ARM" arch="ARM" custom="true"/> <framework src="src/windows/lib.UW/ARM/LocalNotificationProxy.winmd" target-dir="ARM" arch="ARM" custom="true"/>
......
...@@ -29,13 +29,12 @@ import de.appplant.cordova.plugin.notification.Notification; ...@@ -29,13 +29,12 @@ import de.appplant.cordova.plugin.notification.Notification;
* notification manually. It un-persists the cleared notification from the * notification manually. It un-persists the cleared notification from the
* shared preferences. * shared preferences.
*/ */
public class ClearReceiver extends de.appplant.cordova.plugin.notification.ClearReceiver { public class ClearReceiver extends de.appplant.cordova.plugin.notification.receiver.ClearReceiver {
/** /**
* Called when a local notification was cleared from outside of the app. * Called when a local notification was cleared from outside of the app.
* *
* @param notification * @param notification Wrapper around the local notification
* Wrapper around the local notification
*/ */
@Override @Override
public void onClear (Notification notification) { public void onClear (Notification notification) {
......
...@@ -21,22 +21,19 @@ ...@@ -21,22 +21,19 @@
package de.appplant.cordova.plugin.localnotification; package de.appplant.cordova.plugin.localnotification;
import de.appplant.cordova.plugin.notification.Builder;
import de.appplant.cordova.plugin.notification.Notification; import de.appplant.cordova.plugin.notification.Notification;
import de.appplant.cordova.plugin.notification.TriggerReceiver;
/** /**
* The receiver activity is triggered when a notification is clicked by a user. * The receiver activity is triggered when a notification is clicked by a user.
* The activity calls the background callback and brings the launch intent * The activity calls the background callback and brings the launch intent
* up to foreground. * up to foreground.
*/ */
public class ClickActivity extends de.appplant.cordova.plugin.notification.ClickActivity { public class ClickActivity extends de.appplant.cordova.plugin.notification.activity.ClickActivity {
/** /**
* Called when local notification was clicked by the user. * Called when local notification was clicked by the user.
* *
* @param notification * @param notification Wrapper around the local notification
* Wrapper around the local notification
*/ */
@Override @Override
public void onClick(Notification notification) { public void onClick(Notification notification) {
...@@ -44,24 +41,11 @@ public class ClickActivity extends de.appplant.cordova.plugin.notification.Click ...@@ -44,24 +41,11 @@ public class ClickActivity extends de.appplant.cordova.plugin.notification.Click
super.onClick(notification); super.onClick(notification);
if (notification.getOptions().isOngoing()) // if (notification.getOptions().isOngoing())
return; // return;
String event = notification.isRepeating() ? "clear" : "cancel"; // String event = notification.isRepeating() ? "clear" : "cancel";
LocalNotification.fireEvent(event, notification); // LocalNotification.fireEvent(event, notification);
}
/**
* Build notification specified by options.
*
* @param builder
* Notification builder
*/
@Override
public Notification buildNotification (Builder builder) {
return builder
.setTriggerReceiver(TriggerReceiver.class)
.build();
} }
} }
...@@ -39,6 +39,11 @@ import java.lang.Exception; ...@@ -39,6 +39,11 @@ import java.lang.Exception;
import de.appplant.cordova.plugin.notification.Manager; import de.appplant.cordova.plugin.notification.Manager;
import de.appplant.cordova.plugin.notification.Notification; import de.appplant.cordova.plugin.notification.Notification;
import de.appplant.cordova.plugin.notification.Options;
import de.appplant.cordova.plugin.notification.Request;
// import de.appplant.cordova.plugin.notification.Manager;
// import de.appplant.cordova.plugin.notification.Notification;
/** /**
* This plugin utilizes the Android AlarmManager in combination with local * This plugin utilizes the Android AlarmManager in combination with local
...@@ -129,73 +134,77 @@ public class LocalNotification extends CordovaPlugin { ...@@ -129,73 +134,77 @@ public class LocalNotification extends CordovaPlugin {
public boolean execute (final String action, final JSONArray args, public boolean execute (final String action, final JSONArray args,
final CallbackContext command) throws JSONException { final CallbackContext command) throws JSONException {
Notification.setDefaultTriggerReceiver(TriggerReceiver.class);
cordova.getThreadPool().execute(new Runnable() { cordova.getThreadPool().execute(new Runnable() {
public void run() { public void run() {
if (action.equals("schedule")) { if (action.equals("ready")) {
deviceready();
} else
if (action.equalsIgnoreCase("check")) {
check(command);
} else
if (action.equalsIgnoreCase("request")) {
request(command);
} else
if (action.equalsIgnoreCase("schedule")) {
schedule(args); schedule(args);
command.success(); command.success();
} }
else if (action.equals("update")) { // if (action.equals("update")) {
update(args); // update(args);
command.success(); // command.success();
} // } else
else if (action.equals("cancel")) { // if (action.equals("cancel")) {
cancel(args); // cancel(args);
command.success(); // command.success();
} // } else
else if (action.equals("cancelAll")) { // if (action.equals("cancelAll")) {
cancelAll(); // cancelAll();
command.success(); // command.success();
} // } else
else if (action.equals("clear")) { // if (action.equals("clear")) {
clear(args); // clear(args);
command.success(); // command.success();
} // } else
else if (action.equals("clearAll")) { // if (action.equals("clearAll")) {
clearAll(); // clearAll();
command.success(); // command.success();
} // } else
else if (action.equals("isPresent")) { // if (action.equals("isPresent")) {
isPresent(args.optInt(0), command); // isPresent(args.optInt(0), command);
} // } else
else if (action.equals("isScheduled")) { // if (action.equals("isScheduled")) {
isScheduled(args.optInt(0), command); // isScheduled(args.optInt(0), command);
} // } else
else if (action.equals("isTriggered")) { // if (action.equals("isTriggered")) {
isTriggered(args.optInt(0), command); // isTriggered(args.optInt(0), command);
} // } else
else if (action.equals("getAllIds")) { // if (action.equals("getAllIds")) {
getAllIds(command); // getAllIds(command);
} // } else
else if (action.equals("getScheduledIds")) { // if (action.equals("getScheduledIds")) {
getScheduledIds(command); // getScheduledIds(command);
} // } else
else if (action.equals("getTriggeredIds")) { // if (action.equals("getTriggeredIds")) {
getTriggeredIds(command); // getTriggeredIds(command);
} // } else
else if (action.equals("getSingle")) { // if (action.equals("getSingle")) {
getSingle(args, command); // getSingle(args, command);
} // } else
else if (action.equals("getSingleScheduled")) { // if (action.equals("getSingleScheduled")) {
getSingleScheduled(args, command); // getSingleScheduled(args, command);
} // } else
else if (action.equals("getSingleTriggered")) { // if (action.equals("getSingleTriggered")) {
getSingleTriggered(args, command); // getSingleTriggered(args, command);
} // } else
else if (action.equals("getAll")) { // if (action.equals("getAll")) {
getAll(args, command); // getAll(args, command);
} // } else
else if (action.equals("getScheduled")) { // if (action.equals("getScheduled")) {
getScheduled(args, command); // getScheduled(args, command);
} // } else
else if (action.equals("getTriggered")) { // if (action.equals("getTriggered")) {
getTriggered(args, command); // getTriggered(args, command);
} // }
else if (action.equals("deviceready")) {
deviceready();
}
} }
}); });
...@@ -203,323 +212,341 @@ public class LocalNotification extends CordovaPlugin { ...@@ -203,323 +212,341 @@ public class LocalNotification extends CordovaPlugin {
} }
/** /**
* Schedule multiple local notifications. * Ask if user has enabled permission for local notifications.
*
* @param notifications
* Properties for each local notification
*/
private void schedule (JSONArray notifications) {
for (int i = 0; i < notifications.length(); i++) {
JSONObject options = notifications.optJSONObject(i);
try {
Notification notification =
getNotificationMgr().schedule(options, TriggerReceiver.class);
fireEvent("schedule", notification);
}
catch(Exception generic) {
//silently ignore the exception
//on some samsung devices there is a known bug where a 500 alarms limit can crash the app
//http://developer.samsung.com/forum/board/thread/view.do?boardName=General&messageId=280286&listLines=15&startId=zzzzz%7E&searchSubId=0000000001
}
}
}
/**
* Update multiple local notifications.
* *
* @param updates * @param command The callback context used when calling back into
* Notification properties including their IDs * JavaScript.
*/ */
private void update (JSONArray updates) { private void check (CallbackContext command) {
for (int i = 0; i < updates.length(); i++) { boolean allowed = getNotificationMgr().hasPermission();
JSONObject update = updates.optJSONObject(i); PluginResult result = new PluginResult(PluginResult.Status.OK, allowed);
int id = update.optInt("id", 0);
Notification notification =
getNotificationMgr().update(id, update, TriggerReceiver.class);
if (notification == null)
continue;
fireEvent("update", notification);
}
}
/**
* Cancel multiple local notifications.
*
* @param ids
* Set of local notification IDs
*/
private void cancel (JSONArray ids) {
for (int i = 0; i < ids.length(); i++) {
int id = ids.optInt(i, 0);
Notification notification =
getNotificationMgr().cancel(id);
if (notification == null)
continue;
fireEvent("cancel", notification);
}
}
/**
* Cancel all scheduled notifications.
*/
private void cancelAll() {
getNotificationMgr().cancelAll();
fireEvent("cancelall");
}
/**
* Clear multiple local notifications without canceling them.
*
* @param ids
* Set of local notification IDs
*/
private void clear(JSONArray ids){
for (int i = 0; i < ids.length(); i++) {
int id = ids.optInt(i, 0);
Notification notification =
getNotificationMgr().clear(id);
if (notification == null)
continue;
fireEvent("clear", notification);
}
}
/**
* Clear all triggered notifications without canceling them.
*/
private void clearAll() {
getNotificationMgr().clearAll();
fireEvent("clearall");
}
/**
* If a notification with an ID is present.
*
* @param id
* Notification ID
* @param command
* The callback context used when calling back into JavaScript.
*/
private void isPresent (int id, CallbackContext command) {
boolean exist = getNotificationMgr().exist(id);
PluginResult result = new PluginResult(
PluginResult.Status.OK, exist);
command.sendPluginResult(result); command.sendPluginResult(result);
} }
/** /**
* If a notification with an ID is scheduled. * Request permission for local notifications.
* *
* @param id * @param command The callback context used when calling back into
* Notification ID * JavaScript.
* @param command
* The callback context used when calling back into JavaScript.
*/ */
private void isScheduled (int id, CallbackContext command) { private void request (CallbackContext command) {
boolean exist = getNotificationMgr().exist( check(command);
id, Notification.Type.SCHEDULED);
PluginResult result = new PluginResult(
PluginResult.Status.OK, exist);
command.sendPluginResult(result);
} }
/** /**
* If a notification with an ID is triggered. * Schedule multiple local notifications.
*
* @param id
* Notification ID
* @param command
* The callback context used when calling back into JavaScript.
*/
private void isTriggered (int id, CallbackContext command) {
boolean exist = getNotificationMgr().exist(
id, Notification.Type.TRIGGERED);
PluginResult result = new PluginResult(
PluginResult.Status.OK, exist);
command.sendPluginResult(result);
}
/**
* Set of IDs from all existent notifications.
*
* @param command
* The callback context used when calling back into JavaScript.
*/
private void getAllIds (CallbackContext command) {
List<Integer> ids = getNotificationMgr().getIds();
command.success(new JSONArray(ids));
}
/**
* Set of IDs from all scheduled notifications.
*
* @param command
* The callback context used when calling back into JavaScript.
*/
private void getScheduledIds (CallbackContext command) {
List<Integer> ids = getNotificationMgr().getIdsByType(
Notification.Type.SCHEDULED);
command.success(new JSONArray(ids));
}
/**
* Set of IDs from all triggered notifications.
*
* @param command
* The callback context used when calling back into JavaScript.
*/
private void getTriggeredIds (CallbackContext command) {
List<Integer> ids = getNotificationMgr().getIdsByType(
Notification.Type.TRIGGERED);
command.success(new JSONArray(ids));
}
/**
* Options from local notification.
*
* @param ids
* Set of local notification IDs
* @param command
* The callback context used when calling back into JavaScript.
*/
private void getSingle (JSONArray ids, CallbackContext command) {
getOptions(ids.optString(0), Notification.Type.ALL, command);
}
/**
* Options from scheduled notification.
*
* @param ids
* Set of local notification IDs
* @param command
* The callback context used when calling back into JavaScript.
*/
private void getSingleScheduled (JSONArray ids, CallbackContext command) {
getOptions(ids.optString(0), Notification.Type.SCHEDULED, command);
}
/**
* Options from triggered notification.
*
* @param ids
* Set of local notification IDs
* @param command
* The callback context used when calling back into JavaScript.
*/
private void getSingleTriggered (JSONArray ids, CallbackContext command) {
getOptions(ids.optString(0), Notification.Type.TRIGGERED, command);
}
/**
* Set of options from local notification.
*
* @param ids
* Set of local notification IDs
* @param command
* The callback context used when calling back into JavaScript.
*/
private void getAll (JSONArray ids, CallbackContext command) {
getOptions(ids, Notification.Type.ALL, command);
}
/**
* Set of options from scheduled notifications.
*
* @param ids
* Set of local notification IDs
* @param command
* The callback context used when calling back into JavaScript.
*/
private void getScheduled (JSONArray ids, CallbackContext command) {
getOptions(ids, Notification.Type.SCHEDULED, command);
}
/**
* Set of options from triggered notifications.
*
* @param ids
* Set of local notification IDs
* @param command
* The callback context used when calling back into JavaScript.
*/
private void getTriggered (JSONArray ids, CallbackContext command) {
getOptions(ids, Notification.Type.TRIGGERED, command);
}
/**
* Options from local notification.
* *
* @param id * @param notifications The notifications to schedule.
* Set of local notification IDs
* @param type
* The local notification life cycle type
* @param command
* The callback context used when calling back into JavaScript.
*/ */
private void getOptions (String id, Notification.Type type, private void schedule (JSONArray notifications) {
CallbackContext command) { Manager mgr = getNotificationMgr();
JSONArray ids = new JSONArray().put(id);
PluginResult result;
List<JSONObject> options = for (int i = 0; i < notifications.length(); i++) {
getNotificationMgr().getOptionsBy(type, toList(ids)); JSONObject dict = notifications.optJSONObject(i);
Options options = new Options(dict);
Request request = new Request(options);
if (options.isEmpty()) { Notification notification =
// Status.NO_RESULT led to no callback invocation :( mgr.schedule(request, TriggerReceiver.class);
// Status.OK led to no NPE and crash
result = new PluginResult(PluginResult.Status.NO_RESULT);
} else {
result = new PluginResult(PluginResult.Status.OK, options.get(0));
}
command.sendPluginResult(result); fireEvent("schedule", notification);
} }
/**
* Set of options from local notifications.
*
* @param ids
* Set of local notification IDs
* @param type
* The local notification life cycle type
* @param command
* The callback context used when calling back into JavaScript.
*/
private void getOptions (JSONArray ids, Notification.Type type,
CallbackContext command) {
List<JSONObject> options;
if (ids.length() == 0) {
options = getNotificationMgr().getOptionsByType(type);
} else {
options = getNotificationMgr().getOptionsBy(type, toList(ids));
} }
command.success(new JSONArray(options)); // /**
} // * Update multiple local notifications.
// *
// * @param updates
// * Notification properties including their IDs
// */
// private void update (JSONArray updates) {
// for (int i = 0; i < updates.length(); i++) {
// JSONObject update = updates.optJSONObject(i);
// int id = update.optInt("id", 0);
// Notification notification =
// getNotificationMgr().update(id, update, TriggerReceiver.class);
// if (notification == null)
// continue;
// fireEvent("update", notification);
// }
// }
// /**
// * Cancel multiple local notifications.
// *
// * @param ids
// * Set of local notification IDs
// */
// private void cancel (JSONArray ids) {
// for (int i = 0; i < ids.length(); i++) {
// int id = ids.optInt(i, 0);
// Notification notification =
// getNotificationMgr().cancel(id);
// if (notification == null)
// continue;
// fireEvent("cancel", notification);
// }
// }
// /**
// * Cancel all scheduled notifications.
// */
// private void cancelAll() {
// getNotificationMgr().cancelAll();
// fireEvent("cancelall");
// }
// /**
// * Clear multiple local notifications without canceling them.
// *
// * @param ids
// * Set of local notification IDs
// */
// private void clear(JSONArray ids){
// for (int i = 0; i < ids.length(); i++) {
// int id = ids.optInt(i, 0);
// Notification notification =
// getNotificationMgr().clear(id);
// if (notification == null)
// continue;
// fireEvent("clear", notification);
// }
// }
// /**
// * Clear all triggered notifications without canceling them.
// */
// private void clearAll() {
// getNotificationMgr().clearAll();
// fireEvent("clearall");
// }
// /**
// * If a notification with an ID is present.
// *
// * @param id
// * Notification ID
// * @param command
// * The callback context used when calling back into JavaScript.
// */
// private void isPresent (int id, CallbackContext command) {
// boolean exist = getNotificationMgr().exist(id);
// PluginResult result = new PluginResult(
// PluginResult.Status.OK, exist);
// command.sendPluginResult(result);
// }
// /**
// * If a notification with an ID is scheduled.
// *
// * @param id
// * Notification ID
// * @param command
// * The callback context used when calling back into JavaScript.
// */
// private void isScheduled (int id, CallbackContext command) {
// boolean exist = getNotificationMgr().exist(
// id, Notification.Type.SCHEDULED);
// PluginResult result = new PluginResult(
// PluginResult.Status.OK, exist);
// command.sendPluginResult(result);
// }
// /**
// * If a notification with an ID is triggered.
// *
// * @param id
// * Notification ID
// * @param command
// * The callback context used when calling back into JavaScript.
// */
// private void isTriggered (int id, CallbackContext command) {
// boolean exist = getNotificationMgr().exist(
// id, Notification.Type.TRIGGERED);
// PluginResult result = new PluginResult(
// PluginResult.Status.OK, exist);
// command.sendPluginResult(result);
// }
// /**
// * Set of IDs from all existent notifications.
// *
// * @param command
// * The callback context used when calling back into JavaScript.
// */
// private void getAllIds (CallbackContext command) {
// List<Integer> ids = getNotificationMgr().getIds();
// command.success(new JSONArray(ids));
// }
// /**
// * Set of IDs from all scheduled notifications.
// *
// * @param command
// * The callback context used when calling back into JavaScript.
// */
// private void getScheduledIds (CallbackContext command) {
// List<Integer> ids = getNotificationMgr().getIdsByType(
// Notification.Type.SCHEDULED);
// command.success(new JSONArray(ids));
// }
// /**
// * Set of IDs from all triggered notifications.
// *
// * @param command
// * The callback context used when calling back into JavaScript.
// */
// private void getTriggeredIds (CallbackContext command) {
// List<Integer> ids = getNotificationMgr().getIdsByType(
// Notification.Type.TRIGGERED);
// command.success(new JSONArray(ids));
// }
// /**
// * Options from local notification.
// *
// * @param ids
// * Set of local notification IDs
// * @param command
// * The callback context used when calling back into JavaScript.
// */
// private void getSingle (JSONArray ids, CallbackContext command) {
// getOptions(ids.optString(0), Notification.Type.ALL, command);
// }
// /**
// * Options from scheduled notification.
// *
// * @param ids
// * Set of local notification IDs
// * @param command
// * The callback context used when calling back into JavaScript.
// */
// private void getSingleScheduled (JSONArray ids, CallbackContext command) {
// getOptions(ids.optString(0), Notification.Type.SCHEDULED, command);
// }
// /**
// * Options from triggered notification.
// *
// * @param ids
// * Set of local notification IDs
// * @param command
// * The callback context used when calling back into JavaScript.
// */
// private void getSingleTriggered (JSONArray ids, CallbackContext command) {
// getOptions(ids.optString(0), Notification.Type.TRIGGERED, command);
// }
// /**
// * Set of options from local notification.
// *
// * @param ids
// * Set of local notification IDs
// * @param command
// * The callback context used when calling back into JavaScript.
// */
// private void getAll (JSONArray ids, CallbackContext command) {
// getOptions(ids, Notification.Type.ALL, command);
// }
// /**
// * Set of options from scheduled notifications.
// *
// * @param ids
// * Set of local notification IDs
// * @param command
// * The callback context used when calling back into JavaScript.
// */
// private void getScheduled (JSONArray ids, CallbackContext command) {
// getOptions(ids, Notification.Type.SCHEDULED, command);
// }
// /**
// * Set of options from triggered notifications.
// *
// * @param ids
// * Set of local notification IDs
// * @param command
// * The callback context used when calling back into JavaScript.
// */
// private void getTriggered (JSONArray ids, CallbackContext command) {
// getOptions(ids, Notification.Type.TRIGGERED, command);
// }
// /**
// * Options from local notification.
// *
// * @param id
// * Set of local notification IDs
// * @param type
// * The local notification life cycle type
// * @param command
// * The callback context used when calling back into JavaScript.
// */
// private void getOptions (String id, Notification.Type type,
// CallbackContext command) {
// JSONArray ids = new JSONArray().put(id);
// PluginResult result;
// List<JSONObject> options =
// getNotificationMgr().getOptionsBy(type, toList(ids));
// if (options.isEmpty()) {
// // Status.NO_RESULT led to no callback invocation :(
// // Status.OK led to no NPE and crash
// result = new PluginResult(PluginResult.Status.NO_RESULT);
// } else {
// result = new PluginResult(PluginResult.Status.OK, options.get(0));
// }
// command.sendPluginResult(result);
// }
// /**
// * Set of options from local notifications.
// *
// * @param ids
// * Set of local notification IDs
// * @param type
// * The local notification life cycle type
// * @param command
// * The callback context used when calling back into JavaScript.
// */
// private void getOptions (JSONArray ids, Notification.Type type,
// CallbackContext command) {
// List<JSONObject> options;
// if (ids.length() == 0) {
// options = getNotificationMgr().getOptionsByType(type);
// } else {
// options = getNotificationMgr().getOptionsBy(type, toList(ids));
// }
// command.success(new JSONArray(options));
// }
/** /**
* Call all pending callbacks after the deviceready event has been fired. * Call all pending callbacks after the deviceready event has been fired.
...@@ -548,10 +575,8 @@ public class LocalNotification extends CordovaPlugin { ...@@ -548,10 +575,8 @@ public class LocalNotification extends CordovaPlugin {
/** /**
* Fire given event on JS side. Does inform all event listeners. * Fire given event on JS side. Does inform all event listeners.
* *
* @param event * @param event The event name
* The event name * @param notification Optional notification to pass the id and properties.
* @param notification
* Optional local notification to pass the id and properties.
*/ */
static void fireEvent (String event, Notification notification) { static void fireEvent (String event, Notification notification) {
String state = getApplicationState(); String state = getApplicationState();
...@@ -570,8 +595,7 @@ public class LocalNotification extends CordovaPlugin { ...@@ -570,8 +595,7 @@ public class LocalNotification extends CordovaPlugin {
/** /**
* Use this instead of deprecated sendJavascript * Use this instead of deprecated sendJavascript
* *
* @param js * @param js JS code snippet as string
* JS code snippet as string
*/ */
private static synchronized void sendJavascript(final String js) { private static synchronized void sendJavascript(final String js) {
...@@ -593,27 +617,26 @@ public class LocalNotification extends CordovaPlugin { ...@@ -593,27 +617,26 @@ public class LocalNotification extends CordovaPlugin {
} }
} }
/** // /**
* Convert JSON array of integers to List. // * Convert JSON array of integers to List.
* // *
* @param ary // * @param ary
* Array of integers // * Array of integers
*/ // */
private List<Integer> toList (JSONArray ary) { // private List<Integer> toList (JSONArray ary) {
ArrayList<Integer> list = new ArrayList<Integer>(); // ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < ary.length(); i++) { // for (int i = 0; i < ary.length(); i++) {
list.add(ary.optInt(i)); // list.add(ary.optInt(i));
} // }
return list; // return list;
} // }
/** /**
* Current application state. * Current application state.
* *
* @return * @return "background" or "foreground"
* "background" or "foreground"
*/ */
static String getApplicationState () { static String getApplicationState () {
return isInBackground ? "background" : "foreground"; return isInBackground ? "background" : "foreground";
......
...@@ -30,16 +30,14 @@ import de.appplant.cordova.plugin.notification.Notification; ...@@ -30,16 +30,14 @@ import de.appplant.cordova.plugin.notification.Notification;
* Android notification bar. The notification uses the default notification * Android notification bar. The notification uses the default notification
* sound and it vibrates the phone. * sound and it vibrates the phone.
*/ */
public class TriggerReceiver extends de.appplant.cordova.plugin.notification.TriggerReceiver { public class TriggerReceiver extends de.appplant.cordova.plugin.notification.receiver.TriggerReceiver {
/** /**
* Called when a local notification was triggered. Does present the local * Called when a local notification was triggered. Does present the local
* notification, re-schedule the alarm if necessary and fire trigger event. * notification, re-schedule the alarm if necessary and fire trigger event.
* *
* @param notification * @param notification Wrapper around the local notification
* Wrapper around the local notification * @param updated If an update has triggered or the original
* @param updated
* If an update has triggered or the original
*/ */
@Override @Override
public void onTrigger (Notification notification, boolean updated) { public void onTrigger (Notification notification, boolean updated) {
...@@ -53,13 +51,11 @@ public class TriggerReceiver extends de.appplant.cordova.plugin.notification.Tri ...@@ -53,13 +51,11 @@ public class TriggerReceiver extends de.appplant.cordova.plugin.notification.Tri
/** /**
* Build notification specified by options. * Build notification specified by options.
* *
* @param builder * @param builder Notification builder
* Notification builder
*/ */
@Override @Override
public Notification buildNotification (Builder builder) { public Notification buildNotification (Builder builder) {
return builder return builder
.setTriggerReceiver(TriggerReceiver.class)
.setClickActivity(ClickActivity.class) .setClickActivity(ClickActivity.class)
.setClearReceiver(ClearReceiver.class) .setClearReceiver(ClearReceiver.class)
.build(); .build();
......
...@@ -24,13 +24,13 @@ package de.appplant.cordova.plugin.notification; ...@@ -24,13 +24,13 @@ package de.appplant.cordova.plugin.notification;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import org.json.JSONObject;
import java.util.Random; import java.util.Random;
import de.appplant.cordova.plugin.notification.activity.ClickActivity;
import de.appplant.cordova.plugin.notification.receiver.ClearReceiver;
/** /**
* Builder class for local notifications. Build fully configured local * Builder class for local notifications. Build fully configured local
* notification specified by JSON object passed from JS side. * notification specified by JSON object passed from JS side.
...@@ -43,9 +43,6 @@ public class Builder { ...@@ -43,9 +43,6 @@ public class Builder {
// Notification options passed by JS // Notification options passed by JS
private final Options options; private final Options options;
// Receiver to handle the trigger event
private Class<?> triggerReceiver;
// Receiver to handle the clear event // Receiver to handle the clear event
private Class<?> clearReceiver = ClearReceiver.class; private Class<?> clearReceiver = ClearReceiver.class;
...@@ -55,21 +52,7 @@ public class Builder { ...@@ -55,21 +52,7 @@ public class Builder {
/** /**
* Constructor * Constructor
* *
* @param context * @param options Notification options
* Application context
* @param options
* Notification options
*/
public Builder(Context context, JSONObject options) {
this.context = context;
this.options = new Options(context).parse(options);
}
/**
* Constructor
*
* @param options
* Notification options
*/ */
public Builder(Options options) { public Builder(Options options) {
this.context = options.getContext(); this.context = options.getContext();
...@@ -77,21 +60,9 @@ public class Builder { ...@@ -77,21 +60,9 @@ public class Builder {
} }
/** /**
* Set trigger receiver.
*
* @param receiver
* Broadcast receiver
*/
public Builder setTriggerReceiver(Class<?> receiver) {
this.triggerReceiver = receiver;
return this;
}
/**
* Set clear receiver. * Set clear receiver.
* *
* @param receiver * @param receiver Broadcast receiver for the clear event.
* Broadcast receiver
*/ */
public Builder setClearReceiver(Class<?> receiver) { public Builder setClearReceiver(Class<?> receiver) {
this.clearReceiver = receiver; this.clearReceiver = receiver;
...@@ -101,8 +72,7 @@ public class Builder { ...@@ -101,8 +72,7 @@ public class Builder {
/** /**
* Set click activity. * Set click activity.
* *
* @param activity * @param activity The activity to handler the click event.
* Activity
*/ */
public Builder setClickActivity(Class<?> activity) { public Builder setClickActivity(Class<?> activity) {
this.clickActivity = activity; this.clickActivity = activity;
...@@ -111,50 +81,45 @@ public class Builder { ...@@ -111,50 +81,45 @@ public class Builder {
/** /**
* Creates the notification with all its options passed through JS. * Creates the notification with all its options passed through JS.
*
* @return The final notification to display.
*/ */
public Notification build() { public Notification build() {
Uri sound = options.getSoundUri();
int smallIcon = options.getSmallIcon(); int smallIcon = options.getSmallIcon();
int ledColor = options.getLedColor();
NotificationCompat.Builder builder; NotificationCompat.Builder builder;
builder = new NotificationCompat.Builder(context) builder = new NotificationCompat.Builder(context, "group")
.setDefaults(0) .setDefaults(options.getDefaults())
.setContentTitle(options.getTitle()) .setContentTitle(options.getTitle())
.setContentText(options.getText()) .setContentText(options.getText())
.setNumber(options.getBadgeNumber()) .setNumber(options.getBadgeNumber())
.setTicker(options.getText())
.setAutoCancel(options.isAutoClear()) .setAutoCancel(options.isAutoClear())
.setOngoing(options.isOngoing()) .setOngoing(options.isSticky())
.setColor(options.getColor()); .setColor(options.getColor())
.setSound(options.getSound())
if (ledColor != 0) { .setVisibility(options.getVisibility())
builder.setLights(ledColor, 100, 100); .setPriority(options.getPriority())
} .setShowWhen(options.getShowWhen())
.setLights(options.getLedColor(), options.getLedOn(), options.getLedOff());
if (sound != null) {
builder.setSound(sound); if (smallIcon != 0) {
} builder.setSmallIcon(smallIcon);
builder.setLargeIcon(options.getLargeIcon());
if (smallIcon == 0) {
builder.setSmallIcon(options.getIcon());
} else { } else {
builder.setSmallIcon(options.getSmallIcon()); builder.setSmallIcon(options.getIcon());
builder.setLargeIcon(options.getIconBitmap());
} }
applyDeleteReceiver(builder); applyDeleteReceiver(builder);
applyContentReceiver(builder); applyContentReceiver(builder);
return new Notification(context, options, builder, triggerReceiver); return new Notification(context, options, builder);
} }
/** /**
* Set intent to handle the delete event. Will clean up some persisted * Set intent to handle the delete event. Will clean up some persisted
* preferences. * preferences.
* *
* @param builder * @param builder Local notification builder instance
* Local notification builder instance
*/ */
private void applyDeleteReceiver(NotificationCompat.Builder builder) { private void applyDeleteReceiver(NotificationCompat.Builder builder) {
...@@ -162,7 +127,7 @@ public class Builder { ...@@ -162,7 +127,7 @@ public class Builder {
return; return;
Intent intent = new Intent(context, clearReceiver) Intent intent = new Intent(context, clearReceiver)
.setAction(options.getIdStr()) .setAction(options.getIdentifier())
.putExtra(Options.EXTRA, options.toString()); .putExtra(Options.EXTRA, options.toString());
PendingIntent deleteIntent = PendingIntent.getBroadcast( PendingIntent deleteIntent = PendingIntent.getBroadcast(
...@@ -175,8 +140,7 @@ public class Builder { ...@@ -175,8 +140,7 @@ public class Builder {
* Set intent to handle the click event. Will bring the app to * Set intent to handle the click event. Will bring the app to
* foreground. * foreground.
* *
* @param builder * @param builder Local notification builder instance
* Local notification builder instance
*/ */
private void applyContentReceiver(NotificationCompat.Builder builder) { private void applyContentReceiver(NotificationCompat.Builder builder) {
......
...@@ -21,20 +21,30 @@ ...@@ -21,20 +21,30 @@
package de.appplant.cordova.plugin.notification; package de.appplant.cordova.plugin.notification;
import android.app.AlarmManager;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Build;
import android.support.v4.app.NotificationManagerCompat;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import static de.appplant.cordova.plugin.notification.Notification.PREF_KEY; import de.appplant.cordova.plugin.notification.receiver.TriggerReceiver;
// import static de.appplant.cordova.plugin.notification.Notification.PREF_KEY;
/** /**
* Central way to access all or single local notifications set by specific * Central way to access all or single local notifications set by specific
...@@ -49,8 +59,7 @@ public class Manager { ...@@ -49,8 +59,7 @@ public class Manager {
/** /**
* Constructor * Constructor
* *
* @param context * @param context Application context
* Application context
*/ */
private Manager(Context context){ private Manager(Context context){
this.context = context; this.context = context;
...@@ -59,398 +68,441 @@ public class Manager { ...@@ -59,398 +68,441 @@ public class Manager {
/** /**
* Static method to retrieve class instance. * Static method to retrieve class instance.
* *
* @param context * @param context Application context
* Application context
*/ */
public static Manager getInstance(Context context) { public static Manager getInstance(Context context) {
return new Manager(context); return new Manager(context);
} }
/** /**
* Schedule local notification specified by JSON object. * Check if app has local notification permission.
*
* @param options
* JSON object with set of options
* @param receiver
* Receiver to handle the trigger event
*/
public Notification schedule (JSONObject options, Class<?> receiver) {
return schedule(new Options(context).parse(options), receiver);
}
/**
* Schedule local notification specified by options object.
*
* @param options
* Set of notification options
* @param receiver
* Receiver to handle the trigger event
*/
public Notification schedule (Options options, Class<?> receiver) {
Notification notification = new Builder(options)
.setTriggerReceiver(receiver)
.build();
notification.schedule();
return notification;
}
/**
* Clear local notification specified by ID.
*
* @param id
* The notification ID
* @param updates
* JSON object with notification options
* @param receiver
* Receiver to handle the trigger event
*/
public Notification update (int id, JSONObject updates, Class<?> receiver) {
Notification notification = get(id);
if (notification == null)
return null;
notification.cancel();
JSONObject options = mergeJSONObjects(
notification.getOptions().getDict(), updates);
try {
options.put("updated", true);
} catch (JSONException ignore) {}
return schedule(options, receiver);
}
/**
* Clear local notification specified by ID.
*
* @param id
* The notification ID
*/
public Notification clear (int id) {
Notification notification = get(id);
if (notification != null) {
notification.clear();
}
return notification;
}
/**
* Clear local notification specified by ID.
*
* @param id
* The notification ID
*/
public Notification cancel (int id) {
Notification notification = get(id);
if (notification != null) {
notification.cancel();
}
return notification;
}
/**
* Clear all local notifications.
*/
public void clearAll () {
List<Notification> notifications = getAll();
for (Notification notification : notifications) {
notification.clear();
}
getNotMgr().cancelAll();
}
/**
* Cancel all local notifications.
*/
public void cancelAll () {
List<Notification> notifications = getAll();
for (Notification notification : notifications) {
notification.cancel();
}
getNotMgr().cancelAll();
}
/**
* All local notifications IDs.
*/
public List<Integer> getIds() {
Set<String> keys = getPrefs().getAll().keySet();
ArrayList<Integer> ids = new ArrayList<Integer>();
for (String key : keys) {
try {
ids.add(Integer.parseInt(key));
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
return ids;
}
/**
* All local notification IDs for given type.
*
* @param type
* The notification life cycle type
*/
public List<Integer> getIdsByType(Notification.Type type) {
List<Notification> notifications = getAll();
ArrayList<Integer> ids = new ArrayList<Integer>();
for (Notification notification : notifications) {
if (notification.getType() == type) {
ids.add(notification.getId());
}
}
return ids;
}
/**
* List of local notifications with matching ID.
*
* @param ids
* Set of notification IDs
*/
public List<Notification> getByIds(List<Integer> ids) {
ArrayList<Notification> notifications = new ArrayList<Notification>();
for (int id : ids) {
Notification notification = get(id);
if (notification != null) {
notifications.add(notification);
}
}
return notifications;
}
/**
* List of all local notification.
*/
public List<Notification> getAll() {
return getByIds(getIds());
}
/**
* List of local notifications from given type.
*
* @param type
* The notification life cycle type
*/
public List<Notification> getByType(Notification.Type type) {
List<Notification> notifications = getAll();
ArrayList<Notification> list = new ArrayList<Notification>();
if (type == Notification.Type.ALL)
return notifications;
for (Notification notification : notifications) {
if (notification.getType() == type) {
list.add(notification);
}
}
return list;
}
/**
* List of local notifications with matching ID from given type.
*
* @param type
* The notification life cycle type
* @param ids
* Set of notification IDs
*/
@SuppressWarnings("UnusedDeclaration")
public List<Notification> getBy(Notification.Type type, List<Integer> ids) {
ArrayList<Notification> notifications = new ArrayList<Notification>();
for (int id : ids) {
Notification notification = get(id);
if (notification != null && notification.isScheduled()) {
notifications.add(notification);
}
}
return notifications;
}
/**
* If a notification with an ID exists.
*
* @param id
* Notification ID
*/
public boolean exist (int id) {
return get(id) != null;
}
/**
* If a notification with an ID and type exists.
*
* @param id
* Notification ID
* @param type
* Notification type
*/
public boolean exist (int id, Notification.Type type) {
Notification notification = get(id);
return notification != null && notification.getType() == type;
}
/**
* List of properties from all local notifications.
*/ */
public List<JSONObject> getOptions() { public boolean hasPermission () {
return getOptionsById(getIds()); return getNotMgr().areNotificationsEnabled();
} }
/** /**
* List of properties from local notifications with matching ID. * Schedule local notification specified by request.
* *
* @param ids * @param request Set of notification options.
* Set of notification IDs * @param receiver Receiver to handle the trigger event.
*/ */
public List<JSONObject> getOptionsById(List<Integer> ids) { public Notification schedule (Request request, Class<?> receiver) {
ArrayList<JSONObject> options = new ArrayList<JSONObject>(); Options options = request.getOptions();
AlarmManager mgr = getAlarmMgr();
for (int id : ids) { do {
Notification notification = get(id); Date date = request.getTriggerDate();
if (notification != null) { if (date == null)
options.add(notification.getOptions().getDict()); continue;
}
}
return options; Intent intent = new Intent(context, receiver)
} .setAction(request.getIdentifier())
.putExtra(Request.EXTRA, request.getOccurrence())
.putExtra(Options.EXTRA, options.toString());
/** if (!date.after(new Date()) && trigger(intent, receiver))
* List of properties from all local notifications from given type. continue;
*
* @param type
* The notification life cycle type
*/
public List<JSONObject> getOptionsByType(Notification.Type type) {
ArrayList<JSONObject> options = new ArrayList<JSONObject>();
List<Notification> notifications = getByType(type);
for (Notification notification : notifications) {
options.add(notification.getOptions().getDict());
}
return options;
}
/**
* List of properties from local notifications with matching ID from
* given type.
*
* @param type
* The notification life cycle type
* @param ids
* Set of notification IDs
*/
public List<JSONObject> getOptionsBy(Notification.Type type,
List<Integer> ids) {
if (type == Notification.Type.ALL)
return getOptionsById(ids);
ArrayList<JSONObject> options = new ArrayList<JSONObject>();
List<Notification> notifications = getByIds(ids);
for (Notification notification : notifications) {
if (notification.getType() == type) {
options.add(notification.getOptions().getDict());
}
}
return options;
}
/**
* Get existent local notification.
*
* @param id
* Notification ID
*/
public Notification get(int id) {
Map<String, ?> alarms = getPrefs().getAll();
String notId = Integer.toString(id);
JSONObject options;
if (!alarms.containsKey(notId))
return null;
PendingIntent pi = PendingIntent.getBroadcast(
context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
try { try {
String json = alarms.get(notId).toString(); mgr.set(AlarmManager.RTC_WAKEUP, date.getTime(), pi);
options = new JSONObject(json); } catch (Exception ignore) {
} catch (JSONException e) { // on some Samsung devices there is a known bug where a 500 alarms limit can crash the app
e.printStackTrace(); // http://developer.samsung.com/forum/board/thread/view.do?boardName=General&messageId=280286&listLines=15&startId=zzzzz%7E&searchSubId=0000000001
return null;
} }
Builder builder = new Builder(context, options); } while (request.moveNext());
return builder.build(); return new Notification(context, options);
} }
/** /**
* Merge two JSON objects. * Trigger local notification specified by options.
* *
* @param obj1 * @param intent The intent to broadcast.
* JSON object * @param cls The broadcast class.
* @param obj2
* JSON object with new options
*/ */
private JSONObject mergeJSONObjects (JSONObject obj1, JSONObject obj2) { private boolean trigger (Intent intent, Class<?> cls) {
Iterator it = obj2.keys(); BroadcastReceiver receiver;
while (it.hasNext()) {
try { try {
String key = (String)it.next(); receiver = (BroadcastReceiver) cls.newInstance();
} catch (InstantiationException e) {
obj1.put(key, obj2.opt(key)); return false;
} catch (JSONException e) { } catch (IllegalAccessException e) {
e.printStackTrace(); return false;
} }
}
receiver.onReceive(context, intent);
return obj1; return true;
} }
// /**
// * Clear local notification specified by ID.
// *
// * @param id
// * The notification ID
// * @param updates
// * JSON object with notification options
// * @param receiver
// * Receiver to handle the trigger event
// */
// public Notification update (int id, JSONObject updates, Class<?> receiver) {
// Notification notification = get(id);
// if (notification == null)
// return null;
// notification.cancel();
// JSONObject options = mergeJSONObjects(
// notification.getOptions().getDict(), updates);
// try {
// options.put("updated", true);
// } catch (JSONException ignore) {}
// return schedule(options, receiver);
// }
// /**
// * Clear local notification specified by ID.
// *
// * @param id
// * The notification ID
// */
// public Notification clear (int id) {
// Notification notification = get(id);
// if (notification != null) {
// notification.clear();
// }
// return notification;
// }
// /**
// * Clear local notification specified by ID.
// *
// * @param id
// * The notification ID
// */
// public Notification cancel (int id) {
// Notification notification = get(id);
// if (notification != null) {
// notification.cancel();
// }
// return notification;
// }
// /**
// * Clear all local notifications.
// */
// public void clearAll () {
// List<Notification> notifications = getAll();
// for (Notification notification : notifications) {
// notification.clear();
// }
// getNotMgr().cancelAll();
// }
// /**
// * Cancel all local notifications.
// */
// public void cancelAll () {
// List<Notification> notifications = getAll();
// for (Notification notification : notifications) {
// notification.cancel();
// }
// getNotMgr().cancelAll();
// }
// /**
// * All local notifications IDs.
// */
// public List<Integer> getIds() {
// Set<String> keys = getPrefs().getAll().keySet();
// ArrayList<Integer> ids = new ArrayList<Integer>();
// for (String key : keys) {
// try {
// ids.add(Integer.parseInt(key));
// } catch (NumberFormatException e) {
// e.printStackTrace();
// }
// }
// return ids;
// }
// /**
// * All local notification IDs for given type.
// *
// * @param type
// * The notification life cycle type
// */
// public List<Integer> getIdsByType(Notification.Type type) {
// List<Notification> notifications = getAll();
// ArrayList<Integer> ids = new ArrayList<Integer>();
// for (Notification notification : notifications) {
// if (notification.getType() == type) {
// ids.add(notification.getId());
// }
// }
// return ids;
// }
// /**
// * List of local notifications with matching ID.
// *
// * @param ids
// * Set of notification IDs
// */
// public List<Notification> getByIds(List<Integer> ids) {
// ArrayList<Notification> notifications = new ArrayList<Notification>();
// for (int id : ids) {
// Notification notification = get(id);
// if (notification != null) {
// notifications.add(notification);
// }
// }
// return notifications;
// }
// /**
// * List of all local notification.
// */
// public List<Notification> getAll() {
// return getByIds(getIds());
// }
// /**
// * List of local notifications from given type.
// *
// * @param type
// * The notification life cycle type
// */
// public List<Notification> getByType(Notification.Type type) {
// List<Notification> notifications = getAll();
// ArrayList<Notification> list = new ArrayList<Notification>();
// if (type == Notification.Type.ALL)
// return notifications;
// for (Notification notification : notifications) {
// if (notification.getType() == type) {
// list.add(notification);
// }
// }
// return list;
// }
// /**
// * List of local notifications with matching ID from given type.
// *
// * @param type
// * The notification life cycle type
// * @param ids
// * Set of notification IDs
// */
// @SuppressWarnings("UnusedDeclaration")
// public List<Notification> getBy(Notification.Type type, List<Integer> ids) {
// ArrayList<Notification> notifications = new ArrayList<Notification>();
// for (int id : ids) {
// Notification notification = get(id);
// if (notification != null && notification.isScheduled()) {
// notifications.add(notification);
// }
// }
// return notifications;
// }
// /**
// * If a notification with an ID exists.
// *
// * @param id
// * Notification ID
// */
// public boolean exist (int id) {
// return get(id) != null;
// }
// /**
// * If a notification with an ID and type exists.
// *
// * @param id
// * Notification ID
// * @param type
// * Notification type
// */
// public boolean exist (int id, Notification.Type type) {
// Notification notification = get(id);
// return notification != null && notification.getType() == type;
// }
// /**
// * List of properties from all local notifications.
// */
// public List<JSONObject> getOptions() {
// return getOptionsById(getIds());
// }
// /**
// * List of properties from local notifications with matching ID.
// *
// * @param ids
// * Set of notification IDs
// */
// public List<JSONObject> getOptionsById(List<Integer> ids) {
// ArrayList<JSONObject> options = new ArrayList<JSONObject>();
// for (int id : ids) {
// Notification notification = get(id);
// if (notification != null) {
// options.add(notification.getOptions().getDict());
// }
// }
// return options;
// }
// /**
// * List of properties from all local notifications from given type.
// *
// * @param type
// * The notification life cycle type
// */
// public List<JSONObject> getOptionsByType(Notification.Type type) {
// ArrayList<JSONObject> options = new ArrayList<JSONObject>();
// List<Notification> notifications = getByType(type);
// for (Notification notification : notifications) {
// options.add(notification.getOptions().getDict());
// }
// return options;
// }
// /**
// * List of properties from local notifications with matching ID from
// * given type.
// *
// * @param type
// * The notification life cycle type
// * @param ids
// * Set of notification IDs
// */
// public List<JSONObject> getOptionsBy(Notification.Type type,
// List<Integer> ids) {
// if (type == Notification.Type.ALL)
// return getOptionsById(ids);
// ArrayList<JSONObject> options = new ArrayList<JSONObject>();
// List<Notification> notifications = getByIds(ids);
// for (Notification notification : notifications) {
// if (notification.getType() == type) {
// options.add(notification.getOptions().getDict());
// }
// }
// return options;
// }
// /**
// * Get existent local notification.
// *
// * @param id
// * Notification ID
// */
// public Notification get(int id) {
// Map<String, ?> alarms = getPrefs().getAll();
// String notId = Integer.toString(id);
// JSONObject options;
// if (!alarms.containsKey(notId))
// return null;
// try {
// String json = alarms.get(notId).toString();
// options = new JSONObject(json);
// } catch (JSONException e) {
// e.printStackTrace();
// return null;
// }
// Builder builder = new Builder(context, options);
// return builder.build();
// }
// /**
// * Merge two JSON objects.
// *
// * @param obj1
// * JSON object
// * @param obj2
// * JSON object with new options
// */
// private JSONObject mergeJSONObjects (JSONObject obj1, JSONObject obj2) {
// Iterator it = obj2.keys();
// while (it.hasNext()) {
// try {
// String key = (String)it.next();
// obj1.put(key, obj2.opt(key));
// } catch (JSONException e) {
// e.printStackTrace();
// }
// }
// return obj1;
// }
// /**
// * Shared private preferences for the application.
// */
// private SharedPreferences getPrefs () {
// return context.getSharedPreferences(PREF_KEY, Context.MODE_PRIVATE);
// }
/** /**
* Shared private preferences for the application. * Alarm manager for the application.
*/ */
private SharedPreferences getPrefs () { private AlarmManager getAlarmMgr () {
return context.getSharedPreferences(PREF_KEY, Context.MODE_PRIVATE); return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
} }
/** /**
* Notification manager for the application. * Notification manager for the application.
*/ */
private NotificationManager getNotMgr () { private NotificationManagerCompat getNotMgr () {
return (NotificationManager) context return NotificationManagerCompat.from(context);
.getSystemService(Context.NOTIFICATION_SERVICE);
} }
} }
...@@ -23,6 +23,7 @@ package de.appplant.cordova.plugin.notification; ...@@ -23,6 +23,7 @@ package de.appplant.cordova.plugin.notification;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
...@@ -36,6 +37,8 @@ import org.json.JSONObject; ...@@ -36,6 +37,8 @@ import org.json.JSONObject;
import java.util.Date; import java.util.Date;
import de.appplant.cordova.plugin.notification.receiver.TriggerReceiver;
/** /**
* Wrapper class around OS notification class. Handles basic operations * Wrapper class around OS notification class. Handles basic operations
* like show, delete, cancel for a single local notification instance. * like show, delete, cancel for a single local notification instance.
...@@ -47,9 +50,6 @@ public class Notification { ...@@ -47,9 +50,6 @@ public class Notification {
ALL, SCHEDULED, TRIGGERED ALL, SCHEDULED, TRIGGERED
} }
// Default receiver to handle the trigger event
private static Class<?> defaultReceiver = TriggerReceiver.class;
// Key for private preferences // Key for private preferences
static final String PREF_KEY = "LocalNotification"; static final String PREF_KEY = "LocalNotification";
...@@ -62,27 +62,29 @@ public class Notification { ...@@ -62,27 +62,29 @@ public class Notification {
// Builder with full configuration // Builder with full configuration
private final NotificationCompat.Builder builder; private final NotificationCompat.Builder builder;
// Receiver to handle the trigger event
private Class<?> receiver = defaultReceiver;
/** /**
* Constructor * Constructor
* *
* @param context * @param context Application context.
* Application context * @param options Parsed notification options.
* @param options * @param builder Pre-configured notification builder.
* Parsed notification options
* @param builder
* Pre-configured notification builder
*/ */
protected Notification (Context context, Options options, Notification (Context context, Options options, NotificationCompat.Builder builder) {
NotificationCompat.Builder builder, Class<?> receiver) {
this.context = context; this.context = context;
this.options = options; this.options = options;
this.builder = builder; this.builder = builder;
}
this.receiver = receiver != null ? receiver : defaultReceiver; /**
* Constructor
*
* @param context Application context.
* @param options Parsed notification options.
*/
public Notification(Context context, Options options) {
this.context = context;
this.options = options;
this.builder = null;
} }
/** /**
...@@ -106,113 +108,113 @@ public class Notification { ...@@ -106,113 +108,113 @@ public class Notification {
return options.getId(); return options.getId();
} }
/** // /**
* If it's a repeating notification. // * If it's a repeating notification.
*/ // */
public boolean isRepeating () { // public boolean isRepeating () {
return getOptions().getRepeatInterval() > 0; // return getOptions().getRepeatInterval() > 0;
} // }
/** // /**
* If the notification was in the past. // * If the notification was in the past.
*/ // */
public boolean wasInThePast () { // public boolean wasInThePast () {
return new Date().after(options.getTriggerDate()); // return new Date().after(options.getTriggerDate());
} // }
/** // /**
* If the notification is scheduled. // * If the notification is scheduled.
*/ // */
public boolean isScheduled () { // public boolean isScheduled () {
return isRepeating() || !wasInThePast(); // return isRepeating() || !wasInThePast();
} // }
/** // /**
* If the notification is triggered. // * If the notification is triggered.
*/ // */
public boolean isTriggered () { // public boolean isTriggered () {
return wasInThePast(); // return wasInThePast();
} // }
/** // /**
* If the notification is an update. // * If the notification is an update.
* // *
* @param keepFlag // * @param keepFlag
* Set to false to remove the flag from the option map // * Set to false to remove the flag from the option map
*/ // */
protected boolean isUpdate (boolean keepFlag) { // protected boolean isUpdate (boolean keepFlag) {
boolean updated = options.getDict().optBoolean("updated", false); // boolean updated = options.getDict().optBoolean("updated", false);
if (!keepFlag) { // if (!keepFlag) {
options.getDict().remove("updated"); // options.getDict().remove("updated");
} // }
return updated; // return updated;
} // }
/** // /**
* Notification type can be one of pending or scheduled. // * Notification type can be one of pending or scheduled.
*/ // */
public Type getType () { // public Type getType () {
return isScheduled() ? Type.SCHEDULED : Type.TRIGGERED; // return isScheduled() ? Type.SCHEDULED : Type.TRIGGERED;
} // }
/** /**
* Schedule the local notification. * Schedule the local notification.
*/ */
public void schedule() { public void schedule() {
long triggerTime = options.getTriggerTime(); // long triggerTime = options.getTriggerTime();
persist(); // persist();
// Intent gets called when the Notification gets fired // // Intent gets called when the Notification gets fired
Intent intent = new Intent(context, receiver) // Intent intent = new Intent(context, receiver)
.setAction(options.getIdStr()) // .setAction(options.getIdStr())
.putExtra(Options.EXTRA, options.toString()); // .putExtra(Options.EXTRA, options.toString());
PendingIntent pi = PendingIntent.getBroadcast( // PendingIntent pi = PendingIntent.getBroadcast(
context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); // context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
if (isRepeating()) { // if (isRepeating()) {
getAlarmMgr().setRepeating(AlarmManager.RTC_WAKEUP, // getAlarmMgr().setRepeating(AlarmManager.RTC_WAKEUP,
triggerTime, options.getRepeatInterval(), pi); // triggerTime, options.getRepeatInterval(), pi);
} else { // } else {
getAlarmMgr().set(AlarmManager.RTC_WAKEUP, triggerTime, pi); // getAlarmMgr().set(AlarmManager.RTC_WAKEUP, triggerTime, pi);
} // }
} }
/** // /**
* Clear the local notification without canceling repeating alarms. // * Clear the local notification without canceling repeating alarms.
*/ // */
public void clear () { // public void clear () {
if (!isRepeating() && wasInThePast()) // if (!isRepeating() && wasInThePast())
unpersist(); // unpersist();
if (!isRepeating()) // if (!isRepeating())
getNotMgr().cancel(getId()); // getNotMgr().cancel(getId());
} // }
/** // /**
* Cancel the local notification. // * Cancel the local notification.
* // *
* Create an intent that looks similar, to the one that was registered // * Create an intent that looks similar, to the one that was registered
* using schedule. Making sure the notification id in the action is the // * using schedule. Making sure the notification id in the action is the
* same. Now we can search for such an intent using the 'getService' // * same. Now we can search for such an intent using the 'getService'
* method and cancel it. // * method and cancel it.
*/ // */
public void cancel() { // public void cancel() {
Intent intent = new Intent(context, receiver) // Intent intent = new Intent(context, receiver)
.setAction(options.getIdStr()); // .setAction(options.getIdStr());
PendingIntent pi = PendingIntent. // PendingIntent pi = PendingIntent.
getBroadcast(context, 0, intent, 0); // getBroadcast(context, 0, intent, 0);
getAlarmMgr().cancel(pi); // getAlarmMgr().cancel(pi);
getNotMgr().cancel(options.getId()); // getNotMgr().cancel(options.getId());
unpersist(); // unpersist();
} // }
/** /**
* Present the local notification to user. * Present the local notification to user.
...@@ -225,87 +227,80 @@ public class Notification { ...@@ -225,87 +227,80 @@ public class Notification {
/** /**
* Show as local notification when in background. * Show as local notification when in background.
*/ */
@SuppressWarnings("deprecation")
private void showNotification () { private void showNotification () {
int id = getOptions().getId(); if (builder != null) {
getNotMgr().notify(getId(), builder.build());
if (Build.VERSION.SDK_INT <= 15) {
// Notification for HoneyComb to ICS
getNotMgr().notify(id, builder.getNotification());
} else {
// Notification for Jellybean and above
getNotMgr().notify(id, builder.build());
}
}
/**
* Count of triggers since schedule.
*/
public int getTriggerCountSinceSchedule() {
long now = System.currentTimeMillis();
long triggerTime = options.getTriggerTime();
if (!wasInThePast())
return 0;
if (!isRepeating())
return 1;
return (int) ((now - triggerTime) / options.getRepeatInterval());
} }
/**
* Encode options to JSON.
*/
public String toString() {
JSONObject dict = options.getDict();
JSONObject json = new JSONObject();
try {
json = new JSONObject(dict.toString());
} catch (JSONException e) {
e.printStackTrace();
} }
json.remove("firstAt"); // /**
json.remove("updated"); // * Count of triggers since schedule.
json.remove("soundUri"); // */
json.remove("iconUri"); // public int getTriggerCountSinceSchedule() {
// long now = System.currentTimeMillis();
return json.toString(); // long triggerTime = options.getTriggerTime();
}
// if (!wasInThePast())
/** // return 0;
* Persist the information of this notification to the Android Shared
* Preferences. This will allow the application to restore the notification // if (!isRepeating())
* upon device reboot, app restart, retrieve notifications, aso. // return 1;
*/
private void persist () { // return (int) ((now - triggerTime) / options.getRepeatInterval());
SharedPreferences.Editor editor = getPrefs().edit(); // }
editor.putString(options.getIdStr(), options.toString()); // /**
// * Encode options to JSON.
if (Build.VERSION.SDK_INT < 9) { // */
editor.commit(); // public String toString() {
} else { // JSONObject dict = options.getDict();
editor.apply(); // JSONObject json = new JSONObject();
}
} // try {
// json = new JSONObject(dict.toString());
/** // } catch (JSONException e) {
* Remove the notification from the Android shared Preferences. // e.printStackTrace();
*/ // }
private void unpersist () {
SharedPreferences.Editor editor = getPrefs().edit(); // json.remove("firstAt");
// json.remove("updated");
editor.remove(options.getIdStr()); // json.remove("soundUri");
// json.remove("iconUri");
if (Build.VERSION.SDK_INT < 9) {
editor.commit(); // return json.toString();
} else { // }
editor.apply();
} // /**
} // * Persist the information of this notification to the Android Shared
// * Preferences. This will allow the application to restore the notification
// * upon device reboot, app restart, retrieve notifications, aso.
// */
// private void persist () {
// SharedPreferences.Editor editor = getPrefs().edit();
// editor.putString(options.getIdStr(), options.toString());
// if (Build.VERSION.SDK_INT < 9) {
// editor.commit();
// } else {
// editor.apply();
// }
// }
// /**
// * Remove the notification from the Android shared Preferences.
// */
// private void unpersist () {
// SharedPreferences.Editor editor = getPrefs().edit();
// editor.remove(options.getIdStr());
// if (Build.VERSION.SDK_INT < 9) {
// editor.commit();
// } else {
// editor.apply();
// }
// }
/** /**
* Shared private preferences for the application. * Shared private preferences for the application.
...@@ -329,14 +324,4 @@ public class Notification { ...@@ -329,14 +324,4 @@ public class Notification {
return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
} }
/**
* Set default receiver to handle the trigger event.
*
* @param receiver
* broadcast receiver
*/
public static void setDefaultTriggerReceiver (Class<?> receiver) {
defaultReceiver = receiver;
}
} }
...@@ -21,16 +21,23 @@ ...@@ -21,16 +21,23 @@
package de.appplant.cordova.plugin.notification; package de.appplant.cordova.plugin.notification;
import android.app.AlarmManager;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import org.json.JSONException; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.Date; import de.appplant.cordova.plugin.notification.util.AssetUtil;
import static android.support.v4.app.NotificationCompat.DEFAULT_LIGHTS;
import static android.support.v4.app.NotificationCompat.DEFAULT_SOUND;
import static android.support.v4.app.NotificationCompat.DEFAULT_VIBRATE;
import static android.support.v4.app.NotificationCompat.PRIORITY_MAX;
import static android.support.v4.app.NotificationCompat.PRIORITY_MIN;
import static android.support.v4.app.NotificationCompat.VISIBILITY_PUBLIC;
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
/** /**
* Wrapper around the JSON object passed through JS which contains all * Wrapper around the JSON object passed through JS which contains all
...@@ -40,119 +47,73 @@ import java.util.Date; ...@@ -40,119 +47,73 @@ import java.util.Date;
public class Options { public class Options {
// Key name for bundled extras // Key name for bundled extras
static final String EXTRA = "NOTIFICATION_OPTIONS"; public static final String EXTRA = "NOTIFICATION_OPTIONS";
// The original JSON object // Default icon path
private JSONObject options = new JSONObject(); private static final String DEFAULT_ICON = "res://icon";
// Repeat interval // The original JSON object
private long interval = 0; private final JSONObject options;
// Application context // The application context
private final Context context; private final Context context;
// Asset util instance // Asset util instance
private final AssetUtil assets; private final AssetUtil assets;
/** /**
* Constructor * When creating without a context, various methods might not work well.
* *
* @param context * @param options The options dict map.
* Application context
*/ */
public Options(Context context){ public Options(JSONObject options) {
this.context = context; this.options = options;
this.assets = AssetUtil.getInstance(context); this.context = null;
this.assets = null;
} }
/** /**
* Parse given JSON properties. * Constructor
* *
* @param options * @param context The application context.
* JSON properties * @param options The options dict map.
*/ */
public Options parse (JSONObject options) { public Options(Context context, JSONObject options) {
this.context = context;
this.options = options; this.options = options;
this.assets = AssetUtil.getInstance(context);
parseInterval();
parseAssets();
return this;
} }
/** /**
* Parse repeat interval. * Application context.
*/ */
private void parseInterval() { public Context getContext () {
String every = options.optString("every").toLowerCase(); return context;
if (every.isEmpty()) {
interval = 0;
} else
if (every.equals("second")) {
interval = 1000;
} else
if (every.equals("minute")) {
interval = AlarmManager.INTERVAL_FIFTEEN_MINUTES / 15;
} else
if (every.equals("hour")) {
interval = AlarmManager.INTERVAL_HOUR;
} else
if (every.equals("day")) {
interval = AlarmManager.INTERVAL_DAY;
} else
if (every.equals("week")) {
interval = AlarmManager.INTERVAL_DAY * 7;
} else
if (every.equals("month")) {
interval = AlarmManager.INTERVAL_DAY * 31;
} else
if (every.equals("quarter")) {
interval = AlarmManager.INTERVAL_HOUR * 2190;
} else
if (every.equals("year")) {
interval = AlarmManager.INTERVAL_DAY * 365;
} else {
try {
interval = Integer.parseInt(every) * 60000;
} catch (Exception e) {
e.printStackTrace();
}
}
} }
/** /**
* Parse asset URIs. * Wrapped JSON object.
*/ */
private void parseAssets() { JSONObject getDict () {
return options;
if (options.has("iconUri") && !options.optBoolean("updated"))
return;
Uri iconUri = assets.parse(options.optString("icon", "res://icon"));
Uri soundUri = assets.parseSound(options.optString("sound", null));
try {
options.put("iconUri", iconUri.toString());
options.put("soundUri", soundUri.toString());
} catch (JSONException e) {
e.printStackTrace();
}
} }
/** /**
* Application context. * Gets the ID for the local notification.
*
* @return 0 if the user did not specify.
*/ */
public Context getContext () { public Integer getId() {
return context; return options.optInt("id", 0);
} }
/** /**
* Wrapped JSON object. * The identifier for the local notification.
*
* @return The notification ID as the string
*/ */
JSONObject getDict () { public String getIdentifier() {
return options; return getId().toString();
} }
/** /**
...@@ -163,62 +124,27 @@ public class Options { ...@@ -163,62 +124,27 @@ public class Options {
} }
/** /**
* Repeat interval (day, week, month, year, aso.)
*/
public long getRepeatInterval() {
return interval;
}
/**
* Badge number for the local notification. * Badge number for the local notification.
*/ */
public int getBadgeNumber() { int getBadgeNumber() {
return options.optInt("badge", 0); return options.optInt("badge", 0);
} }
/** /**
* ongoing flag for local notifications. * ongoing flag for local notifications.
*/ */
public Boolean isOngoing() { Boolean isSticky() {
return options.optBoolean("ongoing", false); return options.optBoolean("sticky", false);
} }
/** /**
* autoClear flag for local notifications. * autoClear flag for local notifications.
*/ */
public Boolean isAutoClear() { Boolean isAutoClear() {
return options.optBoolean("autoClear", false); return options.optBoolean("autoClear", false);
} }
/** /**
* ID for the local notification as a number.
*/
public Integer getId() {
return options.optInt("id", 0);
}
/**
* ID for the local notification as a string.
*/
public String getIdStr() {
return getId().toString();
}
/**
* Trigger date.
*/
public Date getTriggerDate() {
return new Date(getTriggerTime());
}
/**
* Trigger date in milliseconds.
*/
public long getTriggerTime() {
return options.optLong("at", 0) * 1000;
}
/**
* Title for the local notification. * Title for the local notification.
*/ */
public String getTitle() { public String getTitle() {
...@@ -233,65 +159,99 @@ public class Options { ...@@ -233,65 +159,99 @@ public class Options {
} }
/** /**
* @return * The notification color for LED.
* The notification color for LED
*/ */
public int getLedColor() { int getLedColor() {
String hex = options.optString("led", null); Object cfg = options.opt("led");
String hex = null;
if (hex == null) { if (cfg instanceof String) {
return 0; hex = options.optString("led");
} else
if (cfg instanceof JSONArray) {
hex = options.optJSONArray("led").optString(0);
} }
if (hex == null)
return 0;
try {
hex = stripHex(hex);
int aRGB = Integer.parseInt(hex, 16); int aRGB = Integer.parseInt(hex, 16);
return aRGB + 0xFF000000; return aRGB + 0xFF000000;
} catch (NumberFormatException e) {
e.printStackTrace();
}
return 0;
}
/**
* The notification color for LED.
*/
int getLedOn() {
Object cfg = options.opt("led");
if (cfg instanceof JSONArray)
return options.optJSONArray("led").optInt(1);
return 1000;
} }
/** /**
* @return * The notification color for LED.
* The notification background color for the small icon */
* Returns null, if no color is given. int getLedOff() {
Object cfg = options.opt("led");
if (cfg instanceof JSONArray)
return options.optJSONArray("led").optInt(2);
return 1000;
}
/**
* The notification background color for the small icon.
*
* @return null, if no color is given.
*/ */
public int getColor() { public int getColor() {
String hex = options.optString("color", null); String hex = options.optString("color", null);
if (hex == null) { if (hex == null)
return NotificationCompat.COLOR_DEFAULT; return NotificationCompat.COLOR_DEFAULT;
}
try {
hex = stripHex(hex);
int aRGB = Integer.parseInt(hex, 16); int aRGB = Integer.parseInt(hex, 16);
return aRGB + 0xFF000000; return aRGB + 0xFF000000;
} catch (NumberFormatException e) {
e.printStackTrace();
}
return NotificationCompat.COLOR_DEFAULT;
} }
/** /**
* Sound file path for the local notification. * Sound file path for the local notification.
*/ */
public Uri getSoundUri() { public Uri getSound() {
Uri uri = null; return assets.parse(options.optString("sound"));
try{
uri = Uri.parse(options.optString("soundUri"));
} catch (Exception e){
e.printStackTrace();
}
return uri;
} }
/** /**
* Icon bitmap for the local notification. * Icon bitmap for the local notification.
*/ */
public Bitmap getIconBitmap() { Bitmap getLargeIcon() {
Bitmap bmp; Uri uri = assets.parse(options.optString("icon", DEFAULT_ICON));
Bitmap bmp = null;
try { try {
Uri uri = Uri.parse(options.optString("iconUri"));
bmp = assets.getIconFromUri(uri); bmp = assets.getIconFromUri(uri);
} catch (Exception e){ } catch (Exception e){
e.printStackTrace(); e.printStackTrace();
bmp = assets.getIconFromDrawable("icon");
} }
return bmp; return bmp;
...@@ -301,15 +261,19 @@ public class Options { ...@@ -301,15 +261,19 @@ public class Options {
* Icon resource ID for the local notification. * Icon resource ID for the local notification.
*/ */
public int getIcon () { public int getIcon () {
String icon = options.optString("icon", ""); String icon = options.optString("icon", DEFAULT_ICON);
int resId = assets.getResIdForDrawable(icon); int resId = assets.getResId(icon);
if (resId == 0) { if (resId == 0) {
resId = getSmallIcon(); resId = getSmallIcon();
} }
if (resId == 0) { if (resId == 0) {
resId = assets.getResId(DEFAULT_ICON);
}
if (resId == 0) {
resId = android.R.drawable.ic_popup_reminder; resId = android.R.drawable.ic_popup_reminder;
} }
...@@ -319,10 +283,115 @@ public class Options { ...@@ -319,10 +283,115 @@ public class Options {
/** /**
* Small icon resource ID for the local notification. * Small icon resource ID for the local notification.
*/ */
public int getSmallIcon () { int getSmallIcon() {
String icon = options.optString("smallIcon", ""); String icon = options.optString("smallIcon", "");
return assets.getResId(icon);
}
/**
* If the phone should vibrate.
*/
private boolean isWithVibration() {
return options.optBoolean("vibrate", true);
}
/**
* If the phone should play no sound.
*/
private boolean isWithoutSound() {
Object value = options.opt("sound");
return value == null || value.equals(false);
}
/**
* If the phone should play the default sound.
*/
private boolean isWithDefaultSound() {
Object value = options.opt("sound");
return value != null && value.equals(true);
}
/**
* If the phone should show no LED light.
*/
private boolean isWithoutLights() {
Object value = options.opt("led");
return value == null || value.equals(false);
}
return assets.getResIdForDrawable(icon); /**
* If the phone should show the default LED lights.
*/
private boolean isWithDefaultLights() {
Object value = options.opt("led");
return value != null && value.equals(true);
}
/**
* Set the default notification options that will be used.
* The value should be one or more of the following fields combined with
* bitwise-or: DEFAULT_SOUND, DEFAULT_VIBRATE, DEFAULT_LIGHTS.
*/
int getDefaults() {
int defaults = options.optInt("defaults", 0);
if (isWithVibration()) {
defaults |= DEFAULT_VIBRATE;
} else {
defaults &= DEFAULT_VIBRATE;
}
if (isWithDefaultSound()) {
defaults |= DEFAULT_SOUND;
} else
if (isWithoutSound()) {
defaults &= DEFAULT_SOUND;
}
if (isWithDefaultLights()) {
defaults |= DEFAULT_LIGHTS;
} else
if (isWithoutLights()) {
defaults &= DEFAULT_LIGHTS;
}
return defaults;
}
/**
* Gets the visibility for the notification.
*
* @return VISIBILITY_PUBLIC or VISIBILITY_SECRET
*/
int getVisibility() {
if (options.optBoolean("lockscreen", true)) {
return VISIBILITY_PUBLIC;
} else {
return VISIBILITY_SECRET;
}
}
/**
* Gets the notifications priority.
*/
int getPriority() {
int prio = options.optInt("priority");
return Math.min(Math.max(prio, PRIORITY_MIN), PRIORITY_MAX);
}
/**
* If the notification shall show the when date.
*/
boolean getShowWhen() {
return options.optBoolean("showWhen", true);
}
/**
* Gets the raw trigger spec as provided by the user.
*/
public JSONObject getTrigger() {
return options.optJSONObject("trigger");
} }
/** /**
...@@ -332,4 +401,15 @@ public class Options { ...@@ -332,4 +401,15 @@ public class Options {
return options.toString(); return options.toString();
} }
/**
* Strips the hex code #FF00FF => FF00FF
*
* @param hex The hex code to strip.
*
* @return The stripped hex code without a leading #
*/
private String stripHex(String hex) {
return (hex.charAt(0) == '#') ? hex.substring(1) : hex;
}
} }
/*
* Copyright (c) 2014-2015 by appPlant UG. All rights reserved.
*
* @APPPLANT_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apache License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://opensource.org/licenses/Apache-2.0/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPPLANT_LICENSE_HEADER_END@
*/
package de.appplant.cordova.plugin.notification;
import org.json.JSONObject;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import de.appplant.cordova.plugin.notification.trigger.MatchTrigger;
import de.appplant.cordova.plugin.notification.trigger.DateTrigger;
import de.appplant.cordova.plugin.notification.trigger.IntervalTrigger;
import static de.appplant.cordova.plugin.notification.trigger.IntervalTrigger.Unit;
public class Request {
// Key name for bundled extras
static final String EXTRA = "NOTIFICATION_OCCURRENCE_EXTRA";
// The options spec
private final Options options;
// The right trigger for the options
private final DateTrigger trigger;
// How often the trigger shall occur
private final int count;
// The trigger spec
private final JSONObject spec;
// The current trigger date
private Date triggerDate;
/**
* Constructor
*
* @param options The options spec.
*/
public Request(Options options) {
this.options = options;
this.spec = options.getTrigger();
this.count = spec.optInt("count", 1);
this.trigger = buildTrigger();
this.triggerDate = trigger.getNextTriggerDate(getBaseDate());
}
/**
* Gets the options spec.
*/
public Options getOptions() {
return options;
}
/**
* The identifier for the request.
*
* @return The notification ID as the string
*/
public String getIdentifier() {
return options.getId().toString() + "-" + getOccurrence();
}
/**
* The value of the internal occurrence counter.
*/
int getOccurrence() {
return trigger.getOccurrence();
}
/**
* If there's one more trigger date to calculate.
*/
private boolean hasNext() {
return triggerDate != null && getOccurrence() < count;
}
/**
* Moves the internal occurrence counter by one.
*/
boolean moveNext() {
if (hasNext()) {
triggerDate = getNextTriggerDate();
} else {
triggerDate = null;
}
return this.triggerDate != null;
}
/**
* Gets the current trigger date.
*
* @return null if there's no trigger date.
*/
Date getTriggerDate() {
Calendar now = Calendar.getInstance();
if (triggerDate == null)
return null;
if ((now.getTimeInMillis() - triggerDate.getTime()) > 60000) {
return null;
}
return triggerDate;
}
/**
* Gets the next trigger date based on the current trigger date.
*/
private Date getNextTriggerDate() {
return trigger.getNextTriggerDate(triggerDate);
}
/**
* Build the trigger specified in options.
*/
private DateTrigger buildTrigger() {
Object every = spec.opt("every");
if (every instanceof JSONObject) {
return new MatchTrigger(getDateMatchingComponents());
}
Unit unit = getUnit();
int ticks = getTicks();
return new IntervalTrigger(ticks, unit);
}
/**
* Gets the unit value.
*/
private Unit getUnit() {
Object every = spec.opt("every");
String unit = "SECOND";
if (spec.has("unit")) {
unit = spec.optString("unit", "second");
} else
if (every instanceof String) {
unit = spec.optString("every", "second");
}
return Unit.valueOf(unit.toUpperCase());
}
/**
* Gets the tick value.
*/
private int getTicks() {
Object every = spec.opt("every");
int ticks = 0;
if (spec.has("at")) {
ticks = 0;
} else
if (spec.has("in")) {
ticks = spec.optInt("in", 0);
} else
if (every instanceof String) {
ticks = 1;
} else
if (!(every instanceof JSONObject)) {
ticks = spec.optInt("every", 0);
}
return ticks;
}
/**
* Gets an array of all date parts to construct a datetime instance.
*
* @return [min, hour, day, month, year]
*/
private List<Integer> getDateMatchingComponents() {
JSONObject every = spec.optJSONObject("every");
return Arrays.asList(
(Integer) every.opt("minute"),
(Integer) every.opt("hour"),
(Integer) every.opt("day"),
(Integer) every.opt("month"),
(Integer) every.opt("year")
);
}
/**
* Gets the base date from where to calculate the next trigger date.
*/
private Date getBaseDate() {
if (spec.has("at")) {
return new Date(spec.optLong("at", 0) * 1000);
} else {
return new Date();
}
}
}
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
* limitations under the License. * limitations under the License.
*/ */
package de.appplant.cordova.plugin.notification; package de.appplant.cordova.plugin.notification.activity;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
...@@ -29,6 +29,9 @@ import android.os.Bundle; ...@@ -29,6 +29,9 @@ import android.os.Bundle;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import de.appplant.cordova.plugin.notification.Notification;
import de.appplant.cordova.plugin.notification.Options;
/** /**
* Abstract content receiver activity for local notifications. Creates the * Abstract content receiver activity for local notifications. Creates the
* local notification and calls the event functions for further proceeding. * local notification and calls the event functions for further proceeding.
...@@ -38,8 +41,7 @@ abstract public class AbstractClickActivity extends Activity { ...@@ -38,8 +41,7 @@ abstract public class AbstractClickActivity extends Activity {
/** /**
* Called when local notification was clicked to launch the main intent. * Called when local notification was clicked to launch the main intent.
* *
* @param state * @param state Saved instance state
* Saved instance state
*/ */
@Override @Override
public void onCreate (Bundle state) { public void onCreate (Bundle state) {
...@@ -51,13 +53,11 @@ abstract public class AbstractClickActivity extends Activity { ...@@ -51,13 +53,11 @@ abstract public class AbstractClickActivity extends Activity {
try { try {
String data = bundle.getString(Options.EXTRA); String data = bundle.getString(Options.EXTRA);
JSONObject options = new JSONObject(data); JSONObject dict = new JSONObject(data);
Options options = new Options(context, dict);
Builder builder =
new Builder(context, options);
Notification notification = Notification notification =
buildNotification(builder); new Notification(context, options);
onClick(notification); onClick(notification);
} catch (JSONException e) { } catch (JSONException e) {
...@@ -78,20 +78,11 @@ abstract public class AbstractClickActivity extends Activity { ...@@ -78,20 +78,11 @@ abstract public class AbstractClickActivity extends Activity {
/** /**
* Called when local notification was clicked by the user. * Called when local notification was clicked by the user.
* *
* @param notification * @param notification Wrapper around the local notification
* Wrapper around the local notification
*/ */
abstract public void onClick (Notification notification); abstract public void onClick (Notification notification);
/** /**
* Build notification specified by options.
*
* @param builder
* Notification builder
*/
abstract public Notification buildNotification (Builder builder);
/**
* Launch main intent from package. * Launch main intent from package.
*/ */
public void launchApp() { public void launchApp() {
......
...@@ -19,7 +19,9 @@ ...@@ -19,7 +19,9 @@
* limitations under the License. * limitations under the License.
*/ */
package de.appplant.cordova.plugin.notification; package de.appplant.cordova.plugin.notification.activity;
import de.appplant.cordova.plugin.notification.Notification;
/** /**
* The receiver activity is triggered when a notification is clicked by a user. * The receiver activity is triggered when a notification is clicked by a user.
...@@ -32,28 +34,17 @@ public class ClickActivity extends AbstractClickActivity { ...@@ -32,28 +34,17 @@ public class ClickActivity extends AbstractClickActivity {
* Called when local notification was clicked by the user. Will * Called when local notification was clicked by the user. Will
* move the app to foreground. * move the app to foreground.
* *
* @param notification * @param notification Wrapper around the local notification
* Wrapper around the local notification
*/ */
@Override @Override
public void onClick(Notification notification) { public void onClick(Notification notification) {
launchApp(); launchApp();
if (notification.isRepeating()) { // if (notification.isRepeating()) {
notification.clear(); // notification.clear();
} else { // } else {
notification.cancel(); // notification.cancel();
} // }
}
/**
* Build notification specified by options.
*
* @param builder
* Notification builder
*/
public Notification buildNotification (Builder builder) {
return builder.build();
} }
} }
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
* limitations under the License. * limitations under the License.
*/ */
package de.appplant.cordova.plugin.notification; package de.appplant.cordova.plugin.notification.receiver;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
...@@ -29,6 +29,9 @@ import android.os.Bundle; ...@@ -29,6 +29,9 @@ import android.os.Bundle;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import de.appplant.cordova.plugin.notification.Notification;
import de.appplant.cordova.plugin.notification.Options;
/** /**
* Abstract delete receiver for local notifications. Creates the local * Abstract delete receiver for local notifications. Creates the local
* notification and calls the event functions for further proceeding. * notification and calls the event functions for further proceeding.
...@@ -38,35 +41,31 @@ abstract public class AbstractClearReceiver extends BroadcastReceiver { ...@@ -38,35 +41,31 @@ abstract public class AbstractClearReceiver extends BroadcastReceiver {
/** /**
* Called when the notification was cleared from the notification center. * Called when the notification was cleared from the notification center.
* *
* @param context * @param context Application context
* Application context * @param intent Received intent with content data
* @param intent
* Received intent with content data
*/ */
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras(); Bundle bundle = intent.getExtras();
JSONObject options; String data = bundle.getString(Options.EXTRA);
try { try {
String data = bundle.getString(Options.EXTRA); JSONObject dict = new JSONObject(data);
options = new JSONObject(data); Options options = new Options(context, dict);
} catch (JSONException e) {
e.printStackTrace();
return;
}
Notification notification = Notification notification =
new Builder(context, options).build(); new Notification(context, options);
onClear(notification); onClear(notification);
} catch (JSONException e) {
e.printStackTrace();
}
} }
/** /**
* Called when a local notification was cleared from outside of the app. * Called when a local notification was cleared from outside of the app.
* *
* @param notification * @param notification Wrapper around the local notification
* Wrapper around the local notification
*/ */
abstract public void onClear (Notification notification); abstract public void onClear (Notification notification);
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
* limitations under the License. * limitations under the License.
*/ */
package de.appplant.cordova.plugin.notification; package de.appplant.cordova.plugin.notification.receiver;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
...@@ -29,7 +29,9 @@ import android.os.Bundle; ...@@ -29,7 +29,9 @@ import android.os.Bundle;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.Calendar; import de.appplant.cordova.plugin.notification.Builder;
import de.appplant.cordova.plugin.notification.Notification;
import de.appplant.cordova.plugin.notification.Options;
/** /**
* Abstract broadcast receiver for local notifications. Creates the * Abstract broadcast receiver for local notifications. Creates the
...@@ -40,10 +42,8 @@ abstract public class AbstractTriggerReceiver extends BroadcastReceiver { ...@@ -40,10 +42,8 @@ abstract public class AbstractTriggerReceiver extends BroadcastReceiver {
/** /**
* Called when an alarm was triggered. * Called when an alarm was triggered.
* *
* @param context * @param context Application context
* Application context * @param intent Received intent with content data
* @param intent
* Received intent with content data
*/ */
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
...@@ -54,21 +54,15 @@ abstract public class AbstractTriggerReceiver extends BroadcastReceiver { ...@@ -54,21 +54,15 @@ abstract public class AbstractTriggerReceiver extends BroadcastReceiver {
String data = bundle.getString(Options.EXTRA); String data = bundle.getString(Options.EXTRA);
JSONObject dict = new JSONObject(data); JSONObject dict = new JSONObject(data);
options = new Options(context).parse(dict); options = new Options(context, dict);
} catch (JSONException e) { } catch (JSONException e) {
e.printStackTrace(); e.printStackTrace();
return; return;
} }
if (options == null)
return;
if (isFirstAlarmInFuture(options))
return;
Builder builder = new Builder(options); Builder builder = new Builder(options);
Notification notification = buildNotification(builder); Notification notification = buildNotification(builder);
boolean updated = notification.isUpdate(false); boolean updated = false;// notification.isUpdate(false);
onTrigger(notification, updated); onTrigger(notification, updated);
} }
...@@ -76,45 +70,16 @@ abstract public class AbstractTriggerReceiver extends BroadcastReceiver { ...@@ -76,45 +70,16 @@ abstract public class AbstractTriggerReceiver extends BroadcastReceiver {
/** /**
* Called when a local notification was triggered. * Called when a local notification was triggered.
* *
* @param notification * @param notification Wrapper around the local notification
* Wrapper around the local notification * @param updated If an update has triggered or the original
* @param updated
* If an update has triggered or the original
*/ */
abstract public void onTrigger (Notification notification, boolean updated); abstract public void onTrigger (Notification notification, boolean updated);
/** /**
* Build notification specified by options. * Build notification specified by options.
* *
* @param builder * @param builder Notification builder
* Notification builder
*/ */
abstract public Notification buildNotification (Builder builder); abstract public Notification buildNotification (Builder builder);
/*
* If you set a repeating alarm at 11:00 in the morning and it
* should trigger every morning at 08:00 o'clock, it will
* immediately fire. E.g. Android tries to make up for the
* 'forgotten' reminder for that day. Therefore we ignore the event
* if Android tries to 'catch up'.
*/
private Boolean isFirstAlarmInFuture (Options options) {
Notification notification = new Builder(options).build();
if (!notification.isRepeating())
return false;
Calendar now = Calendar.getInstance();
Calendar alarm = Calendar.getInstance();
alarm.setTime(notification.getOptions().getTriggerDate());
int alarmHour = alarm.get(Calendar.HOUR_OF_DAY);
int alarmMin = alarm.get(Calendar.MINUTE);
int currentHour = now.get(Calendar.HOUR_OF_DAY);
int currentMin = now.get(Calendar.MINUTE);
return (currentHour != alarmHour && currentMin != alarmMin);
}
} }
...@@ -19,7 +19,9 @@ ...@@ -19,7 +19,9 @@
* limitations under the License. * limitations under the License.
*/ */
package de.appplant.cordova.plugin.notification; package de.appplant.cordova.plugin.notification.receiver;
import de.appplant.cordova.plugin.notification.Notification;
/** /**
* The clear intent receiver is triggered when the user clears a * The clear intent receiver is triggered when the user clears a
...@@ -31,12 +33,11 @@ public class ClearReceiver extends AbstractClearReceiver { ...@@ -31,12 +33,11 @@ public class ClearReceiver extends AbstractClearReceiver {
/** /**
* Called when a local notification was cleared from outside of the app. * Called when a local notification was cleared from outside of the app.
* *
* @param notification * @param notification Wrapper around the local notification
* Wrapper around the local notification
*/ */
@Override @Override
public void onClear (Notification notification) { public void onClear (Notification notification) {
notification.clear(); // notification.clear();
} }
} }
...@@ -19,7 +19,10 @@ ...@@ -19,7 +19,10 @@
* limitations under the License. * limitations under the License.
*/ */
package de.appplant.cordova.plugin.notification; package de.appplant.cordova.plugin.notification.receiver;
import de.appplant.cordova.plugin.notification.Builder;
import de.appplant.cordova.plugin.notification.Notification;
/** /**
* The alarm receiver is triggered when a scheduled alarm is fired. This class * The alarm receiver is triggered when a scheduled alarm is fired. This class
...@@ -33,10 +36,8 @@ public class TriggerReceiver extends AbstractTriggerReceiver { ...@@ -33,10 +36,8 @@ public class TriggerReceiver extends AbstractTriggerReceiver {
* Called when a local notification was triggered. Does present the local * Called when a local notification was triggered. Does present the local
* notification and re-schedule the alarm if necessary. * notification and re-schedule the alarm if necessary.
* *
* @param notification * @param notification Wrapper around the local notification
* Wrapper around the local notification * @param updated If an update has triggered or the original
* @param updated
* If an update has triggered or the original
*/ */
@Override @Override
public void onTrigger (Notification notification, boolean updated) { public void onTrigger (Notification notification, boolean updated) {
...@@ -46,8 +47,7 @@ public class TriggerReceiver extends AbstractTriggerReceiver { ...@@ -46,8 +47,7 @@ public class TriggerReceiver extends AbstractTriggerReceiver {
/** /**
* Build notification specified by options. * Build notification specified by options.
* *
* @param builder * @param builder Notification builder
* Notification builder
*/ */
@Override @Override
public Notification buildNotification (Builder builder) { public Notification buildNotification (Builder builder) {
......
/*
* Apache 2.0 License
*
* Copyright (c) Sebastian Katzer 2017
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apache License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://opensource.org/licenses/Apache-2.0/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*/
package de.appplant.cordova.plugin.notification.trigger;
import java.util.Calendar;
import java.util.Date;
abstract public class DateTrigger {
// Default unit is SECOND
public enum Unit { SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, YEAR }
// Internal counter
private int occurrence = 1;
/**
* Gets the next trigger date.
*
* @param base The date from where to calculate the trigger date.
*
* @return null if there's none next trigger date.
*/
abstract public Date getNextTriggerDate(Date base);
/**
* The value of the occurrence.
*/
public int getOccurrence() {
return occurrence;
}
/**
* Increase the occurrence by 1.
*/
void incOccurrence() {
occurrence += 1;
}
/**
* Gets a calendar instance pointing to the specified date.
*
* @param date The date to point.
*/
Calendar getCal (Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
return cal;
}
}
\ No newline at end of file
/*
* Apache 2.0 License
*
* Copyright (c) Sebastian Katzer 2017
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apache License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://opensource.org/licenses/Apache-2.0/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*/
package de.appplant.cordova.plugin.notification.trigger;
import java.util.Calendar;
import java.util.Date;
/**
* Trigger class for interval based notification. Trigger by a fixed interval
* from now.
*/
public class IntervalTrigger extends DateTrigger {
// The number of ticks per interval
private final int ticks;
// The unit of the ticks
final Unit unit;
/**
* Interval trigger based from now.
*
* @param ticks The number of ticks per interval.
* @param unit The unit of the ticks.
*/
public IntervalTrigger(int ticks, Unit unit) {
this.ticks = ticks;
this.unit = unit;
}
/**
* Gets the next trigger date.
*
* @param base The date from where to calculate the trigger date.
*
* @return null if there's none next trigger date.
*/
@Override
public Date getNextTriggerDate(Date base) {
Calendar cal = getCal(base);
addInterval(cal);
incOccurrence();
return cal.getTime();
}
/**
* Adds the amount of ticks to the calendar.
*
* @param cal The calendar to manipulate.
*
* @return The calendar instance.
*/
Calendar addInterval(Calendar cal) {
switch (unit) {
case SECOND:
cal.add(Calendar.SECOND, ticks);
break;
case MINUTE:
cal.add(Calendar.MINUTE, ticks);
break;
case HOUR:
cal.add(Calendar.HOUR_OF_DAY, ticks);
break;
case DAY:
cal.add(Calendar.DAY_OF_YEAR, ticks);
break;
case WEEK:
cal.add(Calendar.WEEK_OF_YEAR, ticks);
break;
case MONTH:
cal.add(Calendar.MONTH, ticks);
break;
case QUARTER:
cal.add(Calendar.MONTH, ticks * 3);
break;
case YEAR:
cal.add(Calendar.YEAR, ticks);
break;
}
return cal;
}
}
\ No newline at end of file
/*
* Apache 2.0 License
*
* Copyright (c) Sebastian Katzer 2017
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apache License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://opensource.org/licenses/Apache-2.0/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*/
package de.appplant.cordova.plugin.notification.trigger;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.DAY;
import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.HOUR;
import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.MINUTE;
import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.MONTH;
import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.YEAR;
/**
* Trigger for date matching components.
*/
public class MatchTrigger extends IntervalTrigger {
// Used to determine the interval
private static Unit[] INTERVALS = { null, MINUTE, HOUR, DAY, MONTH, YEAR };
// The date matching components
private final List<Integer> matchers;
/**
* Date matching trigger from now.
*
* @param matchers Describes the date matching parts.
* { day: 15, month: ... }
*/
public MatchTrigger(List<Integer> matchers) {
super(1, INTERVALS[1 + matchers.indexOf(null)]);
this.matchers = matchers;
}
/**
* Gets the date from where to start calculating the initial trigger date.
*/
private Calendar getBaseTriggerDate(Date date) {
Calendar cal = getCal(date);
cal.set(Calendar.SECOND, 0);
if (matchers.get(0) != null) {
cal.set(Calendar.MINUTE, matchers.get(0));
} else {
cal.set(Calendar.MINUTE, 0);
}
if (matchers.get(1) != null) {
cal.set(Calendar.HOUR_OF_DAY, matchers.get(1));
} else {
cal.set(Calendar.HOUR_OF_DAY, 0);
}
if (matchers.get(2) != null) {
cal.set(Calendar.DAY_OF_MONTH, matchers.get(2));
}
if (matchers.get(3) != null) {
cal.set(Calendar.MONTH, matchers.get(3));
}
if (matchers.get(4) != null) {
cal.set(Calendar.YEAR, matchers.get(4));
}
return cal;
}
/**
* Gets the first trigger date.
*
* @param base The date from where to calculate the trigger date.
*
* @return null if there's none trigger date.
*/
private Date getTriggerDate (Date base) {
Calendar date = getBaseTriggerDate(base);
Calendar now = getCal(base);
if (date.compareTo(now) >= 0)
return date.getTime();
if (unit == null || date.get(Calendar.YEAR) < now.get(Calendar.YEAR))
return null;
if (date.get(Calendar.MONTH) < now.get(Calendar.MONTH)) {
switch (unit) {
case MINUTE:
case HOUR:
case DAY:
if (matchers.get(4) == null) {
return addToDate(date, now, Calendar.YEAR, 1);
} else break;
case YEAR:
return addToDate(date, now, Calendar.YEAR, 1);
}
} else
if (date.get(Calendar.DAY_OF_YEAR) < now.get(Calendar.DAY_OF_YEAR)) {
switch (unit) {
case MINUTE:
case HOUR:
if (matchers.get(3) == null) {
return addToDate(date, now, Calendar.MONTH, 1);
} else
if (matchers.get(4) == null) {
return addToDate(date, now, Calendar.YEAR, 1);
}
else break;
case MONTH:
return addToDate(date, now, Calendar.MONTH, 1);
case YEAR:
return addToDate(date, now, Calendar.YEAR, 1);
}
} else
if (date.get(Calendar.HOUR_OF_DAY) < now.get(Calendar.HOUR_OF_DAY)) {
switch (unit) {
case MINUTE:
if (matchers.get(2) == null) {
return addToDate(date, now, Calendar.DAY_OF_YEAR, 1);
} else
if (matchers.get(3) == null) {
return addToDate(date, now, Calendar.MONTH, 1);
}
else break;
case HOUR:
return addToDate(date, now, Calendar.HOUR_OF_DAY, 0);
case DAY:
return addToDate(date, now, Calendar.DAY_OF_YEAR, 1);
case MONTH:
return addToDate(date, now, Calendar.MONTH, 1);
case YEAR:
return addToDate(date, now, Calendar.YEAR, 1);
}
} else
if (date.get(Calendar.MINUTE) < now.get(Calendar.MINUTE)) {
switch (unit) {
case MINUTE:
return addToDate(date, now, Calendar.MINUTE, 1);
case HOUR:
return addToDate(date, now, Calendar.HOUR_OF_DAY, 1);
case DAY:
return addToDate(date, now, Calendar.DAY_OF_YEAR, 1);
case MONTH:
return addToDate(date, now, Calendar.MONTH, 1);
case YEAR:
return addToDate(date, now, Calendar.YEAR, 1);
}
}
return null;
}
/**
* Gets the next trigger date.
*
* @param base The date from where to calculate the trigger date.
*
* @return null if there's none next trigger date.
*/
@Override
public Date getNextTriggerDate (Date base) {
Date date = base;
if (getOccurrence() > 1) {
Calendar cal = getCal(base);
addInterval(cal);
date = cal.getTime();
}
incOccurrence();
return getTriggerDate(date);
}
/**
* Sets the field value of now to date and adds by count.
*
* @return The new date.
*/
private Date addToDate(Calendar date, Calendar now, int field, int count) {
date.set(field, now.get(field));
date.add(field, count);
return date.getTime();
}
}
\ No newline at end of file
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
* limitations under the License. * limitations under the License.
*/ */
package de.appplant.cordova.plugin.notification; package de.appplant.cordova.plugin.notification.util;
import android.content.Context; import android.content.Context;
import android.content.res.AssetManager; import android.content.res.AssetManager;
...@@ -42,20 +42,19 @@ import java.net.MalformedURLException; ...@@ -42,20 +42,19 @@ import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.UUID; import java.util.UUID;
import static android.media.RingtoneManager.TYPE_NOTIFICATION;
/** /**
* Util class to map unified asset URIs to native URIs. URIs like file:/// * Util class to map unified asset URIs to native URIs. URIs like file:///
* map to absolute paths while file:// point relatively to the www folder * map to absolute paths while file:// point relatively to the www folder
* within the asset resources. And res:// means a resource from the native * within the asset resources. And res:// means a resource from the native
* res folder. Remote assets are accessible via http:// for example. * res folder. Remote assets are accessible via http:// for example.
*/ */
class AssetUtil { public class AssetUtil {
// Name of the storage folder // Name of the storage folder
private static final String STORAGE_FOLDER = "/localnotification"; private static final String STORAGE_FOLDER = "/localnotification";
// Placeholder URI for default sound
private static final String DEFAULT_SOUND = "res://platform_default";
// Ref to the context passed through the constructor to access the // Ref to the context passed through the constructor to access the
// resources and app directory. // resources and app directory.
private final Context context; private final Context context;
...@@ -63,8 +62,7 @@ class AssetUtil { ...@@ -63,8 +62,7 @@ class AssetUtil {
/** /**
* Constructor * Constructor
* *
* @param context * @param context Application context
* Application context
*/ */
private AssetUtil(Context context) { private AssetUtil(Context context) {
this.context = context; this.context = context;
...@@ -73,41 +71,22 @@ class AssetUtil { ...@@ -73,41 +71,22 @@ class AssetUtil {
/** /**
* Static method to retrieve class instance. * Static method to retrieve class instance.
* *
* @param context * @param context Application context
* Application context
*/ */
static AssetUtil getInstance(Context context) { public static AssetUtil getInstance(Context context) {
return new AssetUtil(context); return new AssetUtil(context);
} }
/** /**
* Parse path path to native URI.
*
* @param path
* Path to path file
*/
Uri parseSound (String path) {
if (path == null || path.isEmpty())
return Uri.EMPTY;
if (path.equalsIgnoreCase(DEFAULT_SOUND)) {
return RingtoneManager.getDefaultUri(RingtoneManager
.TYPE_NOTIFICATION);
}
return parse(path);
}
/**
* The URI for a path. * The URI for a path.
* *
* @param path * @param path
* The given path * The given path
*/ */
Uri parse (String path) { public Uri parse (String path) {
if (path == null || path.isEmpty()) {
if (path.startsWith("res:")) { return Uri.EMPTY;
} else if (path.startsWith("res:")) {
return getUriForResourcePath(path); return getUriForResourcePath(path);
} else if (path.startsWith("file:///")) { } else if (path.startsWith("file:///")) {
return getUriFromPath(path); return getUriFromPath(path);
...@@ -191,7 +170,7 @@ class AssetUtil { ...@@ -191,7 +170,7 @@ class AssetUtil {
*/ */
private Uri getUriForResourcePath(String path) { private Uri getUriForResourcePath(String path) {
String resPath = path.replaceFirst("res://", ""); String resPath = path.replaceFirst("res://", "");
int resId = getResIdForDrawable(resPath); int resId = getResId(resPath);
File file = getTmpFile(); File file = getTmpFile();
if (resId == 0) { if (resId == 0) {
...@@ -296,14 +275,15 @@ class AssetUtil { ...@@ -296,14 +275,15 @@ class AssetUtil {
/** /**
* Resource ID for drawable. * Resource ID for drawable.
* *
* @param resPath * @param resPath Resource path as string.
* Resource path as string *
* @return The resource ID or 0 if not found.
*/ */
int getResIdForDrawable(String resPath) { public int getResId(String resPath) {
int resId = getResIdForDrawable(getPkgName(), resPath); int resId = getResId(context.getResources(), resPath);
if (resId == 0) { if (resId == 0) {
resId = getResIdForDrawable("android", resPath); resId = getResId(Resources.getSystem(), resPath);
} }
return resId; return resId;
...@@ -312,56 +292,32 @@ class AssetUtil { ...@@ -312,56 +292,32 @@ class AssetUtil {
/** /**
* Resource ID for drawable. * Resource ID for drawable.
* *
* @param clsName * @param res The resources where to look for.
* Relative package or global android name space * @param resPath The name of the resource.
* @param resPath
* Resource path as string
*/
int getResIdForDrawable(String clsName, String resPath) {
String drawable = getBaseName(resPath);
int resId = 0;
try {
Class<?> cls = Class.forName(clsName + ".R$drawable");
resId = (Integer) cls.getDeclaredField(drawable).get(Integer.class);
} catch (Exception ignore) {}
return resId;
}
/**
* Convert drawable resource to bitmap.
* *
* @param drawable * @return The resource ID or 0 if not found.
* Drawable resource name
*/ */
Bitmap getIconFromDrawable (String drawable) { private int getResId(Resources res, String resPath) {
Resources res = context.getResources(); String pkgName = getPkgName(res);
int iconId; String resName = getBaseName(resPath);
int resId;
iconId = getResIdForDrawable(getPkgName(), drawable);
if (iconId == 0) { resId = res.getIdentifier(resName, "mipmap", pkgName);
iconId = getResIdForDrawable("android", drawable);
}
if (iconId == 0) { if (resId == 0) {
iconId = android.R.drawable.screen_background_dark_transparent; resId = res.getIdentifier(resName, "drawable", pkgName);
} }
return BitmapFactory.decodeResource(res, iconId); return resId;
} }
/** /**
* Convert URI to Bitmap. * Convert URI to Bitmap.
* *
* @param uri * @param uri Internal image URI
* Internal image URI
*/ */
Bitmap getIconFromUri (Uri uri) throws IOException { public Bitmap getIconFromUri(Uri uri) throws IOException {
InputStream input = context.getContentResolver().openInputStream(uri); InputStream input = context.getContentResolver().openInputStream(uri);
return BitmapFactory.decodeStream(input); return BitmapFactory.decodeStream(input);
} }
...@@ -422,10 +378,10 @@ class AssetUtil { ...@@ -422,10 +378,10 @@ class AssetUtil {
} }
/** /**
* Package name specified by context. * Package name specified by the resource bundle.
*/ */
private String getPkgName () { private String getPkgName (Resources res) {
return context.getPackageName(); return res == Resources.getSystem() ? "android" : context.getPackageName();
} }
} }
...@@ -158,12 +158,13 @@ ...@@ -158,12 +158,13 @@
NSString* path = [dict objectForKey:@"sound"]; NSString* path = [dict objectForKey:@"sound"];
NSString* file; NSString* file;
if ([path isKindOfClass:NSNumber.class]) {
return [path boolValue] ? [UNNotificationSound defaultSound] : NULL;
}
if (!path.length) if (!path.length)
return NULL; return NULL;
if ([path isEqualToString:@"res://platform_default"])
return [UNNotificationSound defaultSound];
if ([path hasPrefix:@"file:/"]) { if ([path hasPrefix:@"file:/"]) {
file = [self soundNameForAsset:path]; file = [self soundNameForAsset:path];
} else } else
......
...@@ -51,12 +51,17 @@ exports.applyPlatformSpecificOptions = function () { ...@@ -51,12 +51,17 @@ exports.applyPlatformSpecificOptions = function () {
switch (device.platform) { switch (device.platform) {
case 'Android': case 'Android':
defaults.icon = 'res://ic_popup_reminder'; defaults.icon = 'res://icon';
defaults.smallIcon = undefined; defaults.smallIcon = undefined;
defaults.ongoing = false; defaults.sticky = false;
defaults.autoClear = true; defaults.autoClear = true;
defaults.led = undefined; defaults.led = true;
defaults.color = undefined; defaults.color = undefined;
defaults.vibrate = false;
defaults.lockscreen = true;
defaults.showWhen = true;
defaults.priority = 0;
defaults.defaults = 0;
break; break;
} }
}; };
...@@ -111,14 +116,17 @@ exports.mergeWithDefaults = function (options) { ...@@ -111,14 +116,17 @@ exports.mergeWithDefaults = function (options) {
* @return [ Object ] The converted property list * @return [ Object ] The converted property list
*/ */
exports.convertProperties = function (options) { exports.convertProperties = function (options) {
var parseToInt = function (prop, options) {
if (options.id) { if (isNaN(options[prop])) {
if (isNaN(options.id)) { console.warn(prop + ' is not a number: ' + options[prop]);
options.id = this.getDefaults().id; return this.getDefaults()[prop];
console.warn('Id is not a number: ' + options.id);
} else { } else {
options.id = Number(options.id); return Number(options[prop]);
} }
};
if (options.id) {
options.id = parseToInt('id', options);
} }
if (options.title) { if (options.title) {
...@@ -130,12 +138,15 @@ exports.convertProperties = function (options) { ...@@ -130,12 +138,15 @@ exports.convertProperties = function (options) {
} }
if (options.badge) { if (options.badge) {
if (isNaN(options.badge)) { options.badge = parseToInt('badge', options);
options.badge = this.getDefaults().badge; }
console.warn('Badge number is not a number: ' + options.id);
} else { if (options.priority) {
options.badge = Number(options.badge); options.priority = parseToInt('priority', options);
} }
if (options.defaults) {
options.defaults = parseToInt('defaults', options);
} }
if (typeof options.data == 'object') { if (typeof options.data == 'object') {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment