summaryrefslogtreecommitdiff
path: root/plugins/de.appplant.cordova.plugin.local-notification/src/android
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/de.appplant.cordova.plugin.local-notification/src/android')
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/ClearReceiver.java48
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/ClickActivity.java69
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/LocalNotification.java609
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/RestoreReceiver.java65
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/TriggerReceiver.java70
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractClearReceiver.java75
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractClickActivity.java103
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractRestoreReceiver.java83
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractTriggerReceiver.java122
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AssetUtil.java436
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Builder.java194
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/ClearReceiver.java44
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/ClickActivity.java55
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Manager.java455
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Notification.java350
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Options.java303
-rw-r--r--plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/TriggerReceiver.java59
17 files changed, 3140 insertions, 0 deletions
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/ClearReceiver.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/ClearReceiver.java
new file mode 100644
index 00000000..e0892e37
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/ClearReceiver.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013-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.localnotification;
+
+import de.appplant.cordova.plugin.notification.Notification;
+
+
+/**
+ * The clear intent receiver is triggered when the user clears a
+ * notification manually. It un-persists the cleared notification from the
+ * shared preferences.
+ */
+public class ClearReceiver extends de.appplant.cordova.plugin.notification.ClearReceiver {
+
+ /**
+ * Called when a local notification was cleared from outside of the app.
+ *
+ * @param notification
+ * Wrapper around the local notification
+ */
+ @Override
+ public void onClear (Notification notification) {
+ super.onClear(notification);
+ LocalNotification.fireEvent("clear", notification);
+ }
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/ClickActivity.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/ClickActivity.java
new file mode 100644
index 00000000..d366d355
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/ClickActivity.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013-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.localnotification;
+
+import de.appplant.cordova.plugin.notification.Builder;
+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 activity calls the background callback and brings the launch intent
+ * up to foreground.
+ */
+public class ClickActivity extends de.appplant.cordova.plugin.notification.ClickActivity {
+
+ /**
+ * Called when local notification was clicked by the user.
+ *
+ * @param notification
+ * Wrapper around the local notification
+ */
+ @Override
+ public void onClick(Notification notification) {
+ LocalNotification.fireEvent("click", notification);
+
+ if (!notification.getOptions().isOngoing()) {
+ String event = notification.isRepeating() ? "clear" : "cancel";
+
+ LocalNotification.fireEvent(event, notification);
+ }
+
+ super.onClick(notification);
+ }
+
+ /**
+ * Build notification specified by options.
+ *
+ * @param builder
+ * Notification builder
+ */
+ @Override
+ public Notification buildNotification (Builder builder) {
+ return builder
+ .setTriggerReceiver(TriggerReceiver.class)
+ .build();
+ }
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/LocalNotification.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/LocalNotification.java
new file mode 100644
index 00000000..906c8d69
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/LocalNotification.java
@@ -0,0 +1,609 @@
+/*
+ * Copyright (c) 2013-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.localnotification;
+
+import android.app.Activity;
+import android.os.Build;
+
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.CordovaInterface;
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.CordovaWebView;
+import org.apache.cordova.PluginResult;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import de.appplant.cordova.plugin.notification.Manager;
+import de.appplant.cordova.plugin.notification.Notification;
+
+/**
+ * This plugin utilizes the Android AlarmManager in combination with local
+ * notifications. When a local notification is scheduled the alarm manager takes
+ * care of firing the event. When the event is processed, a notification is put
+ * in the Android notification center and status bar.
+ */
+public class LocalNotification extends CordovaPlugin {
+
+ // Reference to the web view for static access
+ private static CordovaWebView webView = null;
+
+ // Indicates if the device is ready (to receive events)
+ private static Boolean deviceready = false;
+
+ // To inform the user about the state of the app in callbacks
+ protected static Boolean isInBackground = true;
+
+ // Queues all events before deviceready
+ private static ArrayList<String> eventQueue = new ArrayList<String>();
+
+ /**
+ * Called after plugin construction and fields have been initialized.
+ * Prefer to use pluginInitialize instead since there is no value in
+ * having parameters on the initialize() function.
+ *
+ * pluginInitialize is not available for cordova 3.0-3.5 !
+ */
+ @Override
+ public void initialize (CordovaInterface cordova, CordovaWebView webView) {
+ LocalNotification.webView = super.webView;
+ }
+
+ /**
+ * Called when the system is about to start resuming a previous activity.
+ *
+ * @param multitasking
+ * Flag indicating if multitasking is turned on for app
+ */
+ @Override
+ public void onPause(boolean multitasking) {
+ super.onPause(multitasking);
+ isInBackground = true;
+ }
+
+ /**
+ * Called when the activity will start interacting with the user.
+ *
+ * @param multitasking
+ * Flag indicating if multitasking is turned on for app
+ */
+ @Override
+ public void onResume(boolean multitasking) {
+ super.onResume(multitasking);
+ isInBackground = false;
+ deviceready();
+ }
+
+ /**
+ * The final call you receive before your activity is destroyed.
+ */
+ @Override
+ public void onDestroy() {
+ deviceready = false;
+ isInBackground = true;
+ }
+
+ /**
+ * Executes the request.
+ *
+ * This method is called from the WebView thread. To do a non-trivial
+ * amount of work, use:
+ * cordova.getThreadPool().execute(runnable);
+ *
+ * To run on the UI thread, use:
+ * cordova.getActivity().runOnUiThread(runnable);
+ *
+ * @param action
+ * The action to execute.
+ * @param args
+ * The exec() arguments in JSON form.
+ * @param command
+ * The callback context used when calling back into JavaScript.
+ * @return
+ * Whether the action was valid.
+ */
+ @Override
+ public boolean execute (final String action, final JSONArray args,
+ final CallbackContext command) throws JSONException {
+
+ Notification.setDefaultTriggerReceiver(TriggerReceiver.class);
+
+ cordova.getThreadPool().execute(new Runnable() {
+ public void run() {
+ if (action.equals("schedule")) {
+ schedule(args);
+ command.success();
+ }
+ else if (action.equals("update")) {
+ update(args);
+ command.success();
+ }
+ else if (action.equals("cancel")) {
+ cancel(args);
+ command.success();
+ }
+ else if (action.equals("cancelAll")) {
+ cancelAll();
+ command.success();
+ }
+ else if (action.equals("clear")) {
+ clear(args);
+ command.success();
+ }
+ else if (action.equals("clearAll")) {
+ clearAll();
+ command.success();
+ }
+ else if (action.equals("isPresent")) {
+ isPresent(args.optInt(0), command);
+ }
+ else if (action.equals("isScheduled")) {
+ isScheduled(args.optInt(0), command);
+ }
+ else if (action.equals("isTriggered")) {
+ isTriggered(args.optInt(0), command);
+ }
+ else if (action.equals("getAllIds")) {
+ getAllIds(command);
+ }
+ else if (action.equals("getScheduledIds")) {
+ getScheduledIds(command);
+ }
+ else if (action.equals("getTriggeredIds")) {
+ getTriggeredIds(command);
+ }
+ else if (action.equals("getSingle")) {
+ getSingle(args, command);
+ }
+ else if (action.equals("getSingleScheduled")) {
+ getSingleScheduled(args, command);
+ }
+ else if (action.equals("getSingleTriggered")) {
+ getSingleTriggered(args, command);
+ }
+ else if (action.equals("getAll")) {
+ getAll(args, command);
+ }
+ else if (action.equals("getScheduled")) {
+ getScheduled(args, command);
+ }
+ else if (action.equals("getTriggered")) {
+ getTriggered(args, command);
+ }
+ else if (action.equals("deviceready")) {
+ deviceready();
+ }
+ }
+ });
+
+ return true;
+ }
+
+ /**
+ * Schedule multiple 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);
+
+ Notification notification =
+ getNotificationMgr().schedule(options, TriggerReceiver.class);
+
+ fireEvent("schedule", notification);
+ }
+ }
+
+ /**
+ * 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);
+
+ 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) {
+ 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) {
+ 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);
+
+ JSONObject options =
+ getNotificationMgr().getOptionsBy(type, toList(ids)).get(0);
+
+ command.success(options);
+ }
+
+ /**
+ * 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.
+ */
+ private static synchronized void deviceready () {
+ isInBackground = false;
+ deviceready = true;
+
+ for (String js : eventQueue) {
+ sendJavascript(js);
+ }
+
+ eventQueue.clear();
+ }
+
+ /**
+ * Fire given event on JS side. Does inform all event listeners.
+ *
+ * @param event
+ * The event name
+ */
+ private void fireEvent (String event) {
+ fireEvent(event, null);
+ }
+
+ /**
+ * Fire given event on JS side. Does inform all event listeners.
+ *
+ * @param event
+ * The event name
+ * @param notification
+ * Optional local notification to pass the id and properties.
+ */
+ static void fireEvent (String event, Notification notification) {
+ String state = getApplicationState();
+ String params = "\"" + state + "\"";
+
+ if (notification != null) {
+ params = notification.toString() + "," + params;
+ }
+
+ String js = "cordova.plugins.notification.local.core.fireEvent(" +
+ "\"" + event + "\"," + params + ")";
+
+ sendJavascript(js);
+ }
+
+ /**
+ * Use this instead of deprecated sendJavascript
+ *
+ * @param js
+ * JS code snippet as string
+ */
+ private static synchronized void sendJavascript(final String js) {
+
+ if (!deviceready) {
+ eventQueue.add(js);
+ return;
+ }
+ Runnable jsLoader = new Runnable() {
+ public void run() {
+ webView.loadUrl("javascript:" + js);
+ }
+ };
+ try {
+ Method post = webView.getClass().getMethod("post",Runnable.class);
+ post.invoke(webView,jsLoader);
+ } catch(Exception e) {
+
+ ((Activity)(webView.getContext())).runOnUiThread(jsLoader);
+ }
+ }
+
+ /**
+ * Convert JSON array of integers to List.
+ *
+ * @param ary
+ * Array of integers
+ */
+ private List<Integer> toList (JSONArray ary) {
+ ArrayList<Integer> list = new ArrayList<Integer>();
+
+ for (int i = 0; i < ary.length(); i++) {
+ list.add(ary.optInt(i));
+ }
+
+ return list;
+ }
+
+ /**
+ * Current application state.
+ *
+ * @return
+ * "background" or "foreground"
+ */
+ static String getApplicationState () {
+ return isInBackground ? "background" : "foreground";
+ }
+
+ /**
+ * Notification manager instance.
+ */
+ private Manager getNotificationMgr() {
+ return Manager.getInstance(cordova.getActivity());
+ }
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/RestoreReceiver.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/RestoreReceiver.java
new file mode 100644
index 00000000..7de4e328
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/RestoreReceiver.java
@@ -0,0 +1,65 @@
+/*
+ * 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.localnotification;
+
+import de.appplant.cordova.plugin.notification.AbstractRestoreReceiver;
+import de.appplant.cordova.plugin.notification.Builder;
+import de.appplant.cordova.plugin.notification.Notification;
+
+/**
+ * This class is triggered upon reboot of the device. It needs to re-register
+ * the alarms with the AlarmManager since these alarms are lost in case of
+ * reboot.
+ */
+public class RestoreReceiver extends AbstractRestoreReceiver {
+
+ /**
+ * Called when a local notification need to be restored.
+ *
+ * @param notification
+ * Wrapper around the local notification
+ */
+ @Override
+ public void onRestore (Notification notification) {
+ if (notification.isScheduled()) {
+ notification.schedule();
+ }
+ }
+
+ /**
+ * Build notification specified by options.
+ *
+ * @param builder
+ * Notification builder
+ */
+ @Override
+ public Notification buildNotification (Builder builder) {
+ return builder
+ .setTriggerReceiver(TriggerReceiver.class)
+ .setClearReceiver(ClearReceiver.class)
+ .setClickActivity(ClickActivity.class)
+ .build();
+ }
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/TriggerReceiver.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/TriggerReceiver.java
new file mode 100644
index 00000000..3c423c01
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/TriggerReceiver.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2013-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.localnotification;
+
+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
+ * reads the information in the intent and displays this information in the
+ * Android notification bar. The notification uses the default notification
+ * sound and it vibrates the phone.
+ */
+public class TriggerReceiver extends de.appplant.cordova.plugin.notification.TriggerReceiver {
+
+ /**
+ * Called when a local notification was triggered. Does present the local
+ * notification, re-schedule the alarm if necessary and fire trigger event.
+ *
+ * @param notification
+ * Wrapper around the local notification
+ * @param updated
+ * If an update has triggered or the original
+ */
+ @Override
+ public void onTrigger (Notification notification, boolean updated) {
+ super.onTrigger(notification, updated);
+
+ if (!updated) {
+ LocalNotification.fireEvent("trigger", notification);
+ }
+ }
+
+ /**
+ * Build notification specified by options.
+ *
+ * @param builder
+ * Notification builder
+ */
+ @Override
+ public Notification buildNotification (Builder builder) {
+ return builder
+ .setTriggerReceiver(TriggerReceiver.class)
+ .setClickActivity(ClickActivity.class)
+ .setClearReceiver(ClearReceiver.class)
+ .build();
+ }
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractClearReceiver.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractClearReceiver.java
new file mode 100644
index 00000000..94d2a19b
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractClearReceiver.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2013-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 android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Abstract delete receiver for local notifications. Creates the local
+ * notification and calls the event functions for further proceeding.
+ */
+abstract public class AbstractClearReceiver extends BroadcastReceiver {
+
+ /**
+ * Called when the notification was cleared from the notification center.
+ *
+ * @param context
+ * Application context
+ * @param intent
+ * Received intent with content data
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Bundle bundle = intent.getExtras();
+ JSONObject options;
+
+ try {
+ String data = bundle.getString(Options.EXTRA);
+ options = new JSONObject(data);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ Notification notification =
+ new Builder(context, options).build();
+
+ onClear(notification);
+ }
+
+ /**
+ * Called when a local notification was cleared from outside of the app.
+ *
+ * @param notification
+ * Wrapper around the local notification
+ */
+ abstract public void onClear (Notification notification);
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractClickActivity.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractClickActivity.java
new file mode 100644
index 00000000..a02a9981
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractClickActivity.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2013-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 android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Abstract content receiver activity for local notifications. Creates the
+ * local notification and calls the event functions for further proceeding.
+ */
+abstract public class AbstractClickActivity extends Activity {
+
+ /**
+ * Called when local notification was clicked to launch the main intent.
+ *
+ * @param state
+ * Saved instance state
+ */
+ @Override
+ public void onCreate (Bundle state) {
+ super.onCreate(state);
+
+ Intent intent = getIntent();
+ Bundle bundle = intent.getExtras();
+ Context context = getApplicationContext();
+
+ try {
+ String data = bundle.getString(Options.EXTRA);
+ JSONObject options = new JSONObject(data);
+
+ Builder builder =
+ new Builder(context, options);
+
+ Notification notification =
+ buildNotification(builder);
+
+ onClick(notification);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Called when local notification was clicked by the user.
+ *
+ * @param notification
+ * Wrapper around the local 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.
+ */
+ public void launchApp() {
+ Context context = getApplicationContext();
+ String pkgName = context.getPackageName();
+
+ Intent intent = context
+ .getPackageManager()
+ .getLaunchIntentForPackage(pkgName);
+
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+
+ context.startActivity(intent);
+ }
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractRestoreReceiver.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractRestoreReceiver.java
new file mode 100644
index 00000000..8a1f3656
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractRestoreReceiver.java
@@ -0,0 +1,83 @@
+/*
+ * 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 android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import org.json.JSONObject;
+
+import java.util.List;
+
+/**
+ * This class is triggered upon reboot of the device. It needs to re-register
+ * the alarms with the AlarmManager since these alarms are lost in case of
+ * reboot.
+ */
+abstract public class AbstractRestoreReceiver extends BroadcastReceiver {
+
+ /**
+ * Called on device reboot.
+ *
+ * @param context
+ * Application context
+ * @param intent
+ * Received intent with content data
+ */
+ @Override
+ public void onReceive (Context context, Intent intent) {
+ Manager notificationMgr =
+ Manager.getInstance(context);
+
+ List<JSONObject> options =
+ notificationMgr.getOptions();
+
+ for (JSONObject data : options) {
+ Builder builder = new Builder(context, data);
+
+ Notification notification =
+ buildNotification(builder);
+
+ onRestore(notification);
+ }
+ }
+
+ /**
+ * Called when a local notification need to be restored.
+ *
+ * @param notification
+ * Wrapper around the local notification
+ */
+ abstract public void onRestore (Notification notification);
+
+ /**
+ * Build notification specified by options.
+ *
+ * @param builder
+ * Notification builder
+ */
+ abstract public Notification buildNotification (Builder builder);
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractTriggerReceiver.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractTriggerReceiver.java
new file mode 100644
index 00000000..fc6759c5
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AbstractTriggerReceiver.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2013-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 android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Calendar;
+
+/**
+ * Abstract broadcast receiver for local notifications. Creates the
+ * notification options and calls the event functions for further proceeding.
+ */
+abstract public class AbstractTriggerReceiver extends BroadcastReceiver {
+
+ /**
+ * Called when an alarm was triggered.
+ *
+ * @param context
+ * Application context
+ * @param intent
+ * Received intent with content data
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Bundle bundle = intent.getExtras();
+ Options options;
+
+ try {
+ String data = bundle.getString(Options.EXTRA);
+ JSONObject dict = new JSONObject(data);
+
+ options = new Options(context).parse(dict);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ if (options == null)
+ return;
+
+ if (isFirstAlarmInFuture(options))
+ return;
+
+ Builder builder = new Builder(options);
+ Notification notification = buildNotification(builder);
+ boolean updated = notification.isUpdate();
+
+ onTrigger(notification, updated);
+ }
+
+ /**
+ * Called when a local notification was triggered.
+ *
+ * @param notification
+ * Wrapper around the local notification
+ * @param updated
+ * If an update has triggered or the original
+ */
+ abstract public void onTrigger (Notification notification, boolean updated);
+
+ /**
+ * Build notification specified by options.
+ *
+ * @param builder
+ * Notification 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);
+ }
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AssetUtil.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AssetUtil.java
new file mode 100644
index 00000000..2da8a2c3
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/AssetUtil.java
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2013-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 android.content.Context;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.StrictMode;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * 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
+ * within the asset resources. And res:// means a resource from the native
+ * res folder. Remote assets are accessible via http:// for example.
+ */
+class AssetUtil {
+
+ // Name of the storage folder
+ 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
+ // resources and app directory.
+ private final Context context;
+
+ /**
+ * Constructor
+ *
+ * @param context
+ * Application context
+ */
+ private AssetUtil(Context context) {
+ this.context = context;
+ }
+
+ /**
+ * Static method to retrieve class instance.
+ *
+ * @param context
+ * Application context
+ */
+ static AssetUtil getInstance(Context 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.
+ *
+ * @param path
+ * The given path
+ */
+ Uri parse (String path) {
+
+ if (path.startsWith("res:")) {
+ return getUriForResourcePath(path);
+ } else if (path.startsWith("file:///")) {
+ return getUriFromPath(path);
+ } else if (path.startsWith("file://")) {
+ return getUriFromAsset(path);
+ } else if (path.startsWith("http")){
+ return getUriFromRemote(path);
+ }
+
+ return Uri.EMPTY;
+ }
+
+ /**
+ * URI for a file.
+ *
+ * @param path
+ * Absolute path like file:///...
+ *
+ * @return
+ * URI pointing to the given path
+ */
+ private Uri getUriFromPath(String path) {
+ String absPath = path.replaceFirst("file://", "");
+ File file = new File(absPath);
+
+ if (!file.exists()) {
+ Log.e("Asset", "File not found: " + file.getAbsolutePath());
+ return Uri.EMPTY;
+ }
+
+ return Uri.fromFile(file);
+ }
+
+ /**
+ * URI for an asset.
+ *
+ * @param path
+ * Asset path like file://...
+ *
+ * @return
+ * URI pointing to the given path
+ */
+ private Uri getUriFromAsset(String path) {
+ File dir = context.getExternalCacheDir();
+
+ if (dir == null) {
+ Log.e("Asset", "Missing external cache dir");
+ return Uri.EMPTY;
+ }
+
+ String resPath = path.replaceFirst("file:/", "www");
+ String fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
+ String storage = dir.toString() + STORAGE_FOLDER;
+ File file = new File(storage, fileName);
+
+ //noinspection ResultOfMethodCallIgnored
+ new File(storage).mkdir();
+
+ try {
+ AssetManager assets = context.getAssets();
+ FileOutputStream outStream = new FileOutputStream(file);
+ InputStream inputStream = assets.open(resPath);
+
+ copyFile(inputStream, outStream);
+
+ outStream.flush();
+ outStream.close();
+
+ return Uri.fromFile(file);
+
+ } catch (Exception e) {
+ Log.e("Asset", "File not found: assets/" + resPath);
+ e.printStackTrace();
+ }
+
+ return Uri.EMPTY;
+ }
+
+ /**
+ * The URI for a resource.
+ *
+ * @param path
+ * The given relative path
+ *
+ * @return
+ * URI pointing to the given path
+ */
+ private Uri getUriForResourcePath(String path) {
+ File dir = context.getExternalCacheDir();
+
+ if (dir == null) {
+ Log.e("Asset", "Missing external cache dir");
+ return Uri.EMPTY;
+ }
+
+ String resPath = path.replaceFirst("res://", "");
+
+ int resId = getResIdForDrawable(resPath);
+
+ if (resId == 0) {
+ Log.e("Asset", "File not found: " + resPath);
+ return Uri.EMPTY;
+ }
+
+ String resName = extractResourceName(resPath);
+ String extName = extractResourceExtension(resPath);
+ String storage = dir.toString() + STORAGE_FOLDER;
+ File file = new File(storage, resName + extName);
+
+ //noinspection ResultOfMethodCallIgnored
+ new File(storage).mkdir();
+
+ try {
+ Resources res = context.getResources();
+ FileOutputStream outStream = new FileOutputStream(file);
+ InputStream inputStream = res.openRawResource(resId);
+ copyFile(inputStream, outStream);
+
+ outStream.flush();
+ outStream.close();
+
+ return Uri.fromFile(file);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return Uri.EMPTY;
+ }
+
+ /**
+ * Uri from remote located content.
+ *
+ * @param path
+ * Remote address
+ *
+ * @return
+ * Uri of the downloaded file
+ */
+ private Uri getUriFromRemote(String path) {
+ File dir = context.getExternalCacheDir();
+
+ if (dir == null) {
+ Log.e("Asset", "Missing external cache dir");
+ return Uri.EMPTY;
+ }
+
+ String resName = extractResourceName(path);
+ String extName = extractResourceExtension(path);
+ String storage = dir.toString() + STORAGE_FOLDER;
+ File file = new File(storage, resName + extName);
+
+ //noinspection ResultOfMethodCallIgnored
+ new File(storage).mkdir();
+
+ try {
+ URL url = new URL(path);
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+
+ StrictMode.ThreadPolicy policy =
+ new StrictMode.ThreadPolicy.Builder().permitAll().build();
+
+ StrictMode.setThreadPolicy(policy);
+
+ connection.setRequestProperty("Connection", "close");
+ connection.setConnectTimeout(5000);
+ connection.connect();
+
+ InputStream input = connection.getInputStream();
+ FileOutputStream outStream = new FileOutputStream(file);
+
+ copyFile(input, outStream);
+
+ outStream.flush();
+ outStream.close();
+
+ return Uri.fromFile(file);
+
+ } catch (MalformedURLException e) {
+ Log.e("Asset", "Incorrect URL");
+ e.printStackTrace();
+ } catch (FileNotFoundException e) {
+ Log.e("Asset", "Failed to create new File from HTTP Content");
+ e.printStackTrace();
+ } catch (IOException e) {
+ Log.e("Asset", "No Input can be created from http Stream");
+ e.printStackTrace();
+ }
+
+ return Uri.EMPTY;
+ }
+
+ /**
+ * Copy content from input stream into output stream.
+ *
+ * @param in
+ * The input stream
+ * @param out
+ * The output stream
+ */
+ private void copyFile(InputStream in, OutputStream out) throws IOException {
+ byte[] buffer = new byte[1024];
+ int read;
+
+ while ((read = in.read(buffer)) != -1) {
+ out.write(buffer, 0, read);
+ }
+ }
+
+ /**
+ * Resource ID for drawable.
+ *
+ * @param resPath
+ * Resource path as string
+ */
+ int getResIdForDrawable(String resPath) {
+ int resId = getResIdForDrawable(getPkgName(), resPath);
+
+ if (resId == 0) {
+ resId = getResIdForDrawable("android", resPath);
+ }
+
+ return resId;
+ }
+
+ /**
+ * Resource ID for drawable.
+ *
+ * @param clsName
+ * Relative package or global android name space
+ * @param resPath
+ * Resource path as string
+ */
+ int getResIdForDrawable(String clsName, String resPath) {
+ String drawable = extractResourceName(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
+ * Drawable resource name
+ */
+ Bitmap getIconFromDrawable (String drawable) {
+ Resources res = context.getResources();
+ int iconId;
+
+ iconId = getResIdForDrawable(getPkgName(), drawable);
+
+ if (iconId == 0) {
+ iconId = getResIdForDrawable("android", drawable);
+ }
+
+ if (iconId == 0) {
+ iconId = android.R.drawable.ic_menu_info_details;
+ }
+
+ return BitmapFactory.decodeResource(res, iconId);
+ }
+
+ /**
+ * Convert URI to Bitmap.
+ *
+ * @param uri
+ * Internal image URI
+ */
+ Bitmap getIconFromUri (Uri uri) throws IOException {
+ InputStream input = context.getContentResolver().openInputStream(uri);
+
+ return BitmapFactory.decodeStream(input);
+ }
+
+ /**
+ * Extract name of drawable resource from path.
+ *
+ * @param resPath
+ * Resource path as string
+ */
+ private String extractResourceName (String resPath) {
+ String drawable = resPath;
+
+ if (drawable.contains("/")) {
+ drawable = drawable.substring(drawable.lastIndexOf('/') + 1);
+ }
+
+ if (resPath.contains(".")) {
+ drawable = drawable.substring(0, drawable.lastIndexOf('.'));
+ }
+
+ return drawable;
+ }
+
+ /**
+ * Extract extension of drawable resource from path.
+ *
+ * @param resPath
+ * Resource path as string
+ */
+ private String extractResourceExtension (String resPath) {
+ String extName = "png";
+
+ if (resPath.contains(".")) {
+ extName = resPath.substring(resPath.lastIndexOf('.'));
+ }
+
+ return extName;
+ }
+
+ /**
+ * Package name specified by context.
+ */
+ private String getPkgName () {
+ return context.getPackageName();
+ }
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Builder.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Builder.java
new file mode 100644
index 00000000..a0be8b93
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Builder.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2013-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 android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.v4.app.NotificationCompat;
+
+import org.json.JSONObject;
+
+import java.util.Random;
+
+/**
+ * Builder class for local notifications. Build fully configured local
+ * notification specified by JSON object passed from JS side.
+ */
+public class Builder {
+
+ // Application context passed by constructor
+ private final Context context;
+
+ // Notification options passed by JS
+ private final Options options;
+
+ // Receiver to handle the trigger event
+ private Class<?> triggerReceiver;
+
+ // Receiver to handle the clear event
+ private Class<?> clearReceiver = ClearReceiver.class;
+
+ // Activity to handle the click event
+ private Class<?> clickActivity = ClickActivity.class;
+
+ /**
+ * Constructor
+ *
+ * @param context
+ * 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) {
+ this.context = options.getContext();
+ this.options = options;
+ }
+
+ /**
+ * Set trigger receiver.
+ *
+ * @param receiver
+ * Broadcast receiver
+ */
+ public Builder setTriggerReceiver(Class<?> receiver) {
+ this.triggerReceiver = receiver;
+ return this;
+ }
+
+ /**
+ * Set clear receiver.
+ *
+ * @param receiver
+ * Broadcast receiver
+ */
+ public Builder setClearReceiver(Class<?> receiver) {
+ this.clearReceiver = receiver;
+ return this;
+ }
+
+ /**
+ * Set click activity.
+ *
+ * @param activity
+ * Activity
+ */
+ public Builder setClickActivity(Class<?> activity) {
+ this.clickActivity = activity;
+ return this;
+ }
+
+ /**
+ * Creates the notification with all its options passed through JS.
+ */
+ public Notification build() {
+ Uri sound = options.getSoundUri();
+ NotificationCompat.BigTextStyle style;
+ NotificationCompat.Builder builder;
+
+ style = new NotificationCompat.BigTextStyle()
+ .bigText(options.getText());
+
+ builder = new NotificationCompat.Builder(context)
+ .setDefaults(0)
+ .setContentTitle(options.getTitle())
+ .setContentText(options.getText())
+ .setNumber(options.getBadgeNumber())
+ .setTicker(options.getText())
+ .setSmallIcon(options.getSmallIcon())
+ .setLargeIcon(options.getIconBitmap())
+ .setAutoCancel(options.isAutoClear())
+ .setOngoing(options.isOngoing())
+ .setStyle(style)
+ .setLights(options.getLedColor(), 500, 500);
+
+ if (sound != null) {
+ builder.setSound(sound);
+ }
+
+ applyDeleteReceiver(builder);
+ applyContentReceiver(builder);
+
+ return new Notification(context, options, builder, triggerReceiver);
+ }
+
+ /**
+ * Set intent to handle the delete event. Will clean up some persisted
+ * preferences.
+ *
+ * @param builder
+ * Local notification builder instance
+ */
+ private void applyDeleteReceiver(NotificationCompat.Builder builder) {
+
+ if (clearReceiver == null)
+ return;
+
+ Intent deleteIntent = new Intent(context, clearReceiver)
+ .setAction(options.getIdStr())
+ .putExtra(Options.EXTRA, options.toString());
+
+ PendingIntent dpi = PendingIntent.getBroadcast(
+ context, 0, deleteIntent, PendingIntent.FLAG_CANCEL_CURRENT);
+
+ builder.setDeleteIntent(dpi);
+ }
+
+ /**
+ * Set intent to handle the click event. Will bring the app to
+ * foreground.
+ *
+ * @param builder
+ * Local notification builder instance
+ */
+ private void applyContentReceiver(NotificationCompat.Builder builder) {
+
+ if (clickActivity == null)
+ return;
+
+ Intent intent = new Intent(context, clickActivity)
+ .putExtra(Options.EXTRA, options.toString())
+ .setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
+
+ int requestCode = new Random().nextInt();
+
+ PendingIntent contentIntent = PendingIntent.getActivity(
+ context, requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+
+ builder.setContentIntent(contentIntent);
+ }
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/ClearReceiver.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/ClearReceiver.java
new file mode 100644
index 00000000..761b6c5c
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/ClearReceiver.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013-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;
+
+/**
+ * The clear intent receiver is triggered when the user clears a
+ * notification manually. It un-persists the cleared notification from the
+ * shared preferences.
+ */
+public class ClearReceiver extends AbstractClearReceiver {
+
+ /**
+ * Called when a local notification was cleared from outside of the app.
+ *
+ * @param notification
+ * Wrapper around the local notification
+ */
+ @Override
+ public void onClear (Notification notification) {
+ notification.clear();
+ }
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/ClickActivity.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/ClickActivity.java
new file mode 100644
index 00000000..01af5c45
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/ClickActivity.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2013-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;
+
+/**
+ * The receiver activity is triggered when a notification is clicked by a user.
+ * The activity calls the background callback and brings the launch intent
+ * up to foreground.
+ */
+public class ClickActivity extends AbstractClickActivity {
+
+ /**
+ * Called when local notification was clicked by the user. Will
+ * move the app to foreground.
+ *
+ * @param notification
+ * Wrapper around the local notification
+ */
+ @Override
+ public void onClick(Notification notification) {
+ launchApp();
+ }
+
+ /**
+ * Build notification specified by options.
+ *
+ * @param builder
+ * Notification builder
+ */
+ public Notification buildNotification (Builder builder) {
+ return builder.build();
+ }
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Manager.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Manager.java
new file mode 100644
index 00000000..03ea384f
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Manager.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2013-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 android.app.NotificationManager;
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static de.appplant.cordova.plugin.notification.Notification.PREF_KEY;
+
+/**
+ * Central way to access all or single local notifications set by specific
+ * state like triggered or scheduled. Offers shortcut ways to schedule,
+ * cancel or clear local notifications.
+ */
+public class Manager {
+
+ // Context passed through constructor and used for notification builder.
+ private Context context;
+
+ /**
+ * Constructor
+ *
+ * @param context
+ * Application context
+ */
+ private Manager(Context context){
+ this.context = context;
+ }
+
+ /**
+ * Static method to retrieve class instance.
+ *
+ * @param context
+ * Application context
+ */
+ public static Manager getInstance(Context context) {
+ return new Manager(context);
+ }
+
+ /**
+ * Schedule local notification specified by JSON object.
+ *
+ * @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.putOpt("updatedAt", new Date().getTime());
+ } 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) {
+ ids.add(Integer.parseInt(key));
+ }
+
+ 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);
+ }
+
+ /**
+ * Notification manager for the application.
+ */
+ private NotificationManager getNotMgr () {
+ return (NotificationManager) context
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Notification.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Notification.java
new file mode 100644
index 00000000..5dba9d54
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Notification.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2013-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 android.app.AlarmManager;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.support.v4.app.NotificationCompat;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Date;
+
+/**
+ * Wrapper class around OS notification class. Handles basic operations
+ * like show, delete, cancel for a single local notification instance.
+ */
+public class Notification {
+
+ // Used to differ notifications by their life cycle state
+ public enum Type {
+ ALL, SCHEDULED, TRIGGERED
+ }
+
+ // Default receiver to handle the trigger event
+ private static Class<?> defaultReceiver = TriggerReceiver.class;
+
+ // Key for private preferences
+ static final String PREF_KEY = "LocalNotification";
+
+ // Application context passed by constructor
+ private final Context context;
+
+ // Notification options passed by JS
+ private final Options options;
+
+ // Builder with full configuration
+ private final NotificationCompat.Builder builder;
+
+ // Receiver to handle the trigger event
+ private Class<?> receiver = defaultReceiver;
+
+ /**
+ * Constructor
+ *
+ * @param context
+ * Application context
+ * @param options
+ * Parsed notification options
+ * @param builder
+ * Pre-configured notification builder
+ */
+ protected Notification (Context context, Options options,
+ NotificationCompat.Builder builder, Class<?> receiver) {
+
+ this.context = context;
+ this.options = options;
+ this.builder = builder;
+
+ this.receiver = receiver != null ? receiver : defaultReceiver;
+ }
+
+ /**
+ * Get application context.
+ */
+ public Context getContext () {
+ return context;
+ }
+
+ /**
+ * Get notification options.
+ */
+ public Options getOptions () {
+ return options;
+ }
+
+ /**
+ * Get notification ID.
+ */
+ public int getId () {
+ return options.getId();
+ }
+
+ /**
+ * If it's a repeating notification.
+ */
+ public boolean isRepeating () {
+ return getOptions().getRepeatInterval() > 0;
+ }
+
+ /**
+ * If the notification was in the past.
+ */
+ public boolean wasInThePast () {
+ return new Date().after(options.getTriggerDate());
+ }
+
+ /**
+ * If the notification is scheduled.
+ */
+ public boolean isScheduled () {
+ return isRepeating() || !wasInThePast();
+ }
+
+ /**
+ * If the notification is triggered.
+ */
+ public boolean isTriggered () {
+ return wasInThePast();
+ }
+
+ /**
+ * If the notification is an update.
+ */
+ protected boolean isUpdate () {
+
+ if (!options.getDict().has("updatedAt"))
+ return false;
+
+ long now = new Date().getTime();
+
+ long updatedAt = options.getDict().optLong("updatedAt", now);
+
+ return (now - updatedAt) < 1000;
+ }
+
+ /**
+ * Notification type can be one of pending or scheduled.
+ */
+ public Type getType () {
+ return isTriggered() ? Type.TRIGGERED : Type.SCHEDULED;
+ }
+
+ /**
+ * Schedule the local notification.
+ */
+ public void schedule() {
+ long triggerTime = options.getTriggerTime();
+
+ persist();
+
+ // Intent gets called when the Notification gets fired
+ Intent intent = new Intent(context, receiver)
+ .setAction(options.getIdStr())
+ .putExtra(Options.EXTRA, options.toString());
+
+ PendingIntent pi = PendingIntent.getBroadcast(
+ context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+
+ if (isRepeating()) {
+ getAlarmMgr().setRepeating(AlarmManager.RTC_WAKEUP,
+ triggerTime, options.getRepeatInterval(), pi);
+ } else {
+ getAlarmMgr().set(AlarmManager.RTC_WAKEUP, triggerTime, pi);
+ }
+ }
+
+ /**
+ * Clear the local notification without canceling repeating alarms.
+ *
+ */
+ public void clear () {
+ if (!isRepeating() && wasInThePast()) {
+ unpersist();
+ } else {
+ getNotMgr().cancel(getId());
+ }
+ }
+
+ /**
+ * Cancel the local notification.
+ *
+ * Create an intent that looks similar, to the one that was registered
+ * using schedule. Making sure the notification id in the action is the
+ * same. Now we can search for such an intent using the 'getService'
+ * method and cancel it.
+ */
+ public void cancel() {
+ Intent intent = new Intent(context, receiver)
+ .setAction(options.getIdStr());
+
+ PendingIntent pi = PendingIntent.
+ getBroadcast(context, 0, intent, 0);
+
+ getAlarmMgr().cancel(pi);
+ getNotMgr().cancel(options.getId());
+
+ unpersist();
+ }
+
+ /**
+ * Present the local notification to user.
+ */
+ public void show () {
+ // TODO Show dialog when in foreground
+ showNotification();
+ }
+
+ /**
+ * Show as local notification when in background.
+ */
+ @SuppressWarnings("deprecation")
+ private void showNotification () {
+ int id = getOptions().getId();
+
+ 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());
+ }
+ }
+
+ /**
+ * Show as modal dialog when in foreground.
+ */
+ private void showDialog () {
+ // TODO
+ }
+
+ /**
+ * 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("updatedAt");
+ json.remove("soundUri");
+ json.remove("iconUri");
+
+ return json.toString();
+ }
+
+ /**
+ * 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.
+ */
+ private SharedPreferences getPrefs () {
+ return context.getSharedPreferences(PREF_KEY, Context.MODE_PRIVATE);
+ }
+
+ /**
+ * Notification manager for the application.
+ */
+ private NotificationManager getNotMgr () {
+ return (NotificationManager) context
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ /**
+ * Alarm manager for the application.
+ */
+ private AlarmManager getAlarmMgr () {
+ 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;
+ }
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Options.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Options.java
new file mode 100644
index 00000000..198a52f4
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/Options.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2013-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 android.app.AlarmManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.net.Uri;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Date;
+
+/**
+ * Wrapper around the JSON object passed through JS which contains all
+ * possible option values. Class provides simple readers and more advanced
+ * methods to convert independent values into platform specific values.
+ */
+public class Options {
+
+ // Key name for bundled extras
+ static final String EXTRA = "NOTIFICATION_OPTIONS";
+
+ // The original JSON object
+ private JSONObject options = new JSONObject();
+
+ // Repeat interval
+ private long interval = 0;
+
+ // Application context
+ private final Context context;
+
+ // Asset util instance
+ private final AssetUtil assets;
+
+
+ /**
+ * Constructor
+ *
+ * @param context
+ * Application context
+ */
+ public Options(Context context){
+ this.context = context;
+ this.assets = AssetUtil.getInstance(context);
+ }
+
+ /**
+ * Parse given JSON properties.
+ *
+ * @param options
+ * JSON properties
+ */
+ public Options parse (JSONObject options) {
+ this.options = options;
+
+ parseInterval();
+ parseAssets();
+
+ return this;
+ }
+
+ /**
+ * Parse repeat interval.
+ */
+ private void parseInterval() {
+ String every = options.optString("every").toLowerCase();
+
+ 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("year")) {
+ interval = AlarmManager.INTERVAL_DAY * 365;
+ } else {
+ try {
+ interval = Integer.parseInt(every) * 60000;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Parse asset URIs.
+ */
+ private void parseAssets() {
+
+ if (options.has("iconUri"))
+ return;
+
+ Uri iconUri = assets.parse(options.optString("icon", "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.
+ */
+ public Context getContext () {
+ return context;
+ }
+
+ /**
+ * Wrapped JSON object.
+ */
+ JSONObject getDict () {
+ return options;
+ }
+
+ /**
+ * Text for the local notification.
+ */
+ public String getText() {
+ return options.optString("text", "");
+ }
+
+ /**
+ * Repeat interval (day, week, month, year, aso.)
+ */
+ public long getRepeatInterval() {
+ return interval;
+ }
+
+ /**
+ * Badge number for the local notification.
+ */
+ public int getBadgeNumber() {
+ return options.optInt("badge", 0);
+ }
+
+ /**
+ * ongoing flag for local notifications.
+ */
+ public Boolean isOngoing() {
+ return options.optBoolean("ongoing", false);
+ }
+
+ /**
+ * autoClear flag for local notifications.
+ */
+ public Boolean isAutoClear() {
+ 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 Math.max(
+ System.currentTimeMillis(),
+ options.optLong("at", 0) * 1000
+ );
+ }
+
+ /**
+ * Title for the local notification.
+ */
+ public String getTitle() {
+ String title = options.optString("title", "");
+
+ if (title.isEmpty()) {
+ title = context.getApplicationInfo().loadLabel(
+ context.getPackageManager()).toString();
+ }
+
+ return title;
+ }
+
+ /**
+ * @return
+ * The notification color for LED
+ */
+ public int getLedColor() {
+ String hex = options.optString("led", "000000");
+ int aRGB = Integer.parseInt(hex,16);
+
+ aRGB += 0xFF000000;
+
+ return aRGB;
+ }
+
+ /**
+ * Sound file path for the local notification.
+ */
+ public Uri getSoundUri() {
+ Uri uri = null;
+
+ try{
+ uri = Uri.parse(options.optString("soundUri"));
+ } catch (Exception e){
+ e.printStackTrace();
+ }
+
+ return uri;
+ }
+
+ /**
+ * Icon bitmap for the local notification.
+ */
+ public Bitmap getIconBitmap() {
+ String icon = options.optString("icon", "icon");
+ Bitmap bmp;
+
+ try{
+ Uri uri = Uri.parse(options.optString("iconUri"));
+ bmp = assets.getIconFromUri(uri);
+ } catch (Exception e){
+ bmp = assets.getIconFromDrawable(icon);
+ }
+
+ return bmp;
+ }
+
+ /**
+ * Small icon resource ID for the local notification.
+ */
+ public int getSmallIcon () {
+ String icon = options.optString("smallIcon", "");
+
+ int resId = assets.getResIdForDrawable(icon);
+
+ if (resId == 0) {
+ resId = android.R.drawable.screen_background_dark;
+ }
+
+ return resId;
+ }
+
+ /**
+ * JSON object as string.
+ */
+ public String toString() {
+ return options.toString();
+ }
+
+}
diff --git a/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/TriggerReceiver.java b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/TriggerReceiver.java
new file mode 100644
index 00000000..9427e31e
--- /dev/null
+++ b/plugins/de.appplant.cordova.plugin.local-notification/src/android/notification/TriggerReceiver.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013-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;
+
+/**
+ * The alarm receiver is triggered when a scheduled alarm is fired. This class
+ * reads the information in the intent and displays this information in the
+ * Android notification bar. The notification uses the default notification
+ * sound and it vibrates the phone.
+ */
+public class TriggerReceiver extends AbstractTriggerReceiver {
+
+ /**
+ * Called when a local notification was triggered. Does present the local
+ * notification and re-schedule the alarm if necessary.
+ *
+ * @param notification
+ * Wrapper around the local notification
+ * @param updated
+ * If an update has triggered or the original
+ */
+ @Override
+ public void onTrigger (Notification notification, boolean updated) {
+ notification.show();
+ }
+
+ /**
+ * Build notification specified by options.
+ *
+ * @param builder
+ * Notification builder
+ */
+ @Override
+ public Notification buildNotification (Builder builder) {
+ return builder.build();
+ }
+
+}