Commit 6e0cb626 by Sebastián Katzer

WIP Android

parent 8366689e
{
"name": "cordova-plugin-local-notifications",
"version": "0.0.0",
"version": "0.0.9",
"description": "Schedules and queries for local notifications",
"cordova": {
"id": "cordova-plugin-local-notifications",
......
......@@ -24,7 +24,7 @@
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="cordova-plugin-local-notifications"
version="0.0.0">
version="0.9.0">
<name>LocalNotification</name>
......@@ -40,10 +40,11 @@
<!-- cordova -->
<engines>
<engine name="cordova" version=">=3.6.0" />
<engine name="cordova-plugman" version=">=4.3.0" />
<engine name="cordova-windows" version=">=4.2.0" />
<engine name="apple-ios" version=">=10.0.0" />
<engine name="cordova" version=">=3.6.0" />
<engine name="cordova-plugman" version=">=4.3.0" />
<engine name="cordova-windows" version=">=4.2.0" />
<engine name="apple-ios" version=">=10.0.0" />
<engine name="cordova-android" version=">=6.0.0" />
</engines>
<!-- dependencies -->
......@@ -66,7 +67,6 @@
<!-- ios -->
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="LocalNotification">
<param name="ios-package" value="APPLocalNotification" onload="true" />
......@@ -91,13 +91,11 @@
<header-file src="src/ios/UNNotificationRequest+APPLocalNotification.h" />
<source-file src="src/ios/UNNotificationRequest+APPLocalNotification.m" />
</platform>
<!-- android -->
<!-- <platform name="android">
<framework src="com.android.support:support-v4:+" value="gradle" />
<platform name="android">
<framework src="com.android.support:support-v4:26.+" value="gradle" />
<config-file target="res/xml/config.xml" parent="/*">
<feature name="LocalNotification">
......@@ -106,7 +104,6 @@
</config-file>
<config-file target="AndroidManifest.xml" parent="/manifest/application">
<receiver
android:name="de.appplant.cordova.plugin.localnotification.TriggerReceiver"
android:exported="false" />
......@@ -118,33 +115,19 @@
<activity
android:name="de.appplant.cordova.plugin.localnotification.ClickActivity"
android:launchMode="singleInstance"
android:theme="@android:style/Theme.NoDisplay"
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:theme="@android:style/Theme.Translucent"
android:exported="false" />
<!--
<receiver android:name="de.appplant.cordova.plugin.localnotification.RestoreReceiver" android:exported="false" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</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 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" />
</config-file>
......@@ -163,41 +146,57 @@
<source-file
src="src/android/ClearReceiver.java"
target-dir="src/de/appplant/cordova/plugin/localnotification" />
<!--
<source-file
src="src/android/RestoreReceiver.java"
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
src="src/android/notification/AbstractClearReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
src="src/android/notification/activity/ClickActivity.java"
target-dir="src/de/appplant/cordova/plugin/notification/activity" />
<source-file
src="src/android/notification/AbstractClickActivity.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
src="src/android/notification/receiver/AbstractClearReceiver.java"
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
src="src/android/notification/AbstractRestoreReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
src="src/android/notification/receiver/ClearReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification/receiver" />
<source-file
src="src/android/notification/AbstractTriggerReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
src="src/android/notification/receiver/TriggerReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification/receiver" />
<source-file
src="src/android/notification/AssetUtil.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
src="src/android/notification/trigger/DateTrigger.java"
target-dir="src/de/appplant/cordova/plugin/notification/trigger" />
<source-file
src="src/android/notification/Builder.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
src="src/android/notification/trigger/IntervalTrigger.java"
target-dir="src/de/appplant/cordova/plugin/notification/trigger" />
<source-file
src="src/android/notification/ClearReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
src="src/android/notification/trigger/MatchTrigger.java"
target-dir="src/de/appplant/cordova/plugin/notification/trigger" />
<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" />
<source-file
......@@ -213,17 +212,12 @@
target-dir="src/de/appplant/cordova/plugin/notification" />
<source-file
src="src/android/notification/TriggerReceiver.java"
src="src/android/notification/Request.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
</platform> -->
</platform>
<!-- 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="x64" arch="x64" 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;
* notification manually. It un-persists the cleared notification from the
* 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.
*
* @param notification
* Wrapper around the local notification
* @param notification Wrapper around the local notification
*/
@Override
public void onClear (Notification notification) {
......
......@@ -21,22 +21,19 @@
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 {
public class ClickActivity extends de.appplant.cordova.plugin.notification.activity.ClickActivity {
/**
* Called when local notification was clicked by the user.
*
* @param notification
* Wrapper around the local notification
* @param notification Wrapper around the local notification
*/
@Override
public void onClick(Notification notification) {
......@@ -44,24 +41,11 @@ public class ClickActivity extends de.appplant.cordova.plugin.notification.Click
super.onClick(notification);
if (notification.getOptions().isOngoing())
return;
// if (notification.getOptions().isOngoing())
// return;
String event = notification.isRepeating() ? "clear" : "cancel";
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();
// String event = notification.isRepeating() ? "clear" : "cancel";
// LocalNotification.fireEvent(event, notification);
}
}
......@@ -39,6 +39,11 @@ import java.lang.Exception;
import de.appplant.cordova.plugin.notification.Manager;
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
......@@ -129,73 +134,77 @@ public class LocalNotification extends CordovaPlugin {
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")) {
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);
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();
}
// 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);
// }
}
});
......@@ -203,323 +212,341 @@ public class LocalNotification extends CordovaPlugin {
}
/**
* 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);
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.
* Ask if user has enabled permission for local notifications.
*
* @param updates
* Notification properties including their IDs
* @param command The callback context used when calling back into
* JavaScript.
*/
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);
private void check (CallbackContext command) {
boolean allowed = getNotificationMgr().hasPermission();
PluginResult result = new PluginResult(PluginResult.Status.OK, allowed);
command.sendPluginResult(result);
}
/**
* If a notification with an ID is scheduled.
* Request permission for local notifications.
*
* @param id
* Notification ID
* @param command
* The callback context used when calling back into JavaScript.
* @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);
private void request (CallbackContext command) {
check(command);
}
/**
* 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.
* Schedule multiple local notifications.
*
* @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.
* @param notifications The notifications to schedule.
*/
private void getOptions (String id, Notification.Type type,
CallbackContext command) {
private void schedule (JSONArray notifications) {
Manager mgr = getNotificationMgr();
JSONArray ids = new JSONArray().put(id);
PluginResult result;
for (int i = 0; i < notifications.length(); i++) {
JSONObject dict = notifications.optJSONObject(i);
Options options = new Options(dict);
Request request = new Request(options);
List<JSONObject> options =
getNotificationMgr().getOptionsBy(type, toList(ids));
Notification notification =
mgr.schedule(request, TriggerReceiver.class);
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));
fireEvent("schedule", notification);
}
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));
}
// /**
// * 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.
......@@ -548,10 +575,8 @@ public class LocalNotification extends CordovaPlugin {
/**
* 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.
* @param event The event name
* @param notification Optional notification to pass the id and properties.
*/
static void fireEvent (String event, Notification notification) {
String state = getApplicationState();
......@@ -570,8 +595,7 @@ public class LocalNotification extends CordovaPlugin {
/**
* Use this instead of deprecated sendJavascript
*
* @param js
* JS code snippet as string
* @param js JS code snippet as string
*/
private static synchronized void sendJavascript(final String js) {
......@@ -593,27 +617,26 @@ public class LocalNotification extends CordovaPlugin {
}
}
/**
* Convert JSON array of integers to List.
*
* @param ary
* Array of integers
*/
private List<Integer> toList (JSONArray ary) {
ArrayList<Integer> list = new ArrayList<Integer>();
// /**
// * 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));
}
// for (int i = 0; i < ary.length(); i++) {
// list.add(ary.optInt(i));
// }
return list;
}
// return list;
// }
/**
* Current application state.
*
* @return
* "background" or "foreground"
* @return "background" or "foreground"
*/
static String getApplicationState () {
return isInBackground ? "background" : "foreground";
......
......@@ -30,16 +30,14 @@ import de.appplant.cordova.plugin.notification.Notification;
* 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 {
public class TriggerReceiver extends de.appplant.cordova.plugin.notification.receiver.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
* @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) {
......@@ -53,13 +51,11 @@ public class TriggerReceiver extends de.appplant.cordova.plugin.notification.Tri
/**
* Build notification specified by options.
*
* @param builder
* Notification builder
* @param builder Notification builder
*/
@Override
public Notification buildNotification (Builder builder) {
return builder
.setTriggerReceiver(TriggerReceiver.class)
.setClickActivity(ClickActivity.class)
.setClearReceiver(ClearReceiver.class)
.build();
......
......@@ -24,13 +24,13 @@ 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;
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
* notification specified by JSON object passed from JS side.
......@@ -43,9 +43,6 @@ public class Builder {
// 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;
......@@ -55,21 +52,7 @@ public class Builder {
/**
* 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
* @param options Notification options
*/
public Builder(Options options) {
this.context = options.getContext();
......@@ -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.
*
* @param receiver
* Broadcast receiver
* @param receiver Broadcast receiver for the clear event.
*/
public Builder setClearReceiver(Class<?> receiver) {
this.clearReceiver = receiver;
......@@ -101,8 +72,7 @@ public class Builder {
/**
* Set click activity.
*
* @param activity
* Activity
* @param activity The activity to handler the click event.
*/
public Builder setClickActivity(Class<?> activity) {
this.clickActivity = activity;
......@@ -111,50 +81,45 @@ public class Builder {
/**
* Creates the notification with all its options passed through JS.
*
* @return The final notification to display.
*/
public Notification build() {
Uri sound = options.getSoundUri();
int smallIcon = options.getSmallIcon();
int ledColor = options.getLedColor();
NotificationCompat.Builder builder;
builder = new NotificationCompat.Builder(context)
.setDefaults(0)
builder = new NotificationCompat.Builder(context, "group")
.setDefaults(options.getDefaults())
.setContentTitle(options.getTitle())
.setContentText(options.getText())
.setNumber(options.getBadgeNumber())
.setTicker(options.getText())
.setAutoCancel(options.isAutoClear())
.setOngoing(options.isOngoing())
.setColor(options.getColor());
if (ledColor != 0) {
builder.setLights(ledColor, 100, 100);
}
if (sound != null) {
builder.setSound(sound);
}
if (smallIcon == 0) {
builder.setSmallIcon(options.getIcon());
.setOngoing(options.isSticky())
.setColor(options.getColor())
.setSound(options.getSound())
.setVisibility(options.getVisibility())
.setPriority(options.getPriority())
.setShowWhen(options.getShowWhen())
.setLights(options.getLedColor(), options.getLedOn(), options.getLedOff());
if (smallIcon != 0) {
builder.setSmallIcon(smallIcon);
builder.setLargeIcon(options.getLargeIcon());
} else {
builder.setSmallIcon(options.getSmallIcon());
builder.setLargeIcon(options.getIconBitmap());
builder.setSmallIcon(options.getIcon());
}
applyDeleteReceiver(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
* preferences.
*
* @param builder
* Local notification builder instance
* @param builder Local notification builder instance
*/
private void applyDeleteReceiver(NotificationCompat.Builder builder) {
......@@ -162,7 +127,7 @@ public class Builder {
return;
Intent intent = new Intent(context, clearReceiver)
.setAction(options.getIdStr())
.setAction(options.getIdentifier())
.putExtra(Options.EXTRA, options.toString());
PendingIntent deleteIntent = PendingIntent.getBroadcast(
......@@ -175,8 +140,7 @@ public class Builder {
* Set intent to handle the click event. Will bring the app to
* foreground.
*
* @param builder
* Local notification builder instance
* @param builder Local notification builder instance
*/
private void applyContentReceiver(NotificationCompat.Builder builder) {
......
......@@ -21,20 +21,30 @@
package de.appplant.cordova.plugin.notification;
import android.app.AlarmManager;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.support.v4.app.NotificationManagerCompat;
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;
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
......@@ -49,8 +59,7 @@ public class Manager {
/**
* Constructor
*
* @param context
* Application context
* @param context Application context
*/
private Manager(Context context){
this.context = context;
......@@ -59,398 +68,441 @@ public class Manager {
/**
* Static method to retrieve class instance.
*
* @param context
* Application context
* @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.put("updated", true);
} catch (JSONException ignore) {}
return schedule(options, receiver);
}
/**
* Clear local notification specified by ID.
*
* @param id
* The notification ID
* Check if app has local notification permission.
*/
public Notification clear (int id) {
Notification notification = get(id);
if (notification != null) {
notification.clear();
}
return notification;
public boolean hasPermission () {
return getNotMgr().areNotificationsEnabled();
}
/**
* Clear local notification specified by ID.
* Schedule local notification specified by request.
*
* @param id
* The notification ID
* @param request Set of notification options.
* @param receiver Receiver to handle the trigger event.
*/
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();
}
public Notification schedule (Request request, Class<?> receiver) {
Options options = request.getOptions();
AlarmManager mgr = getAlarmMgr();
getNotMgr().cancelAll();
}
do {
Date date = request.getTriggerDate();
/**
* Cancel all local notifications.
*/
public void cancelAll () {
List<Notification> notifications = getAll();
if (date == null)
continue;
for (Notification notification : notifications) {
notification.cancel();
}
Intent intent = new Intent(context, receiver)
.setAction(request.getIdentifier())
.putExtra(Request.EXTRA, request.getOccurrence())
.putExtra(Options.EXTRA, options.toString());
getNotMgr().cancelAll();
}
if (!date.after(new Date()) && trigger(intent, receiver))
continue;
/**
* All local notifications IDs.
*/
public List<Integer> getIds() {
Set<String> keys = getPrefs().getAll().keySet();
ArrayList<Integer> ids = new ArrayList<Integer>();
PendingIntent pi = PendingIntent.getBroadcast(
context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
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());
mgr.set(AlarmManager.RTC_WAKEUP, date.getTime(), pi);
} catch (Exception ignore) {
// 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
}
}
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());
}
}
} while (request.moveNext());
return options;
return new Notification(context, options);
}
/**
* Get existent local notification.
* Trigger local notification specified by options.
*
* @param id
* Notification ID
* @param intent The intent to broadcast.
* @param cls The broadcast class.
*/
public Notification get(int id) {
Map<String, ?> alarms = getPrefs().getAll();
String notId = Integer.toString(id);
JSONObject options;
if (!alarms.containsKey(notId))
return null;
private boolean trigger (Intent intent, Class<?> cls) {
BroadcastReceiver receiver;
try {
String json = alarms.get(notId).toString();
options = new JSONObject(json);
} catch (JSONException e) {
e.printStackTrace();
return null;
receiver = (BroadcastReceiver) cls.newInstance();
} catch (InstantiationException e) {
return false;
} catch (IllegalAccessException e) {
return false;
}
Builder builder = new Builder(context, options);
return builder.build();
receiver.onReceive(context, intent);
return true;
}
/**
* 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;
}
// /**
// * 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 () {
return context.getSharedPreferences(PREF_KEY, Context.MODE_PRIVATE);
private AlarmManager getAlarmMgr () {
return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
/**
* Notification manager for the application.
*/
private NotificationManager getNotMgr () {
return (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
private NotificationManagerCompat getNotMgr () {
return NotificationManagerCompat.from(context);
}
}
......@@ -23,6 +23,7 @@ package de.appplant.cordova.plugin.notification;
import android.app.AlarmManager;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
......@@ -36,6 +37,8 @@ import org.json.JSONObject;
import java.util.Date;
import de.appplant.cordova.plugin.notification.receiver.TriggerReceiver;
/**
* Wrapper class around OS notification class. Handles basic operations
* like show, delete, cancel for a single local notification instance.
......@@ -47,9 +50,6 @@ public class Notification {
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";
......@@ -62,27 +62,29 @@ public class Notification {
// 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
* @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;
Notification (Context context, Options options, NotificationCompat.Builder builder) {
this.context = context;
this.options = options;
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 {
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.
*
* @param keepFlag
* Set to false to remove the flag from the option map
*/
protected boolean isUpdate (boolean keepFlag) {
boolean updated = options.getDict().optBoolean("updated", false);
if (!keepFlag) {
options.getDict().remove("updated");
}
return updated;
}
/**
* Notification type can be one of pending or scheduled.
*/
public Type getType () {
return isScheduled() ? Type.SCHEDULED : Type.TRIGGERED;
}
// /**
// * 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.
// *
// * @param keepFlag
// * Set to false to remove the flag from the option map
// */
// protected boolean isUpdate (boolean keepFlag) {
// boolean updated = options.getDict().optBoolean("updated", false);
// if (!keepFlag) {
// options.getDict().remove("updated");
// }
// return updated;
// }
// /**
// * Notification type can be one of pending or scheduled.
// */
// public Type getType () {
// return isScheduled() ? Type.SCHEDULED : Type.TRIGGERED;
// }
/**
* Schedule the local notification.
*/
public void schedule() {
long triggerTime = options.getTriggerTime();
// long triggerTime = options.getTriggerTime();
persist();
// persist();
// Intent gets called when the Notification gets fired
Intent intent = new Intent(context, receiver)
.setAction(options.getIdStr())
.putExtra(Options.EXTRA, options.toString());
// // 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);
// 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);
}
// 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 () {
// /**
// * Clear the local notification without canceling repeating alarms.
// */
// public void clear () {
if (!isRepeating() && wasInThePast())
unpersist();
// if (!isRepeating() && wasInThePast())
// unpersist();
if (!isRepeating())
getNotMgr().cancel(getId());
}
// if (!isRepeating())
// 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());
// /**
// * 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);
// PendingIntent pi = PendingIntent.
// getBroadcast(context, 0, intent, 0);
getAlarmMgr().cancel(pi);
getNotMgr().cancel(options.getId());
// getAlarmMgr().cancel(pi);
// getNotMgr().cancel(options.getId());
unpersist();
}
// unpersist();
// }
/**
* Present the local notification to user.
......@@ -225,87 +227,80 @@ public class Notification {
/**
* 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());
}
}
/**
* 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");
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();
if (builder != null) {
getNotMgr().notify(getId(), builder.build());
}
}
/**
* 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();
}
}
// /**
// * 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");
// 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.
......@@ -329,14 +324,4 @@ public class Notification {
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 @@
package de.appplant.cordova.plugin.notification;
import android.app.AlarmManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import org.json.JSONException;
import org.json.JSONArray;
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
......@@ -40,119 +47,73 @@ import java.util.Date;
public class Options {
// Key name for bundled extras
static final String EXTRA = "NOTIFICATION_OPTIONS";
public static final String EXTRA = "NOTIFICATION_OPTIONS";
// The original JSON object
private JSONObject options = new JSONObject();
// Default icon path
private static final String DEFAULT_ICON = "res://icon";
// Repeat interval
private long interval = 0;
// The original JSON object
private final JSONObject options;
// Application context
// The application context
private final Context context;
// Asset util instance
private final AssetUtil assets;
/**
* Constructor
* When creating without a context, various methods might not work well.
*
* @param context
* Application context
* @param options The options dict map.
*/
public Options(Context context){
this.context = context;
this.assets = AssetUtil.getInstance(context);
public Options(JSONObject options) {
this.options = options;
this.context = null;
this.assets = null;
}
/**
* Parse given JSON properties.
* Constructor
*
* @param options
* JSON properties
* @param context The application context.
* @param options The options dict map.
*/
public Options parse (JSONObject options) {
public Options(Context context, JSONObject options) {
this.context = context;
this.options = options;
parseInterval();
parseAssets();
return this;
this.assets = AssetUtil.getInstance(context);
}
/**
* Parse repeat interval.
* Application context.
*/
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("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();
}
}
public Context getContext () {
return context;
}
/**
* Parse asset URIs.
* Wrapped JSON object.
*/
private void parseAssets() {
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();
}
JSONObject getDict () {
return options;
}
/**
* Application context.
* Gets the ID for the local notification.
*
* @return 0 if the user did not specify.
*/
public Context getContext () {
return context;
public Integer getId() {
return options.optInt("id", 0);
}
/**
* Wrapped JSON object.
* The identifier for the local notification.
*
* @return The notification ID as the string
*/
JSONObject getDict () {
return options;
public String getIdentifier() {
return getId().toString();
}
/**
......@@ -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.
*/
public int getBadgeNumber() {
int getBadgeNumber() {
return options.optInt("badge", 0);
}
/**
* ongoing flag for local notifications.
*/
public Boolean isOngoing() {
return options.optBoolean("ongoing", false);
Boolean isSticky() {
return options.optBoolean("sticky", false);
}
/**
* autoClear flag for local notifications.
*/
public Boolean isAutoClear() {
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 options.optLong("at", 0) * 1000;
}
/**
* Title for the local notification.
*/
public String getTitle() {
......@@ -233,65 +159,99 @@ public class Options {
}
/**
* @return
* The notification color for LED
* The notification color for LED.
*/
public int getLedColor() {
String hex = options.optString("led", null);
int getLedColor() {
Object cfg = options.opt("led");
String hex = null;
if (cfg instanceof String) {
hex = options.optString("led");
} else
if (cfg instanceof JSONArray) {
hex = options.optJSONArray("led").optString(0);
}
if (hex == null) {
if (hex == null)
return 0;
try {
hex = stripHex(hex);
int aRGB = Integer.parseInt(hex, 16);
return aRGB + 0xFF000000;
} catch (NumberFormatException e) {
e.printStackTrace();
}
int aRGB = Integer.parseInt(hex, 16);
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 aRGB + 0xFF000000;
return 1000;
}
/**
* @return
* The notification background color for the small icon
* Returns null, if no color is given.
* The notification color for LED.
*/
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() {
String hex = options.optString("color", null);
if (hex == null) {
if (hex == null)
return NotificationCompat.COLOR_DEFAULT;
}
int aRGB = Integer.parseInt(hex, 16);
try {
hex = stripHex(hex);
int aRGB = Integer.parseInt(hex, 16);
return aRGB + 0xFF000000;
} catch (NumberFormatException e) {
e.printStackTrace();
}
return aRGB + 0xFF000000;
return NotificationCompat.COLOR_DEFAULT;
}
/**
* 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;
public Uri getSound() {
return assets.parse(options.optString("sound"));
}
/**
* Icon bitmap for the local notification.
*/
public Bitmap getIconBitmap() {
Bitmap bmp;
Bitmap getLargeIcon() {
Uri uri = assets.parse(options.optString("icon", DEFAULT_ICON));
Bitmap bmp = null;
try {
Uri uri = Uri.parse(options.optString("iconUri"));
bmp = assets.getIconFromUri(uri);
} catch (Exception e){
e.printStackTrace();
bmp = assets.getIconFromDrawable("icon");
}
return bmp;
......@@ -301,15 +261,19 @@ public class Options {
* Icon resource ID for the local notification.
*/
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) {
resId = getSmallIcon();
}
if (resId == 0) {
resId = assets.getResId(DEFAULT_ICON);
}
if (resId == 0) {
resId = android.R.drawable.ic_popup_reminder;
}
......@@ -319,10 +283,115 @@ public class Options {
/**
* Small icon resource ID for the local notification.
*/
public int getSmallIcon () {
int getSmallIcon() {
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 {
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 @@
* limitations under the License.
*/
package de.appplant.cordova.plugin.notification;
package de.appplant.cordova.plugin.notification.activity;
import android.app.Activity;
import android.content.Context;
......@@ -29,6 +29,9 @@ import android.os.Bundle;
import org.json.JSONException;
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
* local notification and calls the event functions for further proceeding.
......@@ -38,8 +41,7 @@ abstract public class AbstractClickActivity extends Activity {
/**
* Called when local notification was clicked to launch the main intent.
*
* @param state
* Saved instance state
* @param state Saved instance state
*/
@Override
public void onCreate (Bundle state) {
......@@ -50,14 +52,12 @@ abstract public class AbstractClickActivity extends Activity {
Context context = getApplicationContext();
try {
String data = bundle.getString(Options.EXTRA);
JSONObject options = new JSONObject(data);
Builder builder =
new Builder(context, options);
String data = bundle.getString(Options.EXTRA);
JSONObject dict = new JSONObject(data);
Options options = new Options(context, dict);
Notification notification =
buildNotification(builder);
new Notification(context, options);
onClick(notification);
} catch (JSONException e) {
......@@ -78,20 +78,11 @@ abstract public class AbstractClickActivity extends Activity {
/**
* Called when local notification was clicked by the user.
*
* @param notification
* Wrapper around the local notification
* @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() {
......
......@@ -19,7 +19,9 @@
* 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.
......@@ -32,28 +34,17 @@ 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
* @param notification Wrapper around the local notification
*/
@Override
public void onClick(Notification notification) {
launchApp();
if (notification.isRepeating()) {
notification.clear();
} else {
notification.cancel();
}
}
/**
* Build notification specified by options.
*
* @param builder
* Notification builder
*/
public Notification buildNotification (Builder builder) {
return builder.build();
// if (notification.isRepeating()) {
// notification.clear();
// } else {
// notification.cancel();
// }
}
}
......@@ -19,7 +19,7 @@
* limitations under the License.
*/
package de.appplant.cordova.plugin.notification;
package de.appplant.cordova.plugin.notification.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
......@@ -29,6 +29,9 @@ import android.os.Bundle;
import org.json.JSONException;
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
* notification and calls the event functions for further proceeding.
......@@ -38,35 +41,31 @@ 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
* @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;
String data = bundle.getString(Options.EXTRA);
try {
String data = bundle.getString(Options.EXTRA);
options = new JSONObject(data);
JSONObject dict = new JSONObject(data);
Options options = new Options(context, dict);
Notification notification =
new Notification(context, options);
onClear(notification);
} 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
* @param notification Wrapper around the local notification
*/
abstract public void onClear (Notification notification);
......
......@@ -19,7 +19,7 @@
* limitations under the License.
*/
package de.appplant.cordova.plugin.notification;
package de.appplant.cordova.plugin.notification.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
......@@ -29,7 +29,9 @@ import android.os.Bundle;
import org.json.JSONException;
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
......@@ -40,10 +42,8 @@ abstract public class AbstractTriggerReceiver extends BroadcastReceiver {
/**
* Called when an alarm was triggered.
*
* @param context
* Application context
* @param intent
* Received intent with content data
* @param context Application context
* @param intent Received intent with content data
*/
@Override
public void onReceive(Context context, Intent intent) {
......@@ -54,21 +54,15 @@ abstract public class AbstractTriggerReceiver extends BroadcastReceiver {
String data = bundle.getString(Options.EXTRA);
JSONObject dict = new JSONObject(data);
options = new Options(context).parse(dict);
options = new Options(context, dict);
} catch (JSONException e) {
e.printStackTrace();
return;
}
if (options == null)
return;
if (isFirstAlarmInFuture(options))
return;
Builder builder = new Builder(options);
Builder builder = new Builder(options);
Notification notification = buildNotification(builder);
boolean updated = notification.isUpdate(false);
boolean updated = false;// notification.isUpdate(false);
onTrigger(notification, updated);
}
......@@ -76,45 +70,16 @@ abstract public class AbstractTriggerReceiver extends BroadcastReceiver {
/**
* Called when a local notification was triggered.
*
* @param notification
* Wrapper around the local notification
* @param updated
* If an update has triggered or the original
* @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
* @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);
}
}
......@@ -19,7 +19,9 @@
* 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
......@@ -31,12 +33,11 @@ public class ClearReceiver extends AbstractClearReceiver {
/**
* Called when a local notification was cleared from outside of the app.
*
* @param notification
* Wrapper around the local notification
* @param notification Wrapper around the local notification
*/
@Override
public void onClear (Notification notification) {
notification.clear();
// notification.clear();
}
}
......@@ -19,7 +19,10 @@
* 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
......@@ -33,10 +36,8 @@ 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
* @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) {
......@@ -46,8 +47,7 @@ public class TriggerReceiver extends AbstractTriggerReceiver {
/**
* Build notification specified by options.
*
* @param builder
* Notification builder
* @param builder Notification builder
*/
@Override
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 @@
* limitations under the License.
*/
package de.appplant.cordova.plugin.notification;
package de.appplant.cordova.plugin.notification.util;
import android.content.Context;
import android.content.res.AssetManager;
......@@ -42,20 +42,19 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.util.UUID;
import static android.media.RingtoneManager.TYPE_NOTIFICATION;
/**
* 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 {
public 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;
......@@ -63,8 +62,7 @@ class AssetUtil {
/**
* Constructor
*
* @param context
* Application context
* @param context Application context
*/
private AssetUtil(Context context) {
this.context = context;
......@@ -73,41 +71,22 @@ class AssetUtil {
/**
* Static method to retrieve class instance.
*
* @param context
* Application context
* @param context Application context
*/
static AssetUtil getInstance(Context context) {
public 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:")) {
public Uri parse (String path) {
if (path == null || path.isEmpty()) {
return Uri.EMPTY;
} else if (path.startsWith("res:")) {
return getUriForResourcePath(path);
} else if (path.startsWith("file:///")) {
return getUriFromPath(path);
......@@ -191,7 +170,7 @@ class AssetUtil {
*/
private Uri getUriForResourcePath(String path) {
String resPath = path.replaceFirst("res://", "");
int resId = getResIdForDrawable(resPath);
int resId = getResId(resPath);
File file = getTmpFile();
if (resId == 0) {
......@@ -296,14 +275,15 @@ class AssetUtil {
/**
* Resource ID for drawable.
*
* @param resPath
* Resource path as string
* @param resPath Resource path as string.
*
* @return The resource ID or 0 if not found.
*/
int getResIdForDrawable(String resPath) {
int resId = getResIdForDrawable(getPkgName(), resPath);
public int getResId(String resPath) {
int resId = getResId(context.getResources(), resPath);
if (resId == 0) {
resId = getResIdForDrawable("android", resPath);
resId = getResId(Resources.getSystem(), resPath);
}
return resId;
......@@ -312,56 +292,32 @@ class AssetUtil {
/**
* 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 = 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 res The resources where to look for.
* @param resPath The name of the resource.
*
* @param drawable
* Drawable resource name
* @return The resource ID or 0 if not found.
*/
Bitmap getIconFromDrawable (String drawable) {
Resources res = context.getResources();
int iconId;
iconId = getResIdForDrawable(getPkgName(), drawable);
private int getResId(Resources res, String resPath) {
String pkgName = getPkgName(res);
String resName = getBaseName(resPath);
int resId;
if (iconId == 0) {
iconId = getResIdForDrawable("android", drawable);
}
resId = res.getIdentifier(resName, "mipmap", pkgName);
if (iconId == 0) {
iconId = android.R.drawable.screen_background_dark_transparent;
if (resId == 0) {
resId = res.getIdentifier(resName, "drawable", pkgName);
}
return BitmapFactory.decodeResource(res, iconId);
return resId;
}
/**
* Convert URI to Bitmap.
*
* @param uri
* Internal image URI
* @param uri Internal image URI
*/
Bitmap getIconFromUri (Uri uri) throws IOException {
public Bitmap getIconFromUri(Uri uri) throws IOException {
InputStream input = context.getContentResolver().openInputStream(uri);
return BitmapFactory.decodeStream(input);
}
......@@ -422,10 +378,10 @@ class AssetUtil {
}
/**
* Package name specified by context.
* Package name specified by the resource bundle.
*/
private String getPkgName () {
return context.getPackageName();
private String getPkgName (Resources res) {
return res == Resources.getSystem() ? "android" : context.getPackageName();
}
}
......@@ -158,12 +158,13 @@
NSString* path = [dict objectForKey:@"sound"];
NSString* file;
if ([path isKindOfClass:NSNumber.class]) {
return [path boolValue] ? [UNNotificationSound defaultSound] : NULL;
}
if (!path.length)
return NULL;
if ([path isEqualToString:@"res://platform_default"])
return [UNNotificationSound defaultSound];
if ([path hasPrefix:@"file:/"]) {
file = [self soundNameForAsset:path];
} else
......
......@@ -51,12 +51,17 @@ exports.applyPlatformSpecificOptions = function () {
switch (device.platform) {
case 'Android':
defaults.icon = 'res://ic_popup_reminder';
defaults.smallIcon = undefined;
defaults.ongoing = false;
defaults.autoClear = true;
defaults.led = undefined;
defaults.color = undefined;
defaults.icon = 'res://icon';
defaults.smallIcon = undefined;
defaults.sticky = false;
defaults.autoClear = true;
defaults.led = true;
defaults.color = undefined;
defaults.vibrate = false;
defaults.lockscreen = true;
defaults.showWhen = true;
defaults.priority = 0;
defaults.defaults = 0;
break;
}
};
......@@ -111,14 +116,17 @@ exports.mergeWithDefaults = function (options) {
* @return [ Object ] The converted property list
*/
exports.convertProperties = function (options) {
if (options.id) {
if (isNaN(options.id)) {
options.id = this.getDefaults().id;
console.warn('Id is not a number: ' + options.id);
var parseToInt = function (prop, options) {
if (isNaN(options[prop])) {
console.warn(prop + ' is not a number: ' + options[prop]);
return this.getDefaults()[prop];
} else {
options.id = Number(options.id);
return Number(options[prop]);
}
};
if (options.id) {
options.id = parseToInt('id', options);
}
if (options.title) {
......@@ -130,12 +138,15 @@ exports.convertProperties = function (options) {
}
if (options.badge) {
if (isNaN(options.badge)) {
options.badge = this.getDefaults().badge;
console.warn('Badge number is not a number: ' + options.id);
} else {
options.badge = Number(options.badge);
}
options.badge = parseToInt('badge', options);
}
if (options.priority) {
options.priority = parseToInt('priority', options);
}
if (options.defaults) {
options.defaults = parseToInt('defaults', options);
}
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