Commit 6618cb2d by Sebastián Katzer

Update example

parent 4b537731
......@@ -11,12 +11,13 @@
</activity>
<service android:name="de.appplant.cordova.plugin.background.ForegroundService" />
<receiver android:name="de.appplant.cordova.plugin.localnotification.Receiver" />
<receiver android:name="de.appplant.cordova.plugin.localnotification.DeleteIntentReceiver" />
<receiver android:name="de.appplant.cordova.plugin.localnotification.Restore">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<activity android:launchMode="singleInstance" android:name="de.appplant.cordova.plugin.localnotification.ReceiverActivity" />
<activity android:launchMode="singleInstance" android:name="de.appplant.cordova.plugin.localnotification.ReceiverActivity" android:theme="@android:style/Theme.NoDisplay" />
</application>
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
......
......@@ -92,9 +92,11 @@
<a href="#" class="button" onclick="hasPermission()">Has permission?<br/><span class="hint">notification.local.hasPermission()</span></a>
<a href="#" class="button" onclick="registerPermission()">Register permission<br/><span class="hint">notification.local.registerPermission()</span></a>
<a href="#" class="button" onclick="schedule()">Schedule now<br/><span class="hint">notification.local.add()</span></a>
<a href="#" class="button" onclick="scheduleDelayed()">Schedule in 5 sec<br/><span class="hint">notification.local.add()</span></a>
<a href="#" class="button" onclick="scheduleMultiple()">Schedule multiple<br/><span class="hint">notification.local.add()</span></a>
<a href="#" class="button" onclick="scheduleDelayed()">Schedule in 5 sec<br/><span class="hint">notification.local.add([])</span></a>
<a href="#" class="button" onclick="scheduleMinutely()">Schedule every min<br/><span class="hint">notification.local.add()</span></a>
<a href="#" class="button" onclick="cancel()">Cancel<br/><span class="hint">notification.local.cancel()</span></a>
<a href="#" class="button" onclick="cancelMultiple()">Cancel multiple<br/><span class="hint">notification.local.cancel([])</span></a>
<a href="#" class="button" onclick="cancelAll()">Cancel all<br/><span class="hint">notification.local.cancelAll()</span></a>
<a href="#" class="button" onclick="getScheduledIds()">Scheduled IDs<br/><span class="hint">notification.local.getScheduledIds()</span></a>
<a href="#" class="button" onclick="isScheduled()">Is scheduled?<br/><span class="hint">notification.local.isScheduled()</span></a>
......@@ -107,7 +109,7 @@
<script type="text/javascript" src="js/index.js"></script>
<script type="text/javascript">
var counter = 0, id = 12;
var counter = 1, id = 12;
var callback = function () {
alert('finished or canceled');
......@@ -129,10 +131,26 @@
plugin.notification.local.add({
id: id,
message: 'Test Message ' + (++counter),
json: { test: 123 }
json: { test: id }
});
};
scheduleMultiple = function () {
plugin.notification.local.add([{
id: id,
message: 'Test Message ' + (++counter),
json: { test: id }
},{
id: id+1,
message: 'Test Message ' + (++counter),
json: { test: id+1 }
},{
id: id+2,
message: 'Test Message ' + (++counter),
json: { test: id+2 }
}]);
};
scheduleDelayed = function () {
var now = new Date().getTime(),
_5_sec_from_now = new Date(now + 5*1000);
......@@ -160,6 +178,11 @@
plugin.notification.local.cancel(id,callback);
};
cancelMultiple = function () {
counter = 0;
plugin.notification.local.cancel([id, id+1],callback);
};
cancelAll = function () {
counter = 0;
plugin.notification.local.cancelAll(callback);
......@@ -198,7 +221,7 @@
<!-- callbacks -->
<script type="text/javascript">
document.addEventListener('deviceready', function () {
document.addEventListener('sdeviceready', function () {
plugin.notification.local.onadd = function (id, state, json) {
alert('on add\n' + Array.apply(null, arguments).join("\n"));
};
......
......@@ -64,7 +64,7 @@ exports._defaults = {
message: '',
title: '',
autoCancel: false,
badge: 0,
badge: -1,
id: '0',
json: '',
repeat: ''
......@@ -104,63 +104,95 @@ exports.setDefaults = function (newDefaults) {
* A function to be called after the notification has been canceled
* @param {Object} scope
* The scope for the callback function
*
* @return {Number}
* The notification's ID
*/
exports.add = function (props, callback, scope) {
var options = this.mergeWithDefaults(props),
fn = this.createCallbackFn(callback, scope);
this.registerPermission(function(granted) {
if (options.id) {
options.id = options.id.toString();
}
if (!granted)
return;
if (options.date === undefined) {
options.date = new Date();
}
var notifications = Array.isArray(props) ? props : [props];
if (options.title) {
options.title = options.title.toString();
}
for (var i = 0; i < notifications.length; i++) {
var properties = notifications[i];
if (options.message) {
options.message = options.message.toString();
}
if (typeof options.date == 'object') {
options.date = Math.round(options.date.getTime()/1000);
}
if (typeof options.json == 'object') {
options.json = JSON.stringify(options.json);
}
this.mergeWithDefaults(properties);
this.convertProperties(properties);
}
if (['WinCE', 'Win32NT'].indexOf(device.platform) > -1) {
fn = function (cmd) {
eval(cmd);
};
}
if (device.platform != 'iOS') {
notifications = notifications[0];
}
exec(fn, null, 'LocalNotification', 'add', [options]);
this.exec('add', notifications, callback, scope);
}, this);
};
return options.id;
/**
* Update existing notification specified by ID in options.
*
* @param {Object} options
* The notification properties to update
* @param {Function} callback
* A function to be called after the notification has been updated
* @param {Object} scope
* The scope for the callback function
*/
exports.update = function (options, callback, scope) {
this.exec('update', options, callback, scope);
};
/**
* Cancels the specified notification.
* Clears the specified notification.
*
* @param {String} id
* The ID of the notification
* @param {Function} callback
* A function to be called after the notification has been canceled
* A function to be called after the notification has been cleared
* @param {Object} scope
* The scope for the callback function
*/
exports.cancel = function (id, callback, scope) {
var fn = this.createCallbackFn(callback, scope);
exports.clear = function (id, callback, scope) {
var notId = (id || '0').toString();
exec(fn, null, 'LocalNotification', 'cancel', [(id || '0').toString()]);
this.exec('clear', notId, callback, scope);
};
/**
* Clears all previously sheduled notifications.
*
* @param {Function} callback
* A function to be called after all notifications have been cleared
* @param {Object} scope
* The scope for the callback function
*/
exports.clearAll = function (callback, scope) {
this.exec('clearAll', null, callback, scope);
};
/**
* Cancels the specified notifications.
*
* @param {String[]} ids
* The IDs of the notifications
* @param {Function} callback
* A function to be called after the notifications has been canceled
* @param {Object} scope
* The scope for the callback function
*/
exports.cancel = function (ids, callback, scope) {
ids = Array.isArray(ids) ? ids : [ids];
for (var i = 0; i < ids.length; i++) {
ids[i] = ids[i].toString();
}
if (device.platform != 'iOS') {
ids = ids[0];
}
this.exec('cancel', ids, callback, scope);
};
/**
......@@ -172,9 +204,7 @@ exports.cancel = function (id, callback, scope) {
* The scope for the callback function
*/
exports.cancelAll = function (callback, scope) {
var fn = this.createCallbackFn(callback, scope);
exec(fn, null, 'LocalNotification', 'cancelAll', []);
this.exec('cancelAll', null, callback, scope);
};
/**
......@@ -186,9 +216,7 @@ exports.cancelAll = function (callback, scope) {
* The scope for the callback function
*/
exports.getScheduledIds = function (callback, scope) {
var fn = this.createCallbackFn(callback, scope);
exec(fn, null, 'LocalNotification', 'getScheduledIds', []);
this.exec('getScheduledIds', null, callback, scope);
};
/**
......@@ -202,9 +230,9 @@ exports.getScheduledIds = function (callback, scope) {
* The scope for the callback function
*/
exports.isScheduled = function (id, callback, scope) {
var fn = this.createCallbackFn(callback, scope);
var notId = (id || '0').toString();
exec(fn, null, 'LocalNotification', 'isScheduled', [id.toString()]);
this.exec('isScheduled', notId, callback, scope);
};
/**
......@@ -216,9 +244,7 @@ exports.isScheduled = function (id, callback, scope) {
* The scope for the callback function
*/
exports.getTriggeredIds = function (callback, scope) {
var fn = this.createCallbackFn(callback, scope);
exec(fn, null, 'LocalNotification', 'getTriggeredIds', []);
this.exec('getTriggeredIds', null, callback, scope);
};
/**
......@@ -232,9 +258,9 @@ exports.getTriggeredIds = function (callback, scope) {
* The scope for the callback function
*/
exports.isTriggered = function (id, callback, scope) {
var fn = this.createCallbackFn(callback, scope);
var notId = (id || '0').toString();
exec(fn, null, 'LocalNotification', 'isTriggered', [id.toString()]);
this.exec('isTriggered', notId, callback, scope);
};
/**
......@@ -267,18 +293,127 @@ exports.hasPermission = function (callback, scope) {
exports.registerPermission = function (callback, scope) {
var fn = this.createCallbackFn(callback, scope);
if (device.platform != 'iOS')
if (device.platform != 'iOS') {
fn(true);
return;
}
exec(fn, null, 'LocalNotification', 'registerPermission', []);
};
/**
* @deprecated
*
* Register permission to show notifications if not already granted.
*
* @param {Function} callback
* The function to be exec as the callback
* @param {Object?} scope
* The callback function's scope
*/
exports.promptForPermission = function (callback, scope) {
console.warn('Depreated: Please use `notification.local.registerPermission` instead.');
exports.registerPermission.apply(this, arguments);
};
/**
* Add new entries to the registry (more than one)
*
* @param {Object} options
* The notification properties
* @param {Function} callback
* A function to be called after the notification has been added
* @param {Object} scope
* The scope for the callback function
*
* @return {Number}
* The notification's ID
*/
exports.addMultiple = function (notifications, callback, scope) {
var length = notifications.length;
var notificationsMerged = new Array(),
callbackFn = this.createCallbackFn(callback, scope);
for (var i=0;i<length;i++){
var options = this.mergeWithDefaults(notifications[i]);
if (options.id) {
options.id = options.id.toString();
}
if (options.date === undefined) {
options.date = new Date();
}
if (options.title) {
options.title = options.title.toString();
}
if (options.message) {
options.message = options.message.toString();
}
if (typeof options.date == 'object') {
options.date = Math.round(options.date.getTime()/1000);
}
if (['WinCE', 'Win32NT'].indexOf(device.platform) > -1) {
callbackFn = function (cmd) {
eval(cmd);
};
}
notificationsMerged.push(options);
}
cordova.exec(callbackFn, null, 'LocalNotification', 'addMultiple', notificationsMerged);
return options.id;
};
/**
* Clear the specified notifications (more than one).
*
* @param {String} id
* The ID of the notification
* @param {Function} callback
* A function to be called after the notifications has been cleared.
* @param {Object} scope
* The scope for the callback function
*/
exports.clearMultiple = function (ids, callback, scope) {
var length = ids.length;
var idArray = new Array(),
callbackFn = this.createCallbackFn(callback, scope);
for (var i=0;i<length;i++){
var id = ids[i].toString();
idArray.push(id);
}
var callbackFn = this.createCallbackFn(callback, scope);
cordova.exec(callbackFn, null, 'LocalNotification', 'clearMultiple', [ids]);
};
/**
* Cancel the specified notifications (more than one).
*
* @param {String} id
* The ID of the notification
* @param {Function} callback
* A function to be called after the notifications has been canceled
* @param {Object} scope
* The scope for the callback function
*/
exports.cancelMultiple = function (ids, callback, scope) {
var length = ids.length;
var idArray = new Array(),
callbackFn = this.createCallbackFn(callback, scope);
for (var i=0;i<length;i++){
var id = ids[i].toString();
idArray.push(id);
}
var callbackFn = this.createCallbackFn(callback, scope);
cordova.exec(callbackFn, null, 'LocalNotification', 'cancelMultiple', [ids]);
};
/**
* Occurs when a notification was added.
*
* @param {String} id
......@@ -287,8 +422,10 @@ exports.promptForPermission = function (callback, scope) {
* Either "foreground" or "background"
* @param {String} json
* A custom (JSON) string
* @param {Object} data
* The notification properties
*/
exports.onadd = function (id, state, json) {};
exports.onadd = function (id, state, json, data) {};
/**
* Occurs when the notification is triggered.
......@@ -299,8 +436,10 @@ exports.onadd = function (id, state, json) {};
* Either "foreground" or "background"
* @param {String} json
* A custom (JSON) string
* @param {Object} data
* The notification properties
*/
exports.ontrigger = function (id, state, json) {};
exports.ontrigger = function (id, state, json, data) {};
/**
* Fires after the notification was clicked.
......@@ -311,8 +450,10 @@ exports.ontrigger = function (id, state, json) {};
* Either "foreground" or "background"
* @param {String} json
* A custom (JSON) string
* @param {Object} data
* The notification properties
*/
exports.onclick = function (id, state, json) {};
exports.onclick = function (id, state, json, data) {};
/**
* Fires if the notification was canceled.
......@@ -323,8 +464,24 @@ exports.onclick = function (id, state, json) {};
* Either "foreground" or "background"
* @param {String} json
* A custom (JSON) string
* @param {Object} data
* The notification properties
*/
exports.oncancel = function (id, state, json, data) {};
/**
* Get fired when the notification was cleared.
*
* @param {String} id
* The ID of the notification
* @param {String} state
* Either "foreground" or "background"
* @param {String} json
* A custom (JSON) string
* @param {Object} data
* The notification properties
*/
exports.oncancel = function (id, state, json) {};
exports.onclear = function (id, state, json, data) {};
/**
......@@ -353,6 +510,49 @@ exports.mergeWithDefaults = function (options) {
/**
* @private
*
* Convert the passed values to their required type.
*
* @param {Object} options
* Set of custom values
*
* @retrun {Object}
* The converted property list
*/
exports.convertProperties = function (options) {
if (options.id) {
options.id = options.id.toString();
}
if (options.date === undefined) {
options.date = new Date();
}
if (options.title) {
options.title = options.title.toString();
}
if (options.message) {
options.message = options.message.toString();
}
if (options.text) {
options.message = options.text.toString();
}
if (typeof options.date == 'object') {
options.date = Math.round(options.date.getTime()/1000);
}
if (typeof options.json == 'object') {
options.json = JSON.stringify(options.json);
}
return options;
};
/**
* @private
*
* Merges the platform specific properties into the default properties.
*
* @return {Object}
......@@ -401,4 +601,31 @@ exports.createCallbackFn = function (callbackFn, scope) {
};
};
/**
* @private
*
* Executes the native counterpart.
*
* @param {String} action
* The name of the action
* @param args[]
* Array of arguments
* @param {Function} callback
* The callback function
* @param {Object} scope
* The scope for the function
*/
exports.exec = function (action, args, callback, scope) {
var fn = this.createCallbackFn(callback, scope),
params = [];
if (Array.isArray(args)) {
params = args;
} else if (args) {
params.push(args);
}
exec(fn, null, 'LocalNotification', action, params);
};
});
/*
Copyright 2013-2014 appPlant UG
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package de.appplant.cordova.plugin.localnotification;
import java.util.Date;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
public class DeleteIntentReceiver extends BroadcastReceiver {
public static final String OPTIONS = "LOCAL_NOTIFICATION_OPTIONS";
@Override
public void onReceive(Context context, Intent intent) {
Options options = null;
Bundle bundle = intent.getExtras();
JSONObject args;
try {
args = new JSONObject(bundle.getString(OPTIONS));
options = new Options(context).parse(args);
} catch (JSONException e) {
return;
}
// The context may got lost if the app was not running before
LocalNotification.setContext(context);
Date now = new Date();
if ((options.getInterval()!=0)){
LocalNotification.persist(options.getId(), setInitDate(args));
}
else if((new Date(options.getDate()).before(now))){
LocalNotification.unpersist(options.getId());
}
fireClearEvent(options);
}
private JSONObject setInitDate(JSONObject arguments){
long initialDate = arguments.optLong("date", 0) * 1000;
try {
arguments.put("initialDate", initialDate);
} catch (JSONException e) {
e.printStackTrace();
}
return arguments;
}
/**
* Fires onclear event.
*/
private void fireClearEvent (Options options) {
LocalNotification.fireEvent("clear", options.getId(), options.getJSON());
}
}
......@@ -35,6 +35,7 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
......@@ -43,6 +44,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Build;
import android.widget.Toast;
/**
* This plugin utilizes the Android AlarmManager in combination with StatusBar
......@@ -59,6 +61,7 @@ public class LocalNotification extends CordovaPlugin {
protected static Context context = null;
protected static Boolean isInBackground = true;
private static ArrayList<String> eventQueue = new ArrayList<String>();
static Activity activity;
@Override
public void initialize (CordovaInterface cordova, CordovaWebView webView) {
......@@ -66,6 +69,7 @@ public class LocalNotification extends CordovaPlugin {
LocalNotification.webView = super.webView;
LocalNotification.context = super.cordova.getActivity().getApplicationContext();
LocalNotification.activity = super.cordova.getActivity();
}
@Override
......@@ -73,14 +77,70 @@ public class LocalNotification extends CordovaPlugin {
if (action.equalsIgnoreCase("add")) {
cordova.getThreadPool().execute( new Runnable() {
public void run() {
JSONObject arguments = setInitDate(args).optJSONObject(0);
JSONObject arguments = setInitDate(args.optJSONObject(0));
Options options = new Options(context).parse(arguments);
add(options, true);
command.success();
}
});
}
if (action.equalsIgnoreCase("addMultiple")) {
cordova.getThreadPool().execute( new Runnable() {
public void run() {
JSONArray notifications = args.optJSONArray(0);
for (int i =0; i<notifications.length();i++){
JSONObject arguments = setInitDate(notifications.optJSONObject(i));
Options options = new Options(context).parse(arguments);
add(options, true);
}
command.success();
}
});
}
if (action.equalsIgnoreCase("update")) {
cordova.getThreadPool().execute( new Runnable() {
public void run() {
JSONObject updates = args.optJSONObject(0);
update(updates);
command.success();
}
});
}
if (action.equalsIgnoreCase("clear")) {
cordova.getThreadPool().execute( new Runnable() {
public void run() {
String id = args.optString(0);
clear(id);
command.success();
}
});
}
if (action.equalsIgnoreCase("clearMultiple")) {
cordova.getThreadPool().execute( new Runnable() {
public void run() {
JSONArray ids = args.optJSONArray(0);
for (int i =0; i<ids.length();i++){
clear(ids.optString(i));
}
command.success();
}
});
}
if (action.equalsIgnoreCase("clearAll")) {
cordova.getThreadPool().execute( new Runnable() {
public void run() {
clearAll();
command.success();
}
});
}
if (action.equalsIgnoreCase("cancel")) {
cordova.getThreadPool().execute( new Runnable() {
......@@ -93,6 +153,18 @@ public class LocalNotification extends CordovaPlugin {
}
});
}
if (action.equalsIgnoreCase("cancelMultiple")) {
cordova.getThreadPool().execute( new Runnable() {
public void run() {
JSONArray ids = args.optJSONArray(0);
for (int i =0; i<ids.length();i++){
cancel(ids.optString(i));
}
command.success();
}
});
}
if (action.equalsIgnoreCase("cancelAll")) {
cordova.getThreadPool().execute( new Runnable() {
......@@ -169,6 +241,7 @@ public class LocalNotification extends CordovaPlugin {
persist(options.getId(), options.getJSONObject());
//Intent is called when the Notification gets fired
Intent intent = new Intent(context, Receiver.class)
.setAction("" + options.getId())
.putExtra(Receiver.OPTIONS, options.getJSONObject().toString());
......@@ -183,6 +256,91 @@ public class LocalNotification extends CordovaPlugin {
am.set(AlarmManager.RTC_WAKEUP, triggerTime, pi);
}
/**
* Update an existing notification
*
* @param updates JSONObject with update-content
*/
public static void update (JSONObject updates){
String id = updates.optString("id", "0");
// update shared preferences
SharedPreferences settings = getSharedPreferences();
Map<String, ?> alarms = settings.getAll();
JSONObject arguments;
try {
arguments = new JSONObject(alarms.get(id).toString());
} catch (JSONException e) {
e.printStackTrace();
return;
}
arguments = updateArguments(arguments, updates);
// cancel existing alarm
Intent intent = new Intent(context, Receiver.class)
.setAction("" + id);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
AlarmManager am = getAlarmManager();
am.cancel(pi);
//add new alarm
Options options = new Options(context).parse(arguments);
add(options,false);
}
/**
* Clear a specific notification without canceling repeating alarms
*
* @param notificationID
* The original ID of the notification that was used when it was
* registered using add()
*/
public static void clear (String notificationId){
SharedPreferences settings = getSharedPreferences();
Map<String, ?> alarms = settings.getAll();
NotificationManager nc = getNotificationManager();
try {
nc.cancel(Integer.parseInt(notificationId));
} catch (Exception e) {}
JSONObject arguments;
try {
arguments = new JSONObject(alarms.get(notificationId).toString());
Options options = new Options(context).parse(arguments);
Date now = new Date();
if ((options.getInterval()!=0)){
persist(notificationId, setInitDate(arguments));
}
else if((new Date(options.getDate()).before(now))){
unpersist(notificationId);
}
} catch (JSONException e) {
e.printStackTrace();
return;
}
fireEvent("clear", notificationId, "");
}
/**
* Clear all notifications without canceling repeating alarms
*/
public static void clearAll (){
SharedPreferences settings = getSharedPreferences();
NotificationManager nc = getNotificationManager();
Map<String, ?> alarms = settings.getAll();
Set<String> alarmIds = alarms.keySet();
for (String alarmId : alarmIds) {
clear(alarmId);
}
nc.cancelAll();
}
/**
* Cancel a specific notification that was previously registered.
*
......@@ -490,15 +648,50 @@ public class LocalNotification extends CordovaPlugin {
* @param args The given JSONArray
* @return A new JSONArray with the parameter "initialDate" set.
*/
private static JSONArray setInitDate(JSONArray args){
long initialDate = args.optJSONObject(0).optLong("date", 0) * 1000;
private static JSONObject setInitDate(JSONObject arguments){
long initialDate = arguments.optLong("date", 0) * 1000;
try {
args.optJSONObject(0).put("initialDate", initialDate);
arguments.put("initialDate", initialDate);
} catch (JSONException e) {
e.printStackTrace();
}
return args;
return arguments;
}
private static JSONObject updateArguments(JSONObject arguments,JSONObject updates){
try {
if(!updates.isNull("message")){
arguments.put("message", updates.get("message"));
}
if(!updates.isNull("title")){
arguments.put("title", updates.get("title"));
}
if(!updates.isNull("badge")){
arguments.put("badge", updates.get("badge"));
}
if(!updates.isNull("sound")){
arguments.put("sound", updates.get("sound"));
}
if(!updates.isNull("icon")){
arguments.put("icon", updates.get("icon"));
}
} catch (JSONException jse){
jse.printStackTrace();
}
return arguments;
}
public static void showNotification(String title,String notification){
int duration = Toast.LENGTH_LONG;
if(title.equals("")){
title = "Notification";
}
String text = title + " \n " + notification;
Toast notificationToast = Toast.makeText(context, text, duration);
notificationToast.show();
}
}
\ No newline at end of file
}
......@@ -21,8 +21,11 @@
package de.appplant.cordova.plugin.localnotification;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Calendar;
......@@ -42,12 +45,13 @@ import android.media.RingtoneManager;
import android.net.Uri;
import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy;
import android.util.Log;
/**
* Class that helps to store the options that can be specified per alarm.
*/
public class Options {
static protected final String STORAGE_FOLDER = "/localnotification";
private JSONObject options = new JSONObject();
private String packageName = null;
private long interval = 0;
......@@ -101,7 +105,7 @@ public class Options {
return this;
}
/**
* Returns options as JSON object
*/
......@@ -160,7 +164,7 @@ public class Options {
return RingtoneManager.getDefaultUri(soundId);
} catch (Exception e) {
return Uri.parse(sound);
return getURIfromPath(sound);
}
}
......@@ -176,7 +180,7 @@ public class Options {
if (icon.startsWith("http")) {
bmp = getIconFromURL(icon);
} else if (icon.startsWith("file://")) {
} else if (icon.startsWith("file://") || (icon.startsWith("res"))) {
bmp = getIconFromURI(icon);
}
......@@ -352,13 +356,11 @@ public class Options {
* The corresponding bitmap
*/
private Bitmap getIconFromURI (String src) {
AssetManager assets = LocalNotification.context.getAssets();
Bitmap bmp = null;
Uri uri = getURIfromPath(src);
try {
String path = src.replace("file:/", "www");
InputStream input = assets.open(path);
try {
InputStream input = LocalNotification.activity.getContentResolver().openInputStream(uri);
bmp = BitmapFactory.decodeStream(input);
} catch (IOException e) {
e.printStackTrace();
......@@ -366,4 +368,166 @@ public class Options {
return bmp;
}
}
\ No newline at end of file
/**
* The URI for a path.
*
* @param path The given path
*
* @return The URI pointing to the given path
*/
private Uri getURIfromPath(String path){
if (path.startsWith("res:")) {
return getUriForResourcePath(path);
} else if (path.startsWith("file:///")) {
return getUriForAbsolutePath(path);
} else if (path.startsWith("file://")) {
return getUriForAssetPath(path);
}
return Uri.parse(path);
}
/**
* The URI for a file.
*
* @param path
* The given absolute path
*
* @return The URI pointing to the given path
*/
private Uri getUriForAbsolutePath(String path) {
String absPath = path.replaceFirst("file://", "");
File file = new File(absPath);
if (!file.exists()) {
Log.e("LocalNotifocation", "File not found: " + file.getAbsolutePath());
}
return Uri.fromFile(file);
}
/**
* The URI for an asset.
*
* @param path
* The given asset path
*
* @return The URI pointing to the given path
*/
private Uri getUriForAssetPath(String path) {
String resPath = path.replaceFirst("file:/", "www");
String fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
File dir = LocalNotification.activity.getExternalCacheDir();
if (dir == null) {
Log.e("LocalNotifocation", "Missing external cache dir");
return Uri.EMPTY;
}
String storage = dir.toString() + STORAGE_FOLDER;
File file = new File(storage, fileName);
new File(storage).mkdir();
try {
AssetManager assets = LocalNotification.activity.getAssets();
FileOutputStream outStream = new FileOutputStream(file);
InputStream inputStream = assets.open(resPath);
copyFile(inputStream, outStream);
outStream.flush();
outStream.close();
} catch (Exception e) {
Log.e("LocalNotifocation", "File not found: assets/" + resPath);
e.printStackTrace();
}
return Uri.fromFile(file);
}
/**
* The URI for a resource.
*
* @param path
* The given relative path
*
* @return The URI pointing to the given path
*/
private Uri getUriForResourcePath(String path) {
String resPath = path.replaceFirst("res://", "");
String fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
String resName = fileName.substring(0, fileName.lastIndexOf('.'));
String extension = resPath.substring(resPath.lastIndexOf('.'));
File dir = LocalNotification.activity.getExternalCacheDir();
if (dir == null) {
Log.e("LocalNotifocation", "Missing external cache dir");
return Uri.EMPTY;
}
String storage = dir.toString() + STORAGE_FOLDER;
int resId = getResId(resPath);
File file = new File(storage, resName + extension);
if (resId == 0) {
Log.e("LocalNotifocation", "File not found: " + resPath);
}
new File(storage).mkdir();
try {
Resources res = LocalNotification.activity.getResources();
FileOutputStream outStream = new FileOutputStream(file);
InputStream inputStream = res.openRawResource(resId);
copyFile(inputStream, outStream);
outStream.flush();
outStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return Uri.fromFile(file);
}
/**
* Writes an InputStream to an OutputStream
*
* @param in
* The input stream
* @param out
* The output stream
*/
private void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
/**
* @return The resource ID for the given resource.
*/
private int getResId(String resPath) {
Resources res = LocalNotification.activity.getResources();
int resId;
String pkgName = getPackageName();
String dirName = "drawable";
String fileName = resPath;
if (resPath.contains("/")) {
dirName = resPath.substring(0, resPath.lastIndexOf('/'));
fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
}
String resName = fileName.substring(0, fileName.lastIndexOf('.'));
resId = res.getIdentifier(resName, dirName, pkgName);
if (resId == 0) {
resId = res.getIdentifier(resName, "drawable", pkgName);
}
return resId;
}
/**
* The name for the package.
*
* @return The package name
*/
private String getPackageName() {
return LocalNotification.activity.getPackageName();
}
/**
* Shows the behavior of notifications when the application is in foreground
*
*
*/
public boolean getForegroundMode(){
return options.optBoolean("foregroundMode",false);
}
}
......@@ -79,10 +79,17 @@ public class Receiver extends BroadcastReceiver {
} else {
LocalNotification.add(options.moveDate(), false);
}
if (!LocalNotification.isInBackground && options.getForegroundMode()){
if (options.getInterval() == 0) {
LocalNotification.unpersist(options.getId());
}
LocalNotification.showNotification(options.getTitle(), options.getMessage());
fireTriggerEvent();
} else {
Builder notification = buildNotification();
Builder notification = buildNotification();
showNotification(notification);
showNotification(notification);
}
}
/*
......@@ -116,7 +123,13 @@ public class Receiver extends BroadcastReceiver {
@SuppressLint("NewApi")
private Builder buildNotification () {
Uri sound = options.getSound();
//DeleteIntent is called when the user clears a notification manually
Intent deleteIntent = new Intent(context, DeleteIntentReceiver.class)
.setAction("" + options.getId())
.putExtra(Receiver.OPTIONS, options.getJSONObject().toString());
PendingIntent dpi = PendingIntent.getBroadcast(context, 0, deleteIntent, PendingIntent.FLAG_CANCEL_CURRENT);
Builder notification = new NotificationCompat.Builder(context)
.setDefaults(0) // Do not inherit any defaults
.setContentTitle(options.getTitle())
......@@ -127,7 +140,8 @@ public class Receiver extends BroadcastReceiver {
.setLargeIcon(options.getIcon())
.setAutoCancel(options.getAutoCancel())
.setOngoing(options.getOngoing())
.setLights(options.getColor(), 500, 500);
.setLights(options.getColor(), 500, 500)
.setDeleteIntent(dpi);
if (sound != null) {
notification.setSound(sound);
......
......@@ -5,12 +5,10 @@
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
0B33C5FC1B224A91B96FDE79 /* CDVDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 212EBD1D7BCB4409838CD818 /* CDVDevice.m */; };
1D3623260D0F684500981E51 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* AppDelegate.m */; };
1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; };
1E733C86398D48E8A1BC03FC /* UIApplication+APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 3FAE800EBDCA42B2A38B569B /* UIApplication+APPLocalNotification.m */; };
288765FD0DF74451002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765FC0DF74451002DB57D /* CoreGraphics.framework */; };
301BF552109A68D80062928A /* libCordova.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 301BF535109A57CC0062928A /* libCordova.a */; };
302D95F114D2391D003F00A1 /* MainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 302D95EF14D2391D003F00A1 /* MainViewController.m */; };
......@@ -33,7 +31,6 @@
3DBE1CCC022B412AA9E356AF /* APPBackgroundMode.m in Sources */ = {isa = PBXBuildFile; fileRef = DDDFCE9D76CA4692A1F4A984 /* APPBackgroundMode.m */; };
5B1594DD16A7569C00FEF299 /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B1594DC16A7569C00FEF299 /* AssetsLibrary.framework */; };
6AD81238C9A04FD19E0ACCFD /* appbeep.wav in Resources */ = {isa = PBXBuildFile; fileRef = E02030E711134A5D8215D2F0 /* appbeep.wav */; };
715548D3C8114B559B6D4496 /* UILocalNotification+APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = DAB48B85CF1041E08D93312D /* UILocalNotification+APPLocalNotification.m */; };
7E7966DE1810823500FA85AD /* icon-40.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D41810823500FA85AD /* icon-40.png */; };
7E7966DF1810823500FA85AD /* icon-40@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D51810823500FA85AD /* icon-40@2x.png */; };
7E7966E01810823500FA85AD /* icon-50.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966D61810823500FA85AD /* icon-50.png */; };
......@@ -46,10 +43,12 @@
7E7966E71810823500FA85AD /* icon-small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7E7966DD1810823500FA85AD /* icon-small@2x.png */; };
9E1A68381A55FA1B00FBCA66 /* beep.caf in Resources */ = {isa = PBXBuildFile; fileRef = 9E1A68371A55FA1B00FBCA66 /* beep.caf */; };
9E5A62291A52DE07002E41A3 /* AppDelegate+APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E5A62281A52DE07002E41A3 /* AppDelegate+APPLocalNotification.m */; };
A5E31957B0D14EDA9EA08E51 /* UIApplication+APPLocalNotification.h in Sources */ = {isa = PBXBuildFile; fileRef = 49F49C19231D4BBE83DBF480 /* UIApplication+APPLocalNotification.h */; };
B0372583B02241029FBE505C /* APPLocalNotificationOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = B15E0B313C114B988FFDB3DF /* APPLocalNotificationOptions.m */; };
B8633FAA70DB4A8BADBA0143 /* APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = B1D78B105CE344DD96990060 /* APPLocalNotification.m */; };
D4A0D8761607E02300AEF8BB /* Default-568h@2x~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = D4A0D8751607E02300AEF8BB /* Default-568h@2x~iphone.png */; };
26914B69E52E40E893E4C399 /* APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C396B7AA36E4F79A25451A3 /* APPLocalNotification.m */; };
979DDA8725D74CA3A4E545F8 /* APPLocalNotificationOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 51E3BEE46964498795BC65EC /* APPLocalNotificationOptions.m */; };
0FFC7382D3B74FADB62619F0 /* UIApplication+APPLocalNotification.h in Resources */ = {isa = PBXBuildFile; fileRef = 5ABA551B6BA54E6885F278AA /* UIApplication+APPLocalNotification.h */; };
B806D27D183342679EFA5FDA /* UIApplication+APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AED74474D334999971FB4EC /* UIApplication+APPLocalNotification.m */; };
C6FCBB7BC1B947B699E811A8 /* UILocalNotification+APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 276D2A95483249CBA0DAE4F5 /* UILocalNotification+APPLocalNotification.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
......@@ -98,12 +97,7 @@
30FC414816E50CA1004E6F35 /* icon-72@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-72@2x.png"; sourceTree = "<group>"; };
32CA4F630368D1EE00C91783 /* NotificationExample-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NotificationExample-Prefix.pch"; sourceTree = "<group>"; };
3C888438876C480D8B36E11A /* APPBackgroundMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = APPBackgroundMode.h; path = "de.appplant.cordova.plugin.background-mode/APPBackgroundMode.h"; sourceTree = "<group>"; };
3EEEE9F809E44CCDA96CDFF7 /* APPLocalNotificationOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = APPLocalNotificationOptions.h; path = "de.appplant.cordova.plugin.local-notification/APPLocalNotificationOptions.h"; sourceTree = "<group>"; };
3FAE800EBDCA42B2A38B569B /* UIApplication+APPLocalNotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIApplication+APPLocalNotification.m"; path = "de.appplant.cordova.plugin.local-notification/UIApplication+APPLocalNotification.m"; sourceTree = "<group>"; };
49A917BFEDB54EBE90626CB7 /* APPLocalNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = APPLocalNotification.h; path = "de.appplant.cordova.plugin.local-notification/APPLocalNotification.h"; sourceTree = "<group>"; };
49F49C19231D4BBE83DBF480 /* UIApplication+APPLocalNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIApplication+APPLocalNotification.h"; path = "de.appplant.cordova.plugin.local-notification/UIApplication+APPLocalNotification.h"; sourceTree = "<group>"; };
5B1594DC16A7569C00FEF299 /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = System/Library/Frameworks/AssetsLibrary.framework; sourceTree = SDKROOT; };
7C2E1A851E1C499084223237 /* UILocalNotification+APPLocalNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UILocalNotification+APPLocalNotification.h"; path = "de.appplant.cordova.plugin.local-notification/UILocalNotification+APPLocalNotification.h"; sourceTree = "<group>"; };
7E7966D41810823500FA85AD /* icon-40.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-40.png"; sourceTree = "<group>"; };
7E7966D51810823500FA85AD /* icon-40@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-40@2x.png"; sourceTree = "<group>"; };
7E7966D61810823500FA85AD /* icon-50.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-50.png"; sourceTree = "<group>"; };
......@@ -116,12 +110,7 @@
7E7966DD1810823500FA85AD /* icon-small@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-small@2x.png"; sourceTree = "<group>"; };
8D1107310486CEB800E47090 /* NotificationExample-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "NotificationExample-Info.plist"; path = "../NotificationExample-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = "<group>"; };
9E1A68371A55FA1B00FBCA66 /* beep.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = beep.caf; sourceTree = "<group>"; };
9E5A62271A52DE07002E41A3 /* AppDelegate+APPLocalNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "AppDelegate+APPLocalNotification.h"; path = "de.appplant.cordova.plugin.local-notification/AppDelegate+APPLocalNotification.h"; sourceTree = "<group>"; };
9E5A62281A52DE07002E41A3 /* AppDelegate+APPLocalNotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "AppDelegate+APPLocalNotification.m"; path = "de.appplant.cordova.plugin.local-notification/AppDelegate+APPLocalNotification.m"; sourceTree = "<group>"; };
B15E0B313C114B988FFDB3DF /* APPLocalNotificationOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = APPLocalNotificationOptions.m; path = "de.appplant.cordova.plugin.local-notification/APPLocalNotificationOptions.m"; sourceTree = "<group>"; };
B1D78B105CE344DD96990060 /* APPLocalNotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = APPLocalNotification.m; path = "de.appplant.cordova.plugin.local-notification/APPLocalNotification.m"; sourceTree = "<group>"; };
D4A0D8751607E02300AEF8BB /* Default-568h@2x~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x~iphone.png"; sourceTree = "<group>"; };
DAB48B85CF1041E08D93312D /* UILocalNotification+APPLocalNotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UILocalNotification+APPLocalNotification.m"; path = "de.appplant.cordova.plugin.local-notification/UILocalNotification+APPLocalNotification.m"; sourceTree = "<group>"; };
DDDFCE9D76CA4692A1F4A984 /* APPBackgroundMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = APPBackgroundMode.m; path = "de.appplant.cordova.plugin.background-mode/APPBackgroundMode.m"; sourceTree = "<group>"; };
E02030E711134A5D8215D2F0 /* appbeep.wav */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = unknown; path = appbeep.wav; sourceTree = "<group>"; };
EB87FDF21871DA7A0020F90C /* merges */ = {isa = PBXFileReference; lastKnownFileType = folder; name = merges; path = ../../merges; sourceTree = "<group>"; };
......@@ -129,6 +118,16 @@
EB87FDF41871DAF40020F90C /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = config.xml; path = ../../config.xml; sourceTree = "<group>"; };
F042F1C3B49D4E2699FF5E1D /* CDVDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVDevice.h; path = org.apache.cordova.device/CDVDevice.h; sourceTree = "<group>"; };
F840E1F0165FE0F500CFE078 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = config.xml; path = NotificationExample/config.xml; sourceTree = "<group>"; };
9C396B7AA36E4F79A25451A3 /* APPLocalNotification.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "APPLocalNotification.m"; path = "de.appplant.cordova.plugin.local-notification/APPLocalNotification.m"; sourceTree = "<group>"; fileEncoding = 4; };
51E3BEE46964498795BC65EC /* APPLocalNotificationOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "APPLocalNotificationOptions.m"; path = "de.appplant.cordova.plugin.local-notification/APPLocalNotificationOptions.m"; sourceTree = "<group>"; fileEncoding = 4; };
5ABA551B6BA54E6885F278AA /* UIApplication+APPLocalNotification.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "UIApplication+APPLocalNotification.h"; path = "de.appplant.cordova.plugin.local-notification/UIApplication+APPLocalNotification.h"; sourceTree = "<group>"; fileEncoding = 4; };
4AED74474D334999971FB4EC /* UIApplication+APPLocalNotification.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "UIApplication+APPLocalNotification.m"; path = "de.appplant.cordova.plugin.local-notification/UIApplication+APPLocalNotification.m"; sourceTree = "<group>"; fileEncoding = 4; };
276D2A95483249CBA0DAE4F5 /* UILocalNotification+APPLocalNotification.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "UILocalNotification+APPLocalNotification.m"; path = "de.appplant.cordova.plugin.local-notification/UILocalNotification+APPLocalNotification.m"; sourceTree = "<group>"; fileEncoding = 4; };
B03D3EA8EA7D4F8A8A9A1E6A /* APPLocalNotification.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "APPLocalNotification.h"; path = "de.appplant.cordova.plugin.local-notification/APPLocalNotification.h"; sourceTree = "<group>"; fileEncoding = 4; };
2957C36784934F95AA71AD0C /* APPLocalNotificationOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "APPLocalNotificationOptions.h"; path = "de.appplant.cordova.plugin.local-notification/APPLocalNotificationOptions.h"; sourceTree = "<group>"; fileEncoding = 4; };
0CEF91E2548247489CAFC744 /* AppDelegate+APPLocalNotification.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "AppDelegate+APPLocalNotification.h"; path = "de.appplant.cordova.plugin.local-notification/AppDelegate+APPLocalNotification.h"; sourceTree = "<group>"; fileEncoding = 4; };
6B223D4E64C9462A9BA448AD /* AppDelegate+APPLocalNotification.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "AppDelegate+APPLocalNotification.m"; path = "de.appplant.cordova.plugin.local-notification/AppDelegate+APPLocalNotification.m"; sourceTree = "<group>"; fileEncoding = 4; };
3BC46FA50763408493A28CD0 /* UILocalNotification+APPLocalNotification.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "UILocalNotification+APPLocalNotification.h"; path = "de.appplant.cordova.plugin.local-notification/UILocalNotification+APPLocalNotification.h"; sourceTree = "<group>"; fileEncoding = 4; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
......@@ -229,20 +228,20 @@
307C750510C5A3420062BCA9 /* Plugins */ = {
isa = PBXGroup;
children = (
9E5A62271A52DE07002E41A3 /* AppDelegate+APPLocalNotification.h */,
9E5A62281A52DE07002E41A3 /* AppDelegate+APPLocalNotification.m */,
212EBD1D7BCB4409838CD818 /* CDVDevice.m */,
F042F1C3B49D4E2699FF5E1D /* CDVDevice.h */,
DDDFCE9D76CA4692A1F4A984 /* APPBackgroundMode.m */,
3C888438876C480D8B36E11A /* APPBackgroundMode.h */,
B1D78B105CE344DD96990060 /* APPLocalNotification.m */,
B15E0B313C114B988FFDB3DF /* APPLocalNotificationOptions.m */,
49F49C19231D4BBE83DBF480 /* UIApplication+APPLocalNotification.h */,
3FAE800EBDCA42B2A38B569B /* UIApplication+APPLocalNotification.m */,
49A917BFEDB54EBE90626CB7 /* APPLocalNotification.h */,
3EEEE9F809E44CCDA96CDFF7 /* APPLocalNotificationOptions.h */,
7C2E1A851E1C499084223237 /* UILocalNotification+APPLocalNotification.h */,
DAB48B85CF1041E08D93312D /* UILocalNotification+APPLocalNotification.m */,
9C396B7AA36E4F79A25451A3 /* APPLocalNotification.m */,
51E3BEE46964498795BC65EC /* APPLocalNotificationOptions.m */,
5ABA551B6BA54E6885F278AA /* UIApplication+APPLocalNotification.h */,
4AED74474D334999971FB4EC /* UIApplication+APPLocalNotification.m */,
276D2A95483249CBA0DAE4F5 /* UILocalNotification+APPLocalNotification.m */,
B03D3EA8EA7D4F8A8A9A1E6A /* APPLocalNotification.h */,
2957C36784934F95AA71AD0C /* APPLocalNotificationOptions.h */,
0CEF91E2548247489CAFC744 /* AppDelegate+APPLocalNotification.h */,
6B223D4E64C9462A9BA448AD /* AppDelegate+APPLocalNotification.m */,
3BC46FA50763408493A28CD0 /* UILocalNotification+APPLocalNotification.h */,
);
name = Plugins;
path = NotificationExample/Plugins;
......@@ -432,11 +431,12 @@
9E5A62291A52DE07002E41A3 /* AppDelegate+APPLocalNotification.m in Sources */,
0B33C5FC1B224A91B96FDE79 /* CDVDevice.m in Sources */,
3DBE1CCC022B412AA9E356AF /* APPBackgroundMode.m in Sources */,
B8633FAA70DB4A8BADBA0143 /* APPLocalNotification.m in Sources */,
B0372583B02241029FBE505C /* APPLocalNotificationOptions.m in Sources */,
A5E31957B0D14EDA9EA08E51 /* UIApplication+APPLocalNotification.h in Sources */,
1E733C86398D48E8A1BC03FC /* UIApplication+APPLocalNotification.m in Sources */,
715548D3C8114B559B6D4496 /* UILocalNotification+APPLocalNotification.m in Sources */,
26914B69E52E40E893E4C399 /* APPLocalNotification.m in Sources */,
979DDA8725D74CA3A4E545F8 /* APPLocalNotificationOptions.m in Sources */,
0FFC7382D3B74FADB62619F0 /* UIApplication+APPLocalNotification.h in Resources */,
B806D27D183342679EFA5FDA /* UIApplication+APPLocalNotification.m in Sources */,
C6FCBB7BC1B947B699E811A8 /* UILocalNotification+APPLocalNotification.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......
......@@ -2,22 +2,4 @@
<Bucket
type = "1"
version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "NotificationExample/Plugins/de.appplant.cordova.plugin.local-notification/UILocalNotification+APPLocalNotification.m"
timestampString = "441842238.113728"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "61"
endingLineNumber = "61"
landmarkName = "-__init"
landmarkType = "5">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>
......@@ -62,47 +62,50 @@
}
/**
* Schedule a new local notification.
* Schedule a set of notifications.
*
* @param properties
* A dict of properties
* A dict of properties for each notification
*/
- (void) add:(CDVInvokedUrlCommand*)command
{
[self.commandDelegate runInBackground:^{
NSDictionary* options = [[command arguments]
objectAtIndex:0];
for (NSDictionary* options in command.arguments) {
UILocalNotification* notification;
UILocalNotification* notification;
notification = [[UILocalNotification alloc]
initWithOptions:options];
notification = [[UILocalNotification alloc]
initWithOptions:options];
[self scheduleLocalNotification:notification];
[self fireEvent:@"add" localNotification:notification];
}
[self scheduleLocalNotification:notification];
[self fireEvent:@"add" localNotification:notification];
[self execCallback:command];
}];
}
/**
* Cancels a given local notification.
* Cancel a set of notifications.
*
* @param id
* The ID of the local notification
* @param ids
* The IDs of the notifications
*/
- (void) cancel:(CDVInvokedUrlCommand*)command
{
[self.commandDelegate runInBackground:^{
NSString* id = [[command arguments]
objectAtIndex:0];
for (NSString* id in command.arguments) {
UILocalNotification* notification;
UILocalNotification* notification;
notification = [[UIApplication sharedApplication]
scheduledLocalNotificationWithId:id];
notification = [[UIApplication sharedApplication]
scheduledLocalNotificationWithId:id];
if (!notification)
continue;
[self cancelLocalNotification:notification];
[self fireEvent:@"cancel" localNotification:notification];
}
[self cancelLocalNotification:notification];
[self fireEvent:@"cancel" localNotification:notification];
[self execCallback:command];
}];
}
......@@ -242,9 +245,9 @@
- (void) registerPermission:(CDVInvokedUrlCommand*)command
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
_command = command;
[self.commandDelegate runInBackground:^{
[[UIApplication sharedApplication]
registerPermissionToScheduleLocalNotifications];
......@@ -282,10 +285,10 @@
{
if (!notification)
return;
[[UIApplication sharedApplication]
cancelLocalNotification:notification];
[UIApplication sharedApplication]
.applicationIconBadgeNumber -= 1;
}
......@@ -328,13 +331,9 @@
[self cancelLocalNotification:forerunner];
}
/**
* Cancels all local notification with are older then
* a specific amount of seconds
*
* @param {float} seconds
* The time interval in seconds
*/
- (void) cancelAllNotificationsWhichAreOlderThen:(float)seconds
{
......@@ -419,9 +418,7 @@
#pragma mark Life Cycle
/**
* Registers obervers for the following events after plugin was initialized.
* didReceiveLocalNotification:
* didFinishLaunchingWithOptions:
* Registers obervers after plugin was initialized.
*/
- (void) pluginInitialize
{
......@@ -439,7 +436,7 @@
selector:@selector(didFinishLaunchingWithOptions:)
name:UIApplicationDidFinishLaunchingNotification
object:nil];
[center addObserver:self
selector:@selector(didRegisterUserNotificationSettings:)
name:UIApplicationRegisterUserNotificationSettings
......
......@@ -102,11 +102,11 @@
- (NSInteger) badgeNumber
{
NSInteger number = [[dict objectForKey:@"badge"] intValue];
if (number == -1) {
number = 1 + [UIApplication sharedApplication].applicationIconBadgeNumber;
}
return number;
}
......
......@@ -37,7 +37,7 @@ NSString* const UIApplicationRegisterUserNotificationSettings = @"UIApplicationR
{
NSNotificationCenter* center = [NSNotificationCenter
defaultCenter];
// re-post (broadcast)
[center postNotificationName:UIApplicationRegisterUserNotificationSettings
object:settings];
......
......@@ -103,16 +103,16 @@ static char optionsKey;
switch (self.repeatInterval) {
case NSCalendarUnitMinute:
return 60;
case NSCalendarUnitHour:
return 60000;
case NSCalendarUnitDay:
case NSCalendarUnitWeekOfYear:
case NSCalendarUnitMonth:
case NSCalendarUnitYear:
return 86400;
default:
return 1;
}
......@@ -125,13 +125,13 @@ static char optionsKey;
{
NSDate* now = [NSDate date];
NSDate* fireDate = self.options.fireDate;
int timespan = [now timeIntervalSinceDate:fireDate];
if (self.repeatInterval != NSCalendarUnitEra) {
timespan = timespan % [self repeatIntervalInSeconds];
}
return timespan;
}
......
......@@ -92,9 +92,11 @@
<a href="#" class="button" onclick="hasPermission()">Has permission?<br/><span class="hint">notification.local.hasPermission()</span></a>
<a href="#" class="button" onclick="registerPermission()">Register permission<br/><span class="hint">notification.local.registerPermission()</span></a>
<a href="#" class="button" onclick="schedule()">Schedule now<br/><span class="hint">notification.local.add()</span></a>
<a href="#" class="button" onclick="scheduleDelayed()">Schedule in 5 sec<br/><span class="hint">notification.local.add()</span></a>
<a href="#" class="button" onclick="scheduleMultiple()">Schedule multiple<br/><span class="hint">notification.local.add()</span></a>
<a href="#" class="button" onclick="scheduleDelayed()">Schedule in 5 sec<br/><span class="hint">notification.local.add([])</span></a>
<a href="#" class="button" onclick="scheduleMinutely()">Schedule every min<br/><span class="hint">notification.local.add()</span></a>
<a href="#" class="button" onclick="cancel()">Cancel<br/><span class="hint">notification.local.cancel()</span></a>
<a href="#" class="button" onclick="cancelMultiple()">Cancel multiple<br/><span class="hint">notification.local.cancel([])</span></a>
<a href="#" class="button" onclick="cancelAll()">Cancel all<br/><span class="hint">notification.local.cancelAll()</span></a>
<a href="#" class="button" onclick="getScheduledIds()">Scheduled IDs<br/><span class="hint">notification.local.getScheduledIds()</span></a>
<a href="#" class="button" onclick="isScheduled()">Is scheduled?<br/><span class="hint">notification.local.isScheduled()</span></a>
......@@ -107,7 +109,7 @@
<script type="text/javascript" src="js/index.js"></script>
<script type="text/javascript">
var counter = 0, id = 12;
var counter = 1, id = 12;
var callback = function () {
alert('finished or canceled');
......@@ -129,10 +131,26 @@
plugin.notification.local.add({
id: id,
message: 'Test Message ' + (++counter),
json: { test: 123 }
json: { test: id }
});
};
scheduleMultiple = function () {
plugin.notification.local.add([{
id: id,
message: 'Test Message ' + (++counter),
json: { test: id }
},{
id: id+1,
message: 'Test Message ' + (++counter),
json: { test: id+1 }
},{
id: id+2,
message: 'Test Message ' + (++counter),
json: { test: id+2 }
}]);
};
scheduleDelayed = function () {
var now = new Date().getTime(),
_5_sec_from_now = new Date(now + 5*1000);
......@@ -160,6 +178,11 @@
plugin.notification.local.cancel(id,callback);
};
cancelMultiple = function () {
counter = 0;
plugin.notification.local.cancel([id, id+1],callback);
};
cancelAll = function () {
counter = 0;
plugin.notification.local.cancelAll(callback);
......@@ -198,7 +221,7 @@
<!-- callbacks -->
<script type="text/javascript">
document.addEventListener('deviceready', function () {
document.addEventListener('sdeviceready', function () {
plugin.notification.local.onadd = function (id, state, json) {
alert('on add\n' + Array.apply(null, arguments).join("\n"));
};
......
......@@ -104,85 +104,32 @@ exports.setDefaults = function (newDefaults) {
* A function to be called after the notification has been canceled
* @param {Object} scope
* The scope for the callback function
*
* @return {Number}
* The notification's ID
*/
exports.add = function (props, callback, scope) {
var options = this.mergeWithDefaults(props),
fn = this.createCallbackFn(callback, scope);
this.convertOptions(options);
if (['WinCE', 'Win32NT'].indexOf(device.platform) > -1) {
fn = function (cmd) {
eval(cmd);
};
}
this.registerPermission(function(granted) {
if (granted) {
exec(fn, null, 'LocalNotification', 'add', [options]);
}
});
return options.id;
};
if (!granted)
return;
/**
* Add new entries to the registry (more than one)
*
* @param {Object} options
* The notification properties
* @param {Function} callback
* A function to be called after the notification has been added
* @param {Object} scope
* The scope for the callback function
*
* @return {Number}
* The notification's ID
*/
exports.addMultiple = function (notifications, callback, scope) {
var length = notifications.length;
var notificationsMerged = new Array(),
callbackFn = this.createCallbackFn(callback, scope);
for (var i=0;i<length;i++){
var options = this.mergeWithDefaults(notifications[i]);
if (options.id) {
options.id = options.id.toString();
}
var notifications = Array.isArray(props) ? props : [props];
if (options.date === undefined) {
options.date = new Date();
}
for (var i = 0; i < notifications.length; i++) {
var properties = notifications[i];
if (options.title) {
options.title = options.title.toString();
}
if (options.message) {
options.message = options.message.toString();
}
if (typeof options.date == 'object') {
options.date = Math.round(options.date.getTime()/1000);
this.mergeWithDefaults(properties);
this.convertProperties(properties);
}
if (['WinCE', 'Win32NT'].indexOf(device.platform) > -1) {
callbackFn = function (cmd) {
eval(cmd);
};
if (device.platform != 'iOS') {
notifications = notifications[0];
}
notificationsMerged.push(options);
}
cordova.exec(callbackFn, null, 'LocalNotification', 'addMultiple', notificationsMerged);
return options.id;
this.exec('add', notifications, callback, scope);
}, this);
};
/**
* Update existing notification (currently android only)
* Update existing notification specified by ID in options.
*
* @param {Object} options
* The notification properties to update
......@@ -190,13 +137,9 @@ exports.addMultiple = function (notifications, callback, scope) {
* A function to be called after the notification has been updated
* @param {Object} scope
* The scope for the callback function
*
* @return {Number}
* The notification's ID
*/
exports.update = function (updates, callback, scope) {
callbackFn = this.createCallbackFn(callback, scope);
cordova.exec(callbackFn, null, 'LocalNotification', 'update', [updates]);
exports.update = function (options, callback, scope) {
this.exec('update', options, callback, scope);
};
/**
......@@ -210,31 +153,9 @@ exports.update = function (updates, callback, scope) {
* The scope for the callback function
*/
exports.clear = function (id, callback, scope) {
var id = id.toString(),
callbackFn = this.createCallbackFn(callback, scope);
cordova.exec(callbackFn, null, 'LocalNotification', 'clear', [id]);
};
var notId = (id || '0').toString();
/**
* Clear the specified notifications (more than one).
*
* @param {String} id
* The ID of the notification
* @param {Function} callback
* A function to be called after the notifications has been cleared.
* @param {Object} scope
* The scope for the callback function
*/
exports.clearMultiple = function (ids, callback, scope) {
var length = ids.length;
var idArray = new Array(),
callbackFn = this.createCallbackFn(callback, scope);
for (var i=0;i<length;i++){
var id = ids[i].toString();
idArray.push(id);
}
var callbackFn = this.createCallbackFn(callback, scope);
cordova.exec(callbackFn, null, 'LocalNotification', 'clearMultiple', [ids]);
this.exec('clear', notId, callback, scope);
};
/**
......@@ -246,47 +167,32 @@ exports.clearMultiple = function (ids, callback, scope) {
* The scope for the callback function
*/
exports.clearAll = function (callback, scope) {
var callbackFn = this.createCallbackFn(callback, scope);
cordova.exec(callbackFn, null, 'LocalNotification', 'clearAll', []);
this.exec('clearAll', null, callback, scope);
};
/**
* Cancels the specified notification.
* Cancels the specified notifications.
*
* @param {String} id
* The ID of the notification
* @param {String[]} ids
* The IDs of the notifications
* @param {Function} callback
* A function to be called after the notification has been canceled
* A function to be called after the notifications has been canceled
* @param {Object} scope
* The scope for the callback function
*/
exports.cancel = function (id, callback, scope) {
var fn = this.createCallbackFn(callback, scope);
exports.cancel = function (ids, callback, scope) {
exec(fn, null, 'LocalNotification', 'cancel', [(id || '0').toString()]);
};
ids = Array.isArray(ids) ? ids : [ids];
/**
* Cancel the specified notifications (more than one).
*
* @param {String} id
* The ID of the notification
* @param {Function} callback
* A function to be called after the notifications has been canceled
* @param {Object} scope
* The scope for the callback function
*/
exports.cancelMultiple = function (ids, callback, scope) {
var length = ids.length;
var idArray = new Array(),
callbackFn = this.createCallbackFn(callback, scope);
for (var i=0;i<length;i++){
var id = ids[i].toString();
idArray.push(id);
for (var i = 0; i < ids.length; i++) {
ids[i] = ids[i].toString();
}
var callbackFn = this.createCallbackFn(callback, scope);
cordova.exec(callbackFn, null, 'LocalNotification', 'cancelMultiple', [ids]);
if (device.platform != 'iOS') {
ids = ids[0];
}
this.exec('cancel', ids, callback, scope);
};
/**
......@@ -298,9 +204,7 @@ exports.cancelMultiple = function (ids, callback, scope) {
* The scope for the callback function
*/
exports.cancelAll = function (callback, scope) {
var fn = this.createCallbackFn(callback, scope);
exec(fn, null, 'LocalNotification', 'cancelAll', []);
this.exec('cancelAll', null, callback, scope);
};
/**
......@@ -312,9 +216,7 @@ exports.cancelAll = function (callback, scope) {
* The scope for the callback function
*/
exports.getScheduledIds = function (callback, scope) {
var fn = this.createCallbackFn(callback, scope);
exec(fn, null, 'LocalNotification', 'getScheduledIds', []);
this.exec('getScheduledIds', null, callback, scope);
};
/**
......@@ -328,9 +230,9 @@ exports.getScheduledIds = function (callback, scope) {
* The scope for the callback function
*/
exports.isScheduled = function (id, callback, scope) {
var fn = this.createCallbackFn(callback, scope);
var notId = (id || '0').toString();
exec(fn, null, 'LocalNotification', 'isScheduled', [id.toString()]);
this.exec('isScheduled', notId, callback, scope);
};
/**
......@@ -342,9 +244,7 @@ exports.isScheduled = function (id, callback, scope) {
* The scope for the callback function
*/
exports.getTriggeredIds = function (callback, scope) {
var fn = this.createCallbackFn(callback, scope);
exec(fn, null, 'LocalNotification', 'getTriggeredIds', []);
this.exec('getTriggeredIds', null, callback, scope);
};
/**
......@@ -358,9 +258,9 @@ exports.getTriggeredIds = function (callback, scope) {
* The scope for the callback function
*/
exports.isTriggered = function (id, callback, scope) {
var fn = this.createCallbackFn(callback, scope);
var notId = (id || '0').toString();
exec(fn, null, 'LocalNotification', 'isTriggered', [id.toString()]);
this.exec('isTriggered', notId, callback, scope);
};
/**
......@@ -401,12 +301,119 @@ exports.registerPermission = function (callback, scope) {
exec(fn, null, 'LocalNotification', 'registerPermission', []);
};
/**
* @deprecated
*
* Register permission to show notifications if not already granted.
*
* @param {Function} callback
* The function to be exec as the callback
* @param {Object?} scope
* The callback function's scope
*/
exports.promptForPermission = function (callback, scope) {
console.warn('Depreated: Please use `notification.local.registerPermission` instead.');
exports.registerPermission.apply(this, arguments);
};
/**
* Add new entries to the registry (more than one)
*
* @param {Object} options
* The notification properties
* @param {Function} callback
* A function to be called after the notification has been added
* @param {Object} scope
* The scope for the callback function
*
* @return {Number}
* The notification's ID
*/
exports.addMultiple = function (notifications, callback, scope) {
var length = notifications.length;
var notificationsMerged = new Array(),
callbackFn = this.createCallbackFn(callback, scope);
for (var i=0;i<length;i++){
var options = this.mergeWithDefaults(notifications[i]);
if (options.id) {
options.id = options.id.toString();
}
if (options.date === undefined) {
options.date = new Date();
}
if (options.title) {
options.title = options.title.toString();
}
if (options.message) {
options.message = options.message.toString();
}
if (typeof options.date == 'object') {
options.date = Math.round(options.date.getTime()/1000);
}
if (['WinCE', 'Win32NT'].indexOf(device.platform) > -1) {
callbackFn = function (cmd) {
eval(cmd);
};
}
notificationsMerged.push(options);
}
cordova.exec(callbackFn, null, 'LocalNotification', 'addMultiple', notificationsMerged);
return options.id;
};
/**
* Clear the specified notifications (more than one).
*
* @param {String} id
* The ID of the notification
* @param {Function} callback
* A function to be called after the notifications has been cleared.
* @param {Object} scope
* The scope for the callback function
*/
exports.clearMultiple = function (ids, callback, scope) {
var length = ids.length;
var idArray = new Array(),
callbackFn = this.createCallbackFn(callback, scope);
for (var i=0;i<length;i++){
var id = ids[i].toString();
idArray.push(id);
}
var callbackFn = this.createCallbackFn(callback, scope);
cordova.exec(callbackFn, null, 'LocalNotification', 'clearMultiple', [ids]);
};
/**
* Cancel the specified notifications (more than one).
*
* @param {String} id
* The ID of the notification
* @param {Function} callback
* A function to be called after the notifications has been canceled
* @param {Object} scope
* The scope for the callback function
*/
exports.cancelMultiple = function (ids, callback, scope) {
var length = ids.length;
var idArray = new Array(),
callbackFn = this.createCallbackFn(callback, scope);
for (var i=0;i<length;i++){
var id = ids[i].toString();
idArray.push(id);
}
var callbackFn = this.createCallbackFn(callback, scope);
cordova.exec(callbackFn, null, 'LocalNotification', 'cancelMultiple', [ids]);
};
/**
* Occurs when a notification was added.
*
* @param {String} id
......@@ -415,8 +422,10 @@ exports.promptForPermission = function (callback, scope) {
* Either "foreground" or "background"
* @param {String} json
* A custom (JSON) string
* @param {Object} data
* The notification properties
*/
exports.onadd = function (id, state, json) {};
exports.onadd = function (id, state, json, data) {};
/**
* Occurs when the notification is triggered.
......@@ -427,8 +436,10 @@ exports.onadd = function (id, state, json) {};
* Either "foreground" or "background"
* @param {String} json
* A custom (JSON) string
* @param {Object} data
* The notification properties
*/
exports.ontrigger = function (id, state, json) {};
exports.ontrigger = function (id, state, json, data) {};
/**
* Fires after the notification was clicked.
......@@ -439,8 +450,10 @@ exports.ontrigger = function (id, state, json) {};
* Either "foreground" or "background"
* @param {String} json
* A custom (JSON) string
* @param {Object} data
* The notification properties
*/
exports.onclick = function (id, state, json) {};
exports.onclick = function (id, state, json, data) {};
/**
* Fires if the notification was canceled.
......@@ -451,8 +464,10 @@ exports.onclick = function (id, state, json) {};
* Either "foreground" or "background"
* @param {String} json
* A custom (JSON) string
* @param {Object} data
* The notification properties
*/
exports.oncancel = function (id, state, json) {};
exports.oncancel = function (id, state, json, data) {};
/**
* Get fired when the notification was cleared.
......@@ -463,8 +478,10 @@ exports.oncancel = function (id, state, json) {};
* Either "foreground" or "background"
* @param {String} json
* A custom (JSON) string
* @param {Object} data
* The notification properties
*/
exports.onclear = function (id, state, json) {};
exports.onclear = function (id, state, json, data) {};
/**
......@@ -501,7 +518,7 @@ exports.mergeWithDefaults = function (options) {
* @retrun {Object}
* The converted property list
*/
exports.convertOptions = function (options) {
exports.convertProperties = function (options) {
if (options.id) {
options.id = options.id.toString();
}
......@@ -584,4 +601,31 @@ exports.createCallbackFn = function (callbackFn, scope) {
};
};
/**
* @private
*
* Executes the native counterpart.
*
* @param {String} action
* The name of the action
* @param args[]
* Array of arguments
* @param {Function} callback
* The callback function
* @param {Object} scope
* The scope for the function
*/
exports.exec = function (action, args, callback, scope) {
var fn = this.createCallbackFn(callback, scope),
params = [];
if (Array.isArray(args)) {
params = args;
} else if (args) {
params.push(args);
}
exec(fn, null, 'LocalNotification', action, params);
};
});
......@@ -39,11 +39,15 @@
"count": 1
},
{
"xml": "<receiver android:name=\"de.appplant.cordova.plugin.localnotification.DeleteIntentReceiver\" />",
"count": 1
},
{
"xml": "<receiver android:name=\"de.appplant.cordova.plugin.localnotification.Restore\"><intent-filter><action android:name=\"android.intent.action.BOOT_COMPLETED\" /></intent-filter></receiver>",
"count": 1
},
{
"xml": "<activity android:launchMode=\"singleInstance\" android:name=\"de.appplant.cordova.plugin.localnotification.ReceiverActivity\" />",
"xml": "<activity android:launchMode=\"singleInstance\" android:name=\"de.appplant.cordova.plugin.localnotification.ReceiverActivity\" android:theme=\"@android:style/Theme.NoDisplay\" />",
"count": 1
}
],
......
......@@ -9,6 +9,14 @@
- [enhancement:] Scope parameter for `isScheduled` and `getScheduledIds`
- [enhancement:] Callbacks for `add`, `cancel` & `cancelAll`
- [enhancement:] `image:` accepts remote URLs and local URIs (Android)
- [enhancement:] Schedule multiple notifications at once
- [enhancement:] Cancel multiple notifications at once
- [enhancement:] Clear multiple notifications at once (Android)
- [enhancement:] `clear` & `clearAll` methods (Android)
- [enhancement:] `onclear` event (Android)
- [enhancement:] Modal dialogs when in foreground (Android)
- [enhancement:] Ability to change repeating notifications (Android)
- [enhancement:] `sound:` accepts local URIs for absolute (file:///), relative (file://) and resource path (res:). (Android)
#### Version 0.7.4 (22.03.2014)
- [bugfix:] Platform specific properties were ignored.
......
......@@ -15,18 +15,18 @@ Local notifications are ideally suited for applications with time-based behavior
For example, applications that depend on servers for messages or data can poll their servers for incoming items while running in the background; if a message is ready to view or an update is ready to download, they can then present a local notification immediately to inform their users.
### Plugin's Purpose
The purpose of the plugin is to create an platform independent javascript interface for [Cordova][cordova] based mobile applications to access the specific API on each platform.
The purpose of the plugin is to create a platform-independent javascript interface for [Cordova][cordova]-based mobile applications to access the specific API on each platform.
## Supported Platforms
- **iOS** _(up to iOS8)_<br>
See [Local and Push Notification Programming Guide][ios_notification_guide] for detailed informations and screenshots.
See [Local and Push Notification Programming Guide][ios_notification_guide] for detailed information and screenshots.
- **Android** *(SDK >=7)*<br>
See [Notification Guide][android_notification_guide] for detailed informations and screenshots.
See [Notification Guide][android_notification_guide] for detailed information and screenshots.
- **WP8**<br>
See [Local notifications for Windows Phone][wp8_notification_guide] for detailed informations and screenshots.
See [Local notifications for Windows Phone][wp8_notification_guide] for detailed information and screenshots.
<br>*Windows Phone 8.0 has no notification center. Instead local notifications are realized through live tiles updates.*
......@@ -65,7 +65,7 @@ or to use an specific version:
```xml
<gap:plugin name="de.appplant.cordova.plugin.local-notification" version="0.7.2" />
```
More informations can be found [here][PGB_plugin].
More information can be found [here][PGB_plugin].
## ChangeLog
......@@ -80,7 +80,7 @@ More informations can be found [here][PGB_plugin].
- [enhancement:] Callbacks for `add`, `cancel` & `cancelAll`
- [enhancement:] `image:` accepts remote URLs and local URIs (Android)
#### Further informations
#### Further information
- See [CHANGELOG.md][changelog] to get the full changelog for the plugin.
- See the [v0.8.x TODO List][todo_list] for upcomming changes and other things.
......@@ -113,11 +113,11 @@ document.addEventListener('deviceready', function () {
```
### Determine if the app does have the permission to show local notifications
If the permission has been granted through the user can be retrieved through the `notification.local.hasPermission` interface.<br/>
The method takes a callback function as its argument which will be called with a boolean value. Optional the scope of the callback function ca be defined through a second argument.
If the permission has been granted through the user it can be retrieved through the `notification.local.hasPermission` interface.<br/>
The method takes a callback function as its argument which will be called with a boolean value. Optional: the scope of the callback function can be defined through a second argument.
#### Further informations
- The method is supported on each platform, however its only relevant for iOS8 and above.
#### Further information
- The method is supported on each platform, however it's only relevant for iOS8 and above.
```javascript
window.plugin.notification.local.hasPermission(function (granted) {
......@@ -126,36 +126,39 @@ window.plugin.notification.local.hasPermission(function (granted) {
```
### Register permission for local notifications
Required permissions can be registered through the `notification.local.registerPermission` interface.
Required permissions can be registered through the `notification.local.registerPermission` interface.<br/>
The method takes a callback function as its argument which will be called with a boolean value. Optional: the scope of the callback function can be defined through a second argument.
#### Further informations
#### Further information
- The method is supported on each platform, however its only relevant for iOS8 and above.
- The user will only get a prompt dialog for the first time. Later its only possible to change the setting via the notification center.
- The user will only get a prompt dialog for the first time. Later it's only possible to change the setting via the notification center.
```javascript
window.plugin.notification.local.registerPermission();
window.plugin.notification.local.registerPermission(function (granted) {
// console.log('Permission has been granted: ' + granted);
});
```
### Schedule local notifications
Local notifications can be scheduled through the `notification.local.add` interface.<br>
The method takes a hash as an argument to specify the notification's properties and returns the ID for the notification.<br>
Scheduling a local notification will override the previously one with the same ID.
Scheduling a local notification will override an earlier one with the same ID.
All properties are optional. If no date object is given, the notification pops-up immediately.
**Note:** On Android the notification id needs to be a string which can be converted to a number.
If the ID has an invalid format, it will be ignored, but canceling the notification will fail.
If the ID has an invalid format, it will be ignored, but cancelling the notification will fail.
#### Further informations
- See the [onadd][onadd] event of how a listener can be registered to be notified when a local notification has been scheduled.
- See the [ontrigger][ontrigger] event of how a listener can be registered to be notified when a local notification has been triggered.
- See the [onclick][onclick] event of how a listener can be registered to be notified when the user has been clicked on a local notification.
- See the [platform specific properties][platform_specific_properties] of which other properties are available too.
- See [getDefaults][getdefaults] of which property values are used by default and [setDefaults][setdefaults] of how to override them.
- See the [examples][examples] of how to schedule local notifications.
#### Further information
- See the [onadd][onadd] event for registering a listener to be notified when a local notification has been scheduled.
- See the [ontrigger][ontrigger] event for registering a listener to be notified when a local notification has been triggered.
- See the [onclick][onclick] event for registering a listener to be notified when the user has been clicked on a local notification.
- See the [platform specific properties][platform_specific_properties] too list which other properties are available too.
- See [getDefaults][getdefaults] to examine which property values are used by default and [setDefaults][setdefaults] how to override them.
- See [examples][examples] for scheduling local notifications.
```javascript
window.plugin.notification.local.add({
id: String, // A unique id of the notifiction
id: String, // A unique id of the notification
date: Date, // This expects a date object
message: String, // The message that is displayed
title: String, // The title of the message
......@@ -163,44 +166,44 @@ window.plugin.notification.local.add({
badge: Number, // Displays number badge to notification
sound: String, // A sound to be played
json: String, // Data to be passed through the notification
autoCancel: Boolean, // Setting this flag and the notification is automatically canceled when the user clicks it
autoCancel: Boolean, // Setting this flag and the notification is automatically cancelled when the user clicks it
ongoing: Boolean, // Prevent clearing of notification (Android only)
}, callback, scope);
```
### Cancel scheduled local notifications
Local notifications can be canceled through the `notification.local.cancel` interface.<br>
Note that only local notifications with an ID can be canceled.
Local notifications can be cancelled through the `notification.local.cancel` interface.<br>
Note that only local notifications with an ID can be cancelled.
#### Further informations
- See the [oncancel][oncancel] event of how a listener can be registered to be notified when a local notification has been canceled.
- See [getScheduledIds][getscheduledids] of how to retrieve a list of IDs of all scheduled local notifications.
#### Further information
- See the [oncancel][oncancel] event for registering a listener to be notified when a local notification has been cancelled.
- See [getScheduledIds][getscheduledids] to retrieve a list of IDs for all scheduled local notifications.
```javascript
window.plugin.notification.local.cancel(ID, function () {
// The notification has been canceled
// The notification has been cancelled
}, scope);
```
### Cancel all scheduled local notifications
All local notifications can be canceled through the `notification.local.cancelAll` interface.<br>
All local notifications can be cancelled through the `notification.local.cancelAll` interface.<br>
The method cancels all local notifications even if they have no ID.
#### Further informations
- See the [oncancel][oncancel] event of how a listener can be registered to be notified when a local notification has been canceled.
#### Further information
- See the [oncancel][oncancel] event for registering a listener to be notified when a local notification has been cancelled.
```javascript
window.plugin.notification.local.cancelAll(function () {
// All notifications have been canceled
// All notifications have been cancelled
}, scope);
```
### Check wether a notification with an ID is scheduled
### Check whether a notification with an ID is scheduled
To check if a notification with an ID is scheduled, the `notification.local.isScheduled` interface can be used.<br>
The method takes the ID of the local notification as an argument and a callback function to be called with the result. Optional the scope of the callback can be assigned too.
The method takes the ID of the local notification as an argument and a callback function to be called with the result. Optional: the scope of the callback can be assigned too.
#### Further informations
- See [getScheduledIds][getscheduledids] of how to retrieve a list of IDs of all scheduled local notifications.
#### Further information
- See [getScheduledIds][getscheduledids] to retrieve a list of IDs for all scheduled local notifications.
```javascript
window.plugin.notification.local.isScheduled(id, function (isScheduled) {
......@@ -210,7 +213,7 @@ window.plugin.notification.local.isScheduled(id, function (isScheduled) {
### Retrieve the IDs from all currently scheduled local notifications
To retrieve the IDs from all currently scheduled local notifications, the `notification.local.getScheduledIds` interface can be used.<br>
The method takes a callback function to be called with the result as an array of IDs. Optional the scope of the callback can be assigned too.
The method takes a callback function to be called with the result as an array of IDs. Optional: the scope of the callback can be assigned too.
```javascript
window.plugin.notification.local.getScheduledIds(function (scheduledIds) {
......@@ -218,12 +221,12 @@ window.plugin.notification.local.getScheduledIds(function (scheduledIds) {
}, scope);
```
### Check wether a notification with an ID was triggered
### Check whether a notification with an ID was triggered
To check if a notification with an ID was triggered, the `notification.local.isTriggered` interface can be used.<br>
The method takes the ID of the local notification as an argument and a callback function to be called with the result. Optional the scope of the callback can be assigned too.
The method takes the ID of the local notification as an argument and a callback function to be called with the result. Optional: the scope of the callback can be assigned too.
#### Further informations
- See [getTriggeredIds][gettriggeredIds] of how to retrieve a list of IDs of all scheduled local notifications.
#### Further information
- See [getTriggeredIds][gettriggeredIds] to retrieve a list of IDs for all scheduled local notifications.
```javascript
window.plugin.notification.local.isTriggered(id, function (isTriggered) {
......@@ -233,7 +236,7 @@ window.plugin.notification.local.isTriggered(id, function (isTriggered) {
### Retrieve the IDs from all currently triggered local notifications
To retrieve the IDs from all currently triggered local notifications, the `notification.local.getTriggeredIds` interface can be used.<br>
The method takes a callback function to be called with the result as an array of IDs. Optional the scope of the callback can be assigned too.
The method takes a callback function to be called with the result as an array of IDs. Optional: the scope of the callback can be assigned too.
```javascript
window.plugin.notification.local.getTriggeredIds(function (triggeredIds) {
......@@ -245,8 +248,8 @@ window.plugin.notification.local.getTriggeredIds(function (triggeredIds) {
The default values of the local notification properties can be retrieved through the `notification.local.getDefaults` interface.<br>
The method returns an object of values for all available local notification properties on the platform.
#### Further informations
- See [setDefaults][setdefaults] of how to override the default values.
#### Further information
- See [setDefaults][setdefaults] to override the default values.
```javascript
window.plugin.notification.local.getDefaults(); // => Object
......@@ -256,9 +259,9 @@ window.plugin.notification.local.getDefaults(); // => Object
The default values of the local notification properties can be set through the `notification.local.setDefaults` interface.<br>
The method takes an object as argument.
#### Further informations
#### Further information
- See the [add][add] interface and the [platform specific properties][platform_specific_properties] to get an overview about all available local notification properties.
- See the [example][setdefaults_example] of how to override default values.
- See the [example][setdefaults_example] to override default values.
```javascript
window.plugin.notification.local.setDefaults(Object);
......@@ -267,15 +270,15 @@ window.plugin.notification.local.setDefaults(Object);
### Get notified when a local notification has been scheduled
The `notification.local.onadd` interface can be used to get notified when a local notification has been scheduled.
The listener has to be a function and takes the following arguments:
The listener must be a function and takes the following arguments:
- id: The ID of the notification
- state: Either *background* or *foreground*
- json: A custom (JSON encoded) string
**Note:** The event is only being invoked in background if the app is not suspended!
#### Further informations
- See the [ontrigger][ontrigger] event of how a listener can be registered to be notified when a local notification has been triggered.
#### Further information
- See the [ontrigger][ontrigger] event for registering a listener to be notified when a local notification has been triggered.
```javascript
window.plugin.notification.local.onadd = function (id, state, json) {};
......@@ -284,15 +287,15 @@ window.plugin.notification.local.onadd = function (id, state, json) {};
### Get notified when a local notification has been triggered
The `notification.local.ontrigger` interface can be used to get notified when a local notification has been triggered.
The listener has to be a function and takes the following arguments:
The listener must be a function and takes the following arguments:
- id: The ID of the notification
- state: Either *background* or *foreground*
- json: A custom (JSON encoded) string
**Note:** The event is only being invoked in background if the app is running and is not suspended!
**Note:** The event is only invoked in background if the app is running and is not suspended!
#### Further informations
- See the [onclick][onclick] event of how a listener can be registered to be notified when the user has been clicked on a local notification.
#### Further information
- See the [onclick][onclick] event for registering a listener to be notified when the user has been clicked on a local notification.
```javascript
window.plugin.notification.local.ontrigger = function (id, state, json) {};
......@@ -301,33 +304,33 @@ window.plugin.notification.local.ontrigger = function (id, state, json) {};
### Get notified when the user has been clicked on a local notification
The `notification.local.onclick` interface can be used to get notified when the user has been clicked on a local notification.
The listener has to be a function and takes the following arguments:
The listener must be a function and takes the following arguments:
- id: The ID of the notification
- state: Either *background* or *foreground*
- json: A custom (JSON encoded) string
**Note:** The event is only being invoked in background if the app is not suspended!
**Note:** The event is only invoked in background if the app is not suspended!
#### Further informations
#### Further information
- The *autoCancel* property can be used to either automatically cancel the local notification or not after it has been clicked by the user.
```javascript
window.plugin.notification.local.onclick = function (id, state, json) {};
```
### Get notified when a local notification has been canceled
The `notification.local.oncancel` interface can be used to get notified when a local notification has been canceled.
### Get notified when a local notification has been cancelled
The `notification.local.oncancel` interface can be used to get notified when a local notification has been cancelled.
The listener has to be a function and takes the following arguments:
The listener must be a function and takes the following arguments:
- id: The ID of the notification
- state: Either *background* or *foreground*
- json: A custom (JSON encoded) string
**Note:** The event is not being invoked if the local notification has been cleared in the notification center.
**Note:** The event is not invoked if the local notification has been cleared in the notification center.
#### Further informations
- The *autoCancel* property can be used to either automatically cancel the local notification or not after it has been clicked by the user.
- See [cancel][cancel] and [cancelAll][cancelall] of how to cancel local notifications manually.
#### Further information
- The *autoCancel* property can automatically cancel the local notification if has been clicked by the user.
- See [cancel][cancel] and [cancelAll][cancelall] to cancel local notifications manually.
```javascript
window.plugin.notification.local.oncancel = function (id, state, json) {};
......@@ -351,22 +354,22 @@ window.plugin.notification.local.add({
});
```
### Scheduling an immediately triggered local notification
The example below shows how to schedule a local notification which will be triggered immediatly.
### Scheduling an immediately-triggered local notification
The example below shows how to schedule a local notification which will be triggered immediately.
```javascript
window.plugin.notification.local.add({ message: 'Great app!' });
```
### Schedule a silent local notification
By default the system sound for local notifications will be used. To turn off any sound the *sound* property has to be set to *NULL*.
By default the system sound for local notifications will be used. To turn off any sound, set the *sound* property to *NULL*.
```javascript
window.plugin.notification.local.add({ sound: null });
```
### Assign user data to the notification
If needed local notifications can be scheduled with any user data. That data can be accessed on each event listener. But cannot be modified later.
If needed, local notifications can be scheduled with any user data. That data can be accessed on each event listener, but cannot be modified later.
```javascript
window.plugin.notification.local.add({
......@@ -391,16 +394,16 @@ window.plugin.notification.local.setDefaults({ autoCancel: true });
## Platform specifics
### Small and large icons on Android
By default all notifications will display the app icon. But an specific icon can be defined through the `icon` and `smallIcon` properties.
By default all notifications will display the app icon. A specific icon can be defined through the `icon` and `smallIcon` properties.
#### Resource icons
The following example shows how to display the `<package.name>.R.drawable.ic_launcher`icon as the notifications icon.
The following example shows how to display the `<package.name>.R.drawable.ic_launcher`icon as the notification icon.
```javascript
window.plugin.notification.local.add({ icon: 'ic_launcher' });
```
See below how to use the `android.R.drawable.ic_dialog_email` icon as the notifications small icon.
See below to use the `android.R.drawable.ic_dialog_email` icon as the notification small icon.
```javascript
window.plugin.notification.local.add({ smallIcon: 'ic_dialog_email' });
......@@ -421,13 +424,13 @@ window.plugin.notification.local.add({ icon: 'https://cordova.apache.org/images/
```
### Notification sound on Android
The sound must be a absolute or relative Uri pointing to the sound file. The default sound is `RingtoneManager.TYPE_NOTIFICATION`.
The sound must be an absolute or relative URI pointing to the sound file. The default sound is `RingtoneManager.TYPE_NOTIFICATION`.
**Note:** Local sound files must be placed into the res-folder and not into the assets-folder.
```javascript
/**
* Plays the `beep.mp3` which has to be located in the res folder
* Plays the `beep.mp3` which must be located in the res folder
*/
window.plugin.notification.local.add({ sound: 'android.resource://' + package_name + '/raw/beep' });
......@@ -437,7 +440,7 @@ window.plugin.notification.local.add({ sound: 'android.resource://' + package_na
window.plugin.notification.local.add({ sound: 'http://remotedomain/beep.mp3' });
/**
* Plays a sound file which has to be located in the android_assets folder
* Plays a sound file which must be located in the android_assets folder
*/
window.plugin.notification.local.add({ sound: '/www/audio/beep.mp3' });
......@@ -450,29 +453,29 @@ window.plugin.notification.local.add({ sound: 'TYPE_ALARM' });
### Notification sound on iOS
You can package the audio data in an *aiff*, *wav*, or *caf* file. Then, in Xcode, add the sound file to your project as a nonlocalized resource of the application bundle. You may use the *afconvert* tool to convert sounds.
**Note:** The right to play notification sounds in the notification center settings has to be granted.<br>
**Note:** To play notification sounds, permission needs to be granted in the notification center settings.<br>
**Note:** Custom sounds must be under 30 seconds when played. If a custom sound is over that limit, the default system sound is played instead.
```javascript
/**
* Plays the `beep.mp3` which has to be located in the root folder of the project
* Plays the `beep.mp3` which must be located in the root folder of the project
*/
window.plugin.notification.local.add({ sound: 'beep.caf' });
/**
* Plays the `beep.mp3` which has to be located in the www folder
* Plays the `beep.mp3` which must located in the www folder
*/
window.plugin.notification.local.add({ sound: 'www/sounds/beep.caf' });
```
### LiveTile background images on WP8
LiveTile's have the ability to display images for different sizes. These images can be defined through the `smallImage`, `image` and `wideImage` properties.
LiveTiles have the ability to display images for different sizes. These images can be defined through the `smallImage`, `image` and `wideImage` properties.
**Note:** An image must be defined as a relative or absolute URI. They can be restored to the default ones by canceling the notification.
**Note:** An image must be defined as a relative or absolute URI. They can be restored to default by cancelling the notification.
```javascript
/**
* Displays the application icon as the livetile's background image
* Displays the application icon as the LiveTile's background image
*/
window.plugin.notification.local.add({ image: 'appdata:ApplicationIcon.png' })
```
......@@ -488,7 +491,7 @@ window.plugin.notification.local.add({ repeat: 15 });
```
### Change the LED color on Android devices
The LED color can be specified through the `led` property. By default the color value is white (FFFFFF). Its possible to change that value by setting another hex code.
The LED color can be specified through the `led` property. By default the color value is white (FFFFFF). It is possible to change that value by setting another hex code.
```javascript
window.plugin.notification.local.add({ led: 'A0FF05' });
......@@ -502,16 +505,16 @@ Each application on a device is limited to 64 scheduled local notifications.<br>
The system discards scheduled notifications in excess of this limit, keeping only the 64 notifications that will fire the soonest. Recurring notifications are treated as a single notification.
### Events aren't fired on iOS
After deploying/replacing the app on the device via Xcode no callback for previously scheduled local notifications aren't fired.
After deploying/replacing the app on the device via Xcode, no callback for previously scheduled local notifications are fired.
### No sound is played on iOS 7
The right to play notification sounds in the notification center settings has to be granted.
Users must grant permission in the notification center settings for notification sounds to be played.
### Adding a notification on WP8
An application can only display one notification at a time. Each time a new notification has to be added, the application live tile's data will be overwritten by the new ones.
An application can only display one notification at a time. Each time a new notification is added, the application's LiveTile data will be overwritten by the new ones.
### TypeError: Cannot read property 'currentVersion' of null
Along with Cordova 3.2 and Windows Phone 8 the `version.bat` script has to be renamed to `version`.
Along with Cordova 3.2 and Windows Phone 8, the `version.bat` script must be renamed to `version`.
On Mac or Linux
```
......@@ -528,8 +531,6 @@ The launch mode for the main activity has to be set to `singleInstance`
<activity ... android:launchMode="singleInstance" ... />
```
### A notification cleared by the User is still shown as Triggered on Android
It's not possible on android, to get informed about a User clearing an applications notifications. Currently the only way to prevent that sort of behavior is, to set the "ongoing" parameter to "true" and cancel the notification during the onclick event.
## Contributing
......
......@@ -65,6 +65,12 @@
* sound and it vibrates the phone.
-->
<receiver android:name="de.appplant.cordova.plugin.localnotification.Receiver" />
<!--
* The delete intent receiver is triggered when the user clears a notification
* manually. It unpersists the cleared notification from the shared preferences.
-->
<receiver android:name="de.appplant.cordova.plugin.localnotification.DeleteIntentReceiver" />
<!--
* This class is triggered upon reboot of the device. It needs to re-register
......@@ -76,13 +82,14 @@
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!--
* The receiver activity is triggered when a notification is clicked by a user.
* The activity calls the background callback and brings the launch inten
* up to foreground.
-->
<activity android:name="de.appplant.cordova.plugin.localnotification.ReceiverActivity" android:launchMode="singleInstance" />
<activity android:name="de.appplant.cordova.plugin.localnotification.ReceiverActivity" android:launchMode="singleInstance" android:theme="@android:style/Theme.NoDisplay" />
</config-file>
<config-file target="AndroidManifest.xml" parent="/manifest">
......@@ -96,6 +103,7 @@
<source-file src="src/android/Options.java" target-dir="src/de/appplant/cordova/plugin/localnotification" />
<source-file src="src/android/Restore.java" target-dir="src/de/appplant/cordova/plugin/localnotification" />
<source-file src="src/android/ReceiverActivity.java" target-dir="src/de/appplant/cordova/plugin/localnotification" />
<source-file src="src/android/DeleteIntentReceiver.java" target-dir="src/de/appplant/cordova/plugin/localnotification" />
</platform>
<!-- wp8 -->
......
/*
Copyright 2013-2014 appPlant UG
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package de.appplant.cordova.plugin.localnotification;
import java.util.Date;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
public class DeleteIntentReceiver extends BroadcastReceiver {
public static final String OPTIONS = "LOCAL_NOTIFICATION_OPTIONS";
@Override
public void onReceive(Context context, Intent intent) {
Options options = null;
Bundle bundle = intent.getExtras();
JSONObject args;
try {
args = new JSONObject(bundle.getString(OPTIONS));
options = new Options(context).parse(args);
} catch (JSONException e) {
return;
}
// The context may got lost if the app was not running before
LocalNotification.setContext(context);
Date now = new Date();
if ((options.getInterval()!=0)){
LocalNotification.persist(options.getId(), setInitDate(args));
}
else if((new Date(options.getDate()).before(now))){
LocalNotification.unpersist(options.getId());
}
fireClearEvent(options);
}
private JSONObject setInitDate(JSONObject arguments){
long initialDate = arguments.optLong("date", 0) * 1000;
try {
arguments.put("initialDate", initialDate);
} catch (JSONException e) {
e.printStackTrace();
}
return arguments;
}
/**
* Fires onclear event.
*/
private void fireClearEvent (Options options) {
LocalNotification.fireEvent("clear", options.getId(), options.getJSON());
}
}
......@@ -35,6 +35,7 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
......@@ -43,6 +44,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Build;
import android.widget.Toast;
/**
* This plugin utilizes the Android AlarmManager in combination with StatusBar
......@@ -59,6 +61,7 @@ public class LocalNotification extends CordovaPlugin {
protected static Context context = null;
protected static Boolean isInBackground = true;
private static ArrayList<String> eventQueue = new ArrayList<String>();
static Activity activity;
@Override
public void initialize (CordovaInterface cordova, CordovaWebView webView) {
......@@ -66,6 +69,7 @@ public class LocalNotification extends CordovaPlugin {
LocalNotification.webView = super.webView;
LocalNotification.context = super.cordova.getActivity().getApplicationContext();
LocalNotification.activity = super.cordova.getActivity();
}
@Override
......@@ -73,14 +77,70 @@ public class LocalNotification extends CordovaPlugin {
if (action.equalsIgnoreCase("add")) {
cordova.getThreadPool().execute( new Runnable() {
public void run() {
JSONObject arguments = setInitDate(args).optJSONObject(0);
JSONObject arguments = setInitDate(args.optJSONObject(0));
Options options = new Options(context).parse(arguments);
add(options, true);
command.success();
}
});
}
if (action.equalsIgnoreCase("addMultiple")) {
cordova.getThreadPool().execute( new Runnable() {
public void run() {
JSONArray notifications = args.optJSONArray(0);
for (int i =0; i<notifications.length();i++){
JSONObject arguments = setInitDate(notifications.optJSONObject(i));
Options options = new Options(context).parse(arguments);
add(options, true);
}
command.success();
}
});
}
if (action.equalsIgnoreCase("update")) {
cordova.getThreadPool().execute( new Runnable() {
public void run() {
JSONObject updates = args.optJSONObject(0);
update(updates);
command.success();
}
});
}
if (action.equalsIgnoreCase("clear")) {
cordova.getThreadPool().execute( new Runnable() {
public void run() {
String id = args.optString(0);
clear(id);
command.success();
}
});
}
if (action.equalsIgnoreCase("clearMultiple")) {
cordova.getThreadPool().execute( new Runnable() {
public void run() {
JSONArray ids = args.optJSONArray(0);
for (int i =0; i<ids.length();i++){
clear(ids.optString(i));
}
command.success();
}
});
}
if (action.equalsIgnoreCase("clearAll")) {
cordova.getThreadPool().execute( new Runnable() {
public void run() {
clearAll();
command.success();
}
});
}
if (action.equalsIgnoreCase("cancel")) {
cordova.getThreadPool().execute( new Runnable() {
......@@ -93,6 +153,18 @@ public class LocalNotification extends CordovaPlugin {
}
});
}
if (action.equalsIgnoreCase("cancelMultiple")) {
cordova.getThreadPool().execute( new Runnable() {
public void run() {
JSONArray ids = args.optJSONArray(0);
for (int i =0; i<ids.length();i++){
cancel(ids.optString(i));
}
command.success();
}
});
}
if (action.equalsIgnoreCase("cancelAll")) {
cordova.getThreadPool().execute( new Runnable() {
......@@ -169,6 +241,7 @@ public class LocalNotification extends CordovaPlugin {
persist(options.getId(), options.getJSONObject());
//Intent is called when the Notification gets fired
Intent intent = new Intent(context, Receiver.class)
.setAction("" + options.getId())
.putExtra(Receiver.OPTIONS, options.getJSONObject().toString());
......@@ -183,6 +256,91 @@ public class LocalNotification extends CordovaPlugin {
am.set(AlarmManager.RTC_WAKEUP, triggerTime, pi);
}
/**
* Update an existing notification
*
* @param updates JSONObject with update-content
*/
public static void update (JSONObject updates){
String id = updates.optString("id", "0");
// update shared preferences
SharedPreferences settings = getSharedPreferences();
Map<String, ?> alarms = settings.getAll();
JSONObject arguments;
try {
arguments = new JSONObject(alarms.get(id).toString());
} catch (JSONException e) {
e.printStackTrace();
return;
}
arguments = updateArguments(arguments, updates);
// cancel existing alarm
Intent intent = new Intent(context, Receiver.class)
.setAction("" + id);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
AlarmManager am = getAlarmManager();
am.cancel(pi);
//add new alarm
Options options = new Options(context).parse(arguments);
add(options,false);
}
/**
* Clear a specific notification without canceling repeating alarms
*
* @param notificationID
* The original ID of the notification that was used when it was
* registered using add()
*/
public static void clear (String notificationId){
SharedPreferences settings = getSharedPreferences();
Map<String, ?> alarms = settings.getAll();
NotificationManager nc = getNotificationManager();
try {
nc.cancel(Integer.parseInt(notificationId));
} catch (Exception e) {}
JSONObject arguments;
try {
arguments = new JSONObject(alarms.get(notificationId).toString());
Options options = new Options(context).parse(arguments);
Date now = new Date();
if ((options.getInterval()!=0)){
persist(notificationId, setInitDate(arguments));
}
else if((new Date(options.getDate()).before(now))){
unpersist(notificationId);
}
} catch (JSONException e) {
e.printStackTrace();
return;
}
fireEvent("clear", notificationId, "");
}
/**
* Clear all notifications without canceling repeating alarms
*/
public static void clearAll (){
SharedPreferences settings = getSharedPreferences();
NotificationManager nc = getNotificationManager();
Map<String, ?> alarms = settings.getAll();
Set<String> alarmIds = alarms.keySet();
for (String alarmId : alarmIds) {
clear(alarmId);
}
nc.cancelAll();
}
/**
* Cancel a specific notification that was previously registered.
*
......@@ -490,15 +648,50 @@ public class LocalNotification extends CordovaPlugin {
* @param args The given JSONArray
* @return A new JSONArray with the parameter "initialDate" set.
*/
private static JSONArray setInitDate(JSONArray args){
long initialDate = args.optJSONObject(0).optLong("date", 0) * 1000;
private static JSONObject setInitDate(JSONObject arguments){
long initialDate = arguments.optLong("date", 0) * 1000;
try {
args.optJSONObject(0).put("initialDate", initialDate);
arguments.put("initialDate", initialDate);
} catch (JSONException e) {
e.printStackTrace();
}
return args;
return arguments;
}
private static JSONObject updateArguments(JSONObject arguments,JSONObject updates){
try {
if(!updates.isNull("message")){
arguments.put("message", updates.get("message"));
}
if(!updates.isNull("title")){
arguments.put("title", updates.get("title"));
}
if(!updates.isNull("badge")){
arguments.put("badge", updates.get("badge"));
}
if(!updates.isNull("sound")){
arguments.put("sound", updates.get("sound"));
}
if(!updates.isNull("icon")){
arguments.put("icon", updates.get("icon"));
}
} catch (JSONException jse){
jse.printStackTrace();
}
return arguments;
}
public static void showNotification(String title,String notification){
int duration = Toast.LENGTH_LONG;
if(title.equals("")){
title = "Notification";
}
String text = title + " \n " + notification;
Toast notificationToast = Toast.makeText(context, text, duration);
notificationToast.show();
}
}
\ No newline at end of file
}
......@@ -21,8 +21,11 @@
package de.appplant.cordova.plugin.localnotification;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Calendar;
......@@ -42,12 +45,13 @@ import android.media.RingtoneManager;
import android.net.Uri;
import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy;
import android.util.Log;
/**
* Class that helps to store the options that can be specified per alarm.
*/
public class Options {
static protected final String STORAGE_FOLDER = "/localnotification";
private JSONObject options = new JSONObject();
private String packageName = null;
private long interval = 0;
......@@ -101,7 +105,7 @@ public class Options {
return this;
}
/**
* Returns options as JSON object
*/
......@@ -160,7 +164,7 @@ public class Options {
return RingtoneManager.getDefaultUri(soundId);
} catch (Exception e) {
return Uri.parse(sound);
return getURIfromPath(sound);
}
}
......@@ -176,7 +180,7 @@ public class Options {
if (icon.startsWith("http")) {
bmp = getIconFromURL(icon);
} else if (icon.startsWith("file://")) {
} else if (icon.startsWith("file://") || (icon.startsWith("res"))) {
bmp = getIconFromURI(icon);
}
......@@ -352,13 +356,11 @@ public class Options {
* The corresponding bitmap
*/
private Bitmap getIconFromURI (String src) {
AssetManager assets = LocalNotification.context.getAssets();
Bitmap bmp = null;
Uri uri = getURIfromPath(src);
try {
String path = src.replace("file:/", "www");
InputStream input = assets.open(path);
try {
InputStream input = LocalNotification.activity.getContentResolver().openInputStream(uri);
bmp = BitmapFactory.decodeStream(input);
} catch (IOException e) {
e.printStackTrace();
......@@ -366,4 +368,166 @@ public class Options {
return bmp;
}
}
\ No newline at end of file
/**
* The URI for a path.
*
* @param path The given path
*
* @return The URI pointing to the given path
*/
private Uri getURIfromPath(String path){
if (path.startsWith("res:")) {
return getUriForResourcePath(path);
} else if (path.startsWith("file:///")) {
return getUriForAbsolutePath(path);
} else if (path.startsWith("file://")) {
return getUriForAssetPath(path);
}
return Uri.parse(path);
}
/**
* The URI for a file.
*
* @param path
* The given absolute path
*
* @return The URI pointing to the given path
*/
private Uri getUriForAbsolutePath(String path) {
String absPath = path.replaceFirst("file://", "");
File file = new File(absPath);
if (!file.exists()) {
Log.e("LocalNotifocation", "File not found: " + file.getAbsolutePath());
}
return Uri.fromFile(file);
}
/**
* The URI for an asset.
*
* @param path
* The given asset path
*
* @return The URI pointing to the given path
*/
private Uri getUriForAssetPath(String path) {
String resPath = path.replaceFirst("file:/", "www");
String fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
File dir = LocalNotification.activity.getExternalCacheDir();
if (dir == null) {
Log.e("LocalNotifocation", "Missing external cache dir");
return Uri.EMPTY;
}
String storage = dir.toString() + STORAGE_FOLDER;
File file = new File(storage, fileName);
new File(storage).mkdir();
try {
AssetManager assets = LocalNotification.activity.getAssets();
FileOutputStream outStream = new FileOutputStream(file);
InputStream inputStream = assets.open(resPath);
copyFile(inputStream, outStream);
outStream.flush();
outStream.close();
} catch (Exception e) {
Log.e("LocalNotifocation", "File not found: assets/" + resPath);
e.printStackTrace();
}
return Uri.fromFile(file);
}
/**
* The URI for a resource.
*
* @param path
* The given relative path
*
* @return The URI pointing to the given path
*/
private Uri getUriForResourcePath(String path) {
String resPath = path.replaceFirst("res://", "");
String fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
String resName = fileName.substring(0, fileName.lastIndexOf('.'));
String extension = resPath.substring(resPath.lastIndexOf('.'));
File dir = LocalNotification.activity.getExternalCacheDir();
if (dir == null) {
Log.e("LocalNotifocation", "Missing external cache dir");
return Uri.EMPTY;
}
String storage = dir.toString() + STORAGE_FOLDER;
int resId = getResId(resPath);
File file = new File(storage, resName + extension);
if (resId == 0) {
Log.e("LocalNotifocation", "File not found: " + resPath);
}
new File(storage).mkdir();
try {
Resources res = LocalNotification.activity.getResources();
FileOutputStream outStream = new FileOutputStream(file);
InputStream inputStream = res.openRawResource(resId);
copyFile(inputStream, outStream);
outStream.flush();
outStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return Uri.fromFile(file);
}
/**
* Writes an InputStream to an OutputStream
*
* @param in
* The input stream
* @param out
* The output stream
*/
private void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
/**
* @return The resource ID for the given resource.
*/
private int getResId(String resPath) {
Resources res = LocalNotification.activity.getResources();
int resId;
String pkgName = getPackageName();
String dirName = "drawable";
String fileName = resPath;
if (resPath.contains("/")) {
dirName = resPath.substring(0, resPath.lastIndexOf('/'));
fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
}
String resName = fileName.substring(0, fileName.lastIndexOf('.'));
resId = res.getIdentifier(resName, dirName, pkgName);
if (resId == 0) {
resId = res.getIdentifier(resName, "drawable", pkgName);
}
return resId;
}
/**
* The name for the package.
*
* @return The package name
*/
private String getPackageName() {
return LocalNotification.activity.getPackageName();
}
/**
* Shows the behavior of notifications when the application is in foreground
*
*
*/
public boolean getForegroundMode(){
return options.optBoolean("foregroundMode",false);
}
}
......@@ -79,10 +79,17 @@ public class Receiver extends BroadcastReceiver {
} else {
LocalNotification.add(options.moveDate(), false);
}
if (!LocalNotification.isInBackground && options.getForegroundMode()){
if (options.getInterval() == 0) {
LocalNotification.unpersist(options.getId());
}
LocalNotification.showNotification(options.getTitle(), options.getMessage());
fireTriggerEvent();
} else {
Builder notification = buildNotification();
Builder notification = buildNotification();
showNotification(notification);
showNotification(notification);
}
}
/*
......@@ -116,7 +123,13 @@ public class Receiver extends BroadcastReceiver {
@SuppressLint("NewApi")
private Builder buildNotification () {
Uri sound = options.getSound();
//DeleteIntent is called when the user clears a notification manually
Intent deleteIntent = new Intent(context, DeleteIntentReceiver.class)
.setAction("" + options.getId())
.putExtra(Receiver.OPTIONS, options.getJSONObject().toString());
PendingIntent dpi = PendingIntent.getBroadcast(context, 0, deleteIntent, PendingIntent.FLAG_CANCEL_CURRENT);
Builder notification = new NotificationCompat.Builder(context)
.setDefaults(0) // Do not inherit any defaults
.setContentTitle(options.getTitle())
......@@ -127,7 +140,8 @@ public class Receiver extends BroadcastReceiver {
.setLargeIcon(options.getIcon())
.setAutoCancel(options.getAutoCancel())
.setOngoing(options.getOngoing())
.setLights(options.getColor(), 500, 500);
.setLights(options.getColor(), 500, 500)
.setDeleteIntent(dpi);
if (sound != null) {
notification.setSound(sound);
......
......@@ -35,6 +35,8 @@
@property (readwrite, assign) BOOL deviceready;
// Event queue
@property (readonly, nonatomic, retain) NSMutableArray* eventQueue;
// Needed when calling `registerPermission`
@property (nonatomic, retain) CDVInvokedUrlCommand* command;
@end
......@@ -60,47 +62,50 @@
}
/**
* Schedule a new local notification.
* Schedule a set of notifications.
*
* @param properties
* A dict of properties
* A dict of properties for each notification
*/
- (void) add:(CDVInvokedUrlCommand*)command
{
[self.commandDelegate runInBackground:^{
NSDictionary* options = [[command arguments]
objectAtIndex:0];
for (NSDictionary* options in command.arguments) {
UILocalNotification* notification;
UILocalNotification* notification;
notification = [[UILocalNotification alloc]
initWithOptions:options];
notification = [[UILocalNotification alloc]
initWithOptions:options];
[self scheduleLocalNotification:notification];
[self fireEvent:@"add" localNotification:notification];
}
[self scheduleLocalNotification:notification];
[self fireEvent:@"add" localNotification:notification];
[self execCallback:command];
}];
}
/**
* Cancels a given local notification.
* Cancel a set of notifications.
*
* @param id
* The ID of the local notification
* @param ids
* The IDs of the notifications
*/
- (void) cancel:(CDVInvokedUrlCommand*)command
{
[self.commandDelegate runInBackground:^{
NSString* id = [[command arguments]
objectAtIndex:0];
for (NSString* id in command.arguments) {
UILocalNotification* notification;
UILocalNotification* notification;
notification = [[UIApplication sharedApplication]
scheduledLocalNotificationWithId:id];
notification = [[UIApplication sharedApplication]
scheduledLocalNotificationWithId:id];
if (!notification)
continue;
[self cancelLocalNotification:notification];
[self fireEvent:@"cancel" localNotification:notification];
}
[self cancelLocalNotification:notification];
[self fireEvent:@"cancel" localNotification:notification];
[self execCallback:command];
}];
}
......@@ -217,7 +222,7 @@
* Inform if the app has the permission to show
* badges and local notifications.
*/
- (void) hasPermission:(CDVInvokedUrlCommand *)command
- (void) hasPermission:(CDVInvokedUrlCommand*)command
{
[self.commandDelegate runInBackground:^{
CDVPluginResult* result;
......@@ -237,12 +242,19 @@
/**
* Ask for permission to show badges.
*/
- (void) registerPermission:(CDVInvokedUrlCommand *)command
- (void) registerPermission:(CDVInvokedUrlCommand*)command
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
_command = command;
[self.commandDelegate runInBackground:^{
[[UIApplication sharedApplication]
registerPermissionToScheduleLocalNotifications];
}];
#else
[self hasPermission:command];
#endif
}
#pragma mark -
......@@ -276,6 +288,9 @@
[[UIApplication sharedApplication]
cancelLocalNotification:notification];
[UIApplication sharedApplication]
.applicationIconBadgeNumber -= 1;
}
/**
......@@ -316,13 +331,9 @@
[self cancelLocalNotification:forerunner];
}
/**
* Cancels all local notification with are older then
* a specific amount of seconds
*
* @param {float} seconds
* The time interval in seconds
*/
- (void) cancelAllNotificationsWhichAreOlderThen:(float)seconds
{
......@@ -351,7 +362,6 @@
*/
- (void) didReceiveLocalNotification:(NSNotification*)localNotification
{
UIApplication* app = [UIApplication sharedApplication];
UILocalNotification* notification = [localNotification object];
BOOL autoCancel = notification.options.autoCancel;
......@@ -359,7 +369,10 @@
NSString* event = (timeInterval <= 1 && deviceready) ? @"trigger" : @"click";
app.applicationIconBadgeNumber -= 1;
if ([event isEqualToString:@"click"]) {
[UIApplication sharedApplication]
.applicationIconBadgeNumber -= 1;
}
[self fireEvent:event localNotification:notification];
......@@ -389,32 +402,45 @@
}
}
/**
* Called on otification settings registration is completed.
*/
- (void) didRegisterUserNotificationSettings:(UIUserNotificationSettings*)settings
{
if (_command)
{
[self hasPermission:_command];
_command = NULL;
}
}
#pragma mark -
#pragma mark Life Cycle
/**
* Registers obervers for the following events after plugin was initialized.
* didReceiveLocalNotification:
* didFinishLaunchingWithOptions:
* Registers obervers after plugin was initialized.
*/
- (void) pluginInitialize
{
NSNotificationCenter* notificationCenter;
notificationCenter = [NSNotificationCenter
defaultCenter];
NSNotificationCenter* center = [NSNotificationCenter
defaultCenter];
eventQueue = [[NSMutableArray alloc] init];
[notificationCenter addObserver:self
selector:@selector(didReceiveLocalNotification:)
name:CDVLocalNotification
object:nil];
[center addObserver:self
selector:@selector(didReceiveLocalNotification:)
name:CDVLocalNotification
object:nil];
[center addObserver:self
selector:@selector(didFinishLaunchingWithOptions:)
name:UIApplicationDidFinishLaunchingNotification
object:nil];
[notificationCenter addObserver:self
selector:@selector(didFinishLaunchingWithOptions:)
name:UIApplicationDidFinishLaunchingNotification
object:nil];
[center addObserver:self
selector:@selector(didRegisterUserNotificationSettings:)
name:UIApplicationRegisterUserNotificationSettings
object:nil];
}
/**
......
......@@ -82,7 +82,7 @@
- (BOOL) autoCancel
{
if (IsAtLeastiOSVersion(@"8.0")){
return YES;
return self.repeatInterval == NSCalendarUnitEra;
} else {
return [[dict objectForKey:@"autoCancel"] boolValue];
}
......@@ -101,7 +101,13 @@
*/
- (NSInteger) badgeNumber
{
return [[dict objectForKey:@"badge"] intValue];
NSInteger number = [[dict objectForKey:@"badge"] intValue];
if (number == -1) {
number = 1 + [UIApplication sharedApplication].applicationIconBadgeNumber;
}
return number;
}
#pragma mark -
......
......@@ -23,6 +23,8 @@
#import <Availability.h>
extern NSString* const UIApplicationRegisterUserNotificationSettings;
@interface AppDelegate (APPLocalNotification)
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
......
......@@ -23,13 +23,9 @@
#import <Availability.h>
@implementation AppDelegate (APPLocalNotification)
NSString* const UIApplicationRegisterUserNotificationSettings = @"UIApplicationRegisterUserNotificationSettings";
+ (void)load {
Methods original = class_getInstanceMethod(self, @selector(applicationDidFinishLaunching:));
Method custom = class_getInstanceMethod(self, @selector(customApplicationDidFinishLaunching:));
method_exchangeImplementations(original, custom);
}
@implementation AppDelegate (APPLocalNotification)
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
/**
......@@ -39,7 +35,12 @@
- (void) application:(UIApplication*)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings*)settings
{
[[NSURLCache sharedURLCache] removeAllCachedResponses];
NSNotificationCenter* center = [NSNotificationCenter
defaultCenter];
// re-post (broadcast)
[center postNotificationName:UIApplicationRegisterUserNotificationSettings
object:settings];
}
#endif
......
......@@ -28,7 +28,7 @@
// The options provided by the plug-in
- (APPLocalNotificationOptions*) options;
// Timeinterval since fire date
- (NSTimeInterval) timeIntervalSinceFireDate;
- (double) timeIntervalSinceFireDate;
// If the fire date was in the past
- (BOOL) wasInThePast;
// If the notification was already triggered
......
......@@ -96,14 +96,43 @@ static char optionsKey;
}
/**
* The repeating interval in seconds.
*/
- (int) repeatIntervalInSeconds
{
switch (self.repeatInterval) {
case NSCalendarUnitMinute:
return 60;
case NSCalendarUnitHour:
return 60000;
case NSCalendarUnitDay:
case NSCalendarUnitWeekOfYear:
case NSCalendarUnitMonth:
case NSCalendarUnitYear:
return 86400;
default:
return 1;
}
}
/**
* Timeinterval since fire date.
*/
- (NSTimeInterval) timeIntervalSinceFireDate
- (double) timeIntervalSinceFireDate
{
NSDate* now = [NSDate date];
NSDate* fireDate = self.options.fireDate;
return [now timeIntervalSinceDate:fireDate];
int timespan = [now timeIntervalSinceDate:fireDate];
if (self.repeatInterval != NSCalendarUnitEra) {
timespan = timespan % [self repeatIntervalInSeconds];
}
return timespan;
}
/**
......
......@@ -64,7 +64,7 @@ exports._defaults = {
message: '',
title: '',
autoCancel: false,
badge: 0,
badge: -1,
id: '0',
json: '',
repeat: ''
......@@ -104,63 +104,95 @@ exports.setDefaults = function (newDefaults) {
* A function to be called after the notification has been canceled
* @param {Object} scope
* The scope for the callback function
*
* @return {Number}
* The notification's ID
*/
exports.add = function (props, callback, scope) {
var options = this.mergeWithDefaults(props),
fn = this.createCallbackFn(callback, scope);
this.registerPermission(function(granted) {
if (options.id) {
options.id = options.id.toString();
}
if (!granted)
return;
if (options.date === undefined) {
options.date = new Date();
}
var notifications = Array.isArray(props) ? props : [props];
if (options.title) {
options.title = options.title.toString();
}
for (var i = 0; i < notifications.length; i++) {
var properties = notifications[i];
if (options.message) {
options.message = options.message.toString();
}
if (typeof options.date == 'object') {
options.date = Math.round(options.date.getTime()/1000);
}
if (typeof options.json == 'object') {
options.json = JSON.stringify(options.json);
}
this.mergeWithDefaults(properties);
this.convertProperties(properties);
}
if (['WinCE', 'Win32NT'].indexOf(device.platform) > -1) {
fn = function (cmd) {
eval(cmd);
};
}
if (device.platform != 'iOS') {
notifications = notifications[0];
}
exec(fn, null, 'LocalNotification', 'add', [options]);
this.exec('add', notifications, callback, scope);
}, this);
};
return options.id;
/**
* Update existing notification specified by ID in options.
*
* @param {Object} options
* The notification properties to update
* @param {Function} callback
* A function to be called after the notification has been updated
* @param {Object} scope
* The scope for the callback function
*/
exports.update = function (options, callback, scope) {
this.exec('update', options, callback, scope);
};
/**
* Cancels the specified notification.
* Clears the specified notification.
*
* @param {String} id
* The ID of the notification
* @param {Function} callback
* A function to be called after the notification has been canceled
* A function to be called after the notification has been cleared
* @param {Object} scope
* The scope for the callback function
*/
exports.cancel = function (id, callback, scope) {
var fn = this.createCallbackFn(callback, scope);
exports.clear = function (id, callback, scope) {
var notId = (id || '0').toString();
exec(fn, null, 'LocalNotification', 'cancel', [(id || '0').toString()]);
this.exec('clear', notId, callback, scope);
};
/**
* Clears all previously sheduled notifications.
*
* @param {Function} callback
* A function to be called after all notifications have been cleared
* @param {Object} scope
* The scope for the callback function
*/
exports.clearAll = function (callback, scope) {
this.exec('clearAll', null, callback, scope);
};
/**
* Cancels the specified notifications.
*
* @param {String[]} ids
* The IDs of the notifications
* @param {Function} callback
* A function to be called after the notifications has been canceled
* @param {Object} scope
* The scope for the callback function
*/
exports.cancel = function (ids, callback, scope) {
ids = Array.isArray(ids) ? ids : [ids];
for (var i = 0; i < ids.length; i++) {
ids[i] = ids[i].toString();
}
if (device.platform != 'iOS') {
ids = ids[0];
}
this.exec('cancel', ids, callback, scope);
};
/**
......@@ -172,9 +204,7 @@ exports.cancel = function (id, callback, scope) {
* The scope for the callback function
*/
exports.cancelAll = function (callback, scope) {
var fn = this.createCallbackFn(callback, scope);
exec(fn, null, 'LocalNotification', 'cancelAll', []);
this.exec('cancelAll', null, callback, scope);
};
/**
......@@ -186,9 +216,7 @@ exports.cancelAll = function (callback, scope) {
* The scope for the callback function
*/
exports.getScheduledIds = function (callback, scope) {
var fn = this.createCallbackFn(callback, scope);
exec(fn, null, 'LocalNotification', 'getScheduledIds', []);
this.exec('getScheduledIds', null, callback, scope);
};
/**
......@@ -202,9 +230,9 @@ exports.getScheduledIds = function (callback, scope) {
* The scope for the callback function
*/
exports.isScheduled = function (id, callback, scope) {
var fn = this.createCallbackFn(callback, scope);
var notId = (id || '0').toString();
exec(fn, null, 'LocalNotification', 'isScheduled', [id.toString()]);
this.exec('isScheduled', notId, callback, scope);
};
/**
......@@ -216,9 +244,7 @@ exports.isScheduled = function (id, callback, scope) {
* The scope for the callback function
*/
exports.getTriggeredIds = function (callback, scope) {
var fn = this.createCallbackFn(callback, scope);
exec(fn, null, 'LocalNotification', 'getTriggeredIds', []);
this.exec('getTriggeredIds', null, callback, scope);
};
/**
......@@ -232,9 +258,9 @@ exports.getTriggeredIds = function (callback, scope) {
* The scope for the callback function
*/
exports.isTriggered = function (id, callback, scope) {
var fn = this.createCallbackFn(callback, scope);
var notId = (id || '0').toString();
exec(fn, null, 'LocalNotification', 'isTriggered', [id.toString()]);
this.exec('isTriggered', notId, callback, scope);
};
/**
......@@ -267,18 +293,127 @@ exports.hasPermission = function (callback, scope) {
exports.registerPermission = function (callback, scope) {
var fn = this.createCallbackFn(callback, scope);
if (device.platform != 'iOS')
if (device.platform != 'iOS') {
fn(true);
return;
}
exec(fn, null, 'LocalNotification', 'registerPermission', []);
};
/**
* @deprecated
*
* Register permission to show notifications if not already granted.
*
* @param {Function} callback
* The function to be exec as the callback
* @param {Object?} scope
* The callback function's scope
*/
exports.promptForPermission = function (callback, scope) {
console.warn('Depreated: Please use `notification.local.registerPermission` instead.');
exports.registerPermission.apply(this, arguments);
};
/**
* Add new entries to the registry (more than one)
*
* @param {Object} options
* The notification properties
* @param {Function} callback
* A function to be called after the notification has been added
* @param {Object} scope
* The scope for the callback function
*
* @return {Number}
* The notification's ID
*/
exports.addMultiple = function (notifications, callback, scope) {
var length = notifications.length;
var notificationsMerged = new Array(),
callbackFn = this.createCallbackFn(callback, scope);
for (var i=0;i<length;i++){
var options = this.mergeWithDefaults(notifications[i]);
if (options.id) {
options.id = options.id.toString();
}
if (options.date === undefined) {
options.date = new Date();
}
if (options.title) {
options.title = options.title.toString();
}
if (options.message) {
options.message = options.message.toString();
}
if (typeof options.date == 'object') {
options.date = Math.round(options.date.getTime()/1000);
}
if (['WinCE', 'Win32NT'].indexOf(device.platform) > -1) {
callbackFn = function (cmd) {
eval(cmd);
};
}
notificationsMerged.push(options);
}
cordova.exec(callbackFn, null, 'LocalNotification', 'addMultiple', notificationsMerged);
return options.id;
};
/**
* Clear the specified notifications (more than one).
*
* @param {String} id
* The ID of the notification
* @param {Function} callback
* A function to be called after the notifications has been cleared.
* @param {Object} scope
* The scope for the callback function
*/
exports.clearMultiple = function (ids, callback, scope) {
var length = ids.length;
var idArray = new Array(),
callbackFn = this.createCallbackFn(callback, scope);
for (var i=0;i<length;i++){
var id = ids[i].toString();
idArray.push(id);
}
var callbackFn = this.createCallbackFn(callback, scope);
cordova.exec(callbackFn, null, 'LocalNotification', 'clearMultiple', [ids]);
};
/**
* Cancel the specified notifications (more than one).
*
* @param {String} id
* The ID of the notification
* @param {Function} callback
* A function to be called after the notifications has been canceled
* @param {Object} scope
* The scope for the callback function
*/
exports.cancelMultiple = function (ids, callback, scope) {
var length = ids.length;
var idArray = new Array(),
callbackFn = this.createCallbackFn(callback, scope);
for (var i=0;i<length;i++){
var id = ids[i].toString();
idArray.push(id);
}
var callbackFn = this.createCallbackFn(callback, scope);
cordova.exec(callbackFn, null, 'LocalNotification', 'cancelMultiple', [ids]);
};
/**
* Occurs when a notification was added.
*
* @param {String} id
......@@ -287,8 +422,10 @@ exports.promptForPermission = function (callback, scope) {
* Either "foreground" or "background"
* @param {String} json
* A custom (JSON) string
* @param {Object} data
* The notification properties
*/
exports.onadd = function (id, state, json) {};
exports.onadd = function (id, state, json, data) {};
/**
* Occurs when the notification is triggered.
......@@ -299,8 +436,10 @@ exports.onadd = function (id, state, json) {};
* Either "foreground" or "background"
* @param {String} json
* A custom (JSON) string
* @param {Object} data
* The notification properties
*/
exports.ontrigger = function (id, state, json) {};
exports.ontrigger = function (id, state, json, data) {};
/**
* Fires after the notification was clicked.
......@@ -311,8 +450,10 @@ exports.ontrigger = function (id, state, json) {};
* Either "foreground" or "background"
* @param {String} json
* A custom (JSON) string
* @param {Object} data
* The notification properties
*/
exports.onclick = function (id, state, json) {};
exports.onclick = function (id, state, json, data) {};
/**
* Fires if the notification was canceled.
......@@ -323,8 +464,24 @@ exports.onclick = function (id, state, json) {};
* Either "foreground" or "background"
* @param {String} json
* A custom (JSON) string
* @param {Object} data
* The notification properties
*/
exports.oncancel = function (id, state, json, data) {};
/**
* Get fired when the notification was cleared.
*
* @param {String} id
* The ID of the notification
* @param {String} state
* Either "foreground" or "background"
* @param {String} json
* A custom (JSON) string
* @param {Object} data
* The notification properties
*/
exports.oncancel = function (id, state, json) {};
exports.onclear = function (id, state, json, data) {};
/**
......@@ -353,6 +510,49 @@ exports.mergeWithDefaults = function (options) {
/**
* @private
*
* Convert the passed values to their required type.
*
* @param {Object} options
* Set of custom values
*
* @retrun {Object}
* The converted property list
*/
exports.convertProperties = function (options) {
if (options.id) {
options.id = options.id.toString();
}
if (options.date === undefined) {
options.date = new Date();
}
if (options.title) {
options.title = options.title.toString();
}
if (options.message) {
options.message = options.message.toString();
}
if (options.text) {
options.message = options.text.toString();
}
if (typeof options.date == 'object') {
options.date = Math.round(options.date.getTime()/1000);
}
if (typeof options.json == 'object') {
options.json = JSON.stringify(options.json);
}
return options;
};
/**
* @private
*
* Merges the platform specific properties into the default properties.
*
* @return {Object}
......@@ -400,3 +600,30 @@ exports.createCallbackFn = function (callbackFn, scope) {
callbackFn.apply(scope || this, arguments);
};
};
/**
* @private
*
* Executes the native counterpart.
*
* @param {String} action
* The name of the action
* @param args[]
* Array of arguments
* @param {Function} callback
* The callback function
* @param {Object} scope
* The scope for the function
*/
exports.exec = function (action, args, callback, scope) {
var fn = this.createCallbackFn(callback, scope),
params = [];
if (Array.isArray(args)) {
params = args;
} else if (args) {
params.push(args);
}
exec(fn, null, 'LocalNotification', action, params);
};
......@@ -92,9 +92,11 @@
<a href="#" class="button" onclick="hasPermission()">Has permission?<br/><span class="hint">notification.local.hasPermission()</span></a>
<a href="#" class="button" onclick="registerPermission()">Register permission<br/><span class="hint">notification.local.registerPermission()</span></a>
<a href="#" class="button" onclick="schedule()">Schedule now<br/><span class="hint">notification.local.add()</span></a>
<a href="#" class="button" onclick="scheduleDelayed()">Schedule in 5 sec<br/><span class="hint">notification.local.add()</span></a>
<a href="#" class="button" onclick="scheduleMultiple()">Schedule multiple<br/><span class="hint">notification.local.add()</span></a>
<a href="#" class="button" onclick="scheduleDelayed()">Schedule in 5 sec<br/><span class="hint">notification.local.add([])</span></a>
<a href="#" class="button" onclick="scheduleMinutely()">Schedule every min<br/><span class="hint">notification.local.add()</span></a>
<a href="#" class="button" onclick="cancel()">Cancel<br/><span class="hint">notification.local.cancel()</span></a>
<a href="#" class="button" onclick="cancelMultiple()">Cancel multiple<br/><span class="hint">notification.local.cancel([])</span></a>
<a href="#" class="button" onclick="cancelAll()">Cancel all<br/><span class="hint">notification.local.cancelAll()</span></a>
<a href="#" class="button" onclick="getScheduledIds()">Scheduled IDs<br/><span class="hint">notification.local.getScheduledIds()</span></a>
<a href="#" class="button" onclick="isScheduled()">Is scheduled?<br/><span class="hint">notification.local.isScheduled()</span></a>
......@@ -107,7 +109,7 @@
<script type="text/javascript" src="js/index.js"></script>
<script type="text/javascript">
var counter = 0, id = 12;
var counter = 1, id = 12;
var callback = function () {
alert('finished or canceled');
......@@ -129,10 +131,26 @@
plugin.notification.local.add({
id: id,
message: 'Test Message ' + (++counter),
json: { test: 123 }
json: { test: id }
});
};
scheduleMultiple = function () {
plugin.notification.local.add([{
id: id,
message: 'Test Message ' + (++counter),
json: { test: id }
},{
id: id+1,
message: 'Test Message ' + (++counter),
json: { test: id+1 }
},{
id: id+2,
message: 'Test Message ' + (++counter),
json: { test: id+2 }
}]);
};
scheduleDelayed = function () {
var now = new Date().getTime(),
_5_sec_from_now = new Date(now + 5*1000);
......@@ -160,6 +178,11 @@
plugin.notification.local.cancel(id,callback);
};
cancelMultiple = function () {
counter = 0;
plugin.notification.local.cancel([id, id+1],callback);
};
cancelAll = function () {
counter = 0;
plugin.notification.local.cancelAll(callback);
......@@ -198,7 +221,7 @@
<!-- callbacks -->
<script type="text/javascript">
document.addEventListener('deviceready', function () {
document.addEventListener('sdeviceready', function () {
plugin.notification.local.onadd = function (id, state, json) {
alert('on add\n' + Array.apply(null, arguments).join("\n"));
};
......
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