Commit 6540f331 by Sebastián Katzer

Add sample for actions

parent 008a0ec0
......@@ -35,6 +35,9 @@
// Request permission to show notifications
- (void) request:(CDVInvokedUrlCommand*)command;
// Register/update an action group
- (void) registerCategory:(CDVInvokedUrlCommand*)command;
// Schedule notifications
- (void) schedule:(CDVInvokedUrlCommand*)command;
//// Update set of notifications
......
......@@ -100,7 +100,7 @@
// NSNumber* id = [options objectForKey:@"id"];
// UNNotificationRequest* notification;
//
// notification = [self.center getNotificationWithId:id];
// notification = [_center getNotificationWithId:id];
//
// if (!notification)
// continue;
......@@ -119,7 +119,7 @@
// NSNumber* id = [options objectForKey:@"id"];
// UILocalNotification* notification;
//
// notification = [self.app localNotificationWithId:id];
// notification = [_app localNotificationWithId:id];
//
// if (!notification)
// continue;
......@@ -152,12 +152,12 @@
for (NSNumber* id in command.arguments) {
UNNotificationRequest* notification;
notification = [self.center getNotificationWithId:id];
notification = [_center getNotificationWithId:id];
if (!notification)
continue;
[self.center clearNotification:notification];
[_center clearNotification:notification];
[self fireEvent:@"clear" notification:notification];
}
......@@ -173,8 +173,8 @@
- (void) clearAll:(CDVInvokedUrlCommand*)command
{
[self.commandDelegate runInBackground:^{
[self.center clearAllNotifications];
[self.app setApplicationIconBadgeNumber:0];
[_center clearAllNotifications];
[_app setApplicationIconBadgeNumber:0];
[self fireEvent:@"clearall"];
[self execCallback:command];
}];
......@@ -193,12 +193,12 @@
for (NSNumber* id in command.arguments) {
UNNotificationRequest* notification;
notification = [self.center getNotificationWithId:id];
notification = [_center getNotificationWithId:id];
if (!notification)
continue;
[self.center cancelNotification:notification];
[_center cancelNotification:notification];
[self fireEvent:@"cancel" notification:notification];
}
......@@ -214,8 +214,8 @@
- (void) cancelAll:(CDVInvokedUrlCommand*)command
{
[self.commandDelegate runInBackground:^{
[self.center cancelAllNotifications];
[self.app setApplicationIconBadgeNumber:0];
[_center cancelAllNotifications];
[_app setApplicationIconBadgeNumber:0];
[self fireEvent:@"cancelall"];
[self execCallback:command];
}];
......@@ -321,7 +321,7 @@
byType:(APPNotificationType)type;
{
[self.commandDelegate runInBackground:^{
NSArray* ids = [self.center getNotificationIdsByType:type];
NSArray* ids = [_center getNotificationIdsByType:type];
CDVPluginResult* result;
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
......@@ -497,11 +497,30 @@
UNAuthorizationOptions options =
(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert);
[self.center requestAuthorizationWithOptions:options completionHandler:^(BOOL granted, NSError* e) {
[_center requestAuthorizationWithOptions:options completionHandler:^(BOOL granted, NSError* e) {
[self check:command];
}];
}
/**
* Register/update an action group.
*
* @return [ Void ]
*/
- (void) registerCategory:(CDVInvokedUrlCommand *)command
{
[self.commandDelegate runInBackground:^{
NSDictionary* options = command.arguments[0];
APPNotificationContent* notification;
notification = [[APPNotificationContent alloc]
initWithOptions:options];
[_center addNotificationCategory:notification.category];
[self execCallback:command];
}];
}
#pragma mark -
#pragma mark Private
......@@ -513,6 +532,8 @@
__weak APPLocalNotification* weakSelf = self;
UNNotificationRequest* request = notification.request;
[_center addNotificationCategory:notification.category];
[_center addNotificationRequest:request withCompletionHandler:^(NSError* e) {
__strong APPLocalNotification* strongSelf = weakSelf;
[strongSelf fireEvent:@"add" notification:request];
......
......@@ -30,5 +30,6 @@
- (id) initWithOptions:(NSDictionary*)dict;
- (APPNotificationOptions*) options;
- (UNNotificationRequest*) request;
- (UNNotificationCategory*) category;
@end
......@@ -23,7 +23,6 @@
#import "APPNotificationContent.h"
#import "APPNotificationOptions.h"
#import "UNUserNotificationCenter+APPLocalNotification.h"
#import <objc/runtime.h>
@import UserNotifications;
......@@ -67,7 +66,7 @@ static char optionsKey;
self.sound = options.sound;
self.badge = options.badge;
self.attachments = options.attachments;
self.categoryIdentifier = kAPPGeneralCategory;
self.categoryIdentifier = options.categoryId;
}
#pragma mark -
......@@ -107,6 +106,25 @@ static char optionsKey;
trigger:opts.trigger];
}
/**
* The category for the notification with all the actions.
*
* @return [ UNNotificationCategory* ]
*/
- (UNNotificationCategory*) category
{
NSString* categoryId = self.categoryIdentifier;
NSArray* actions = self.options.actions;
if (!actions.count)
return NULL;
return [UNNotificationCategory categoryWithIdentifier:categoryId
actions:actions
intentIdentifiers:@[]
options:UNNotificationCategoryOptionCustomDismissAction];
}
#pragma mark -
#pragma mark Private
......
......@@ -27,13 +27,15 @@
@property (readonly, getter=id) NSNumber* id;
@property (readonly, getter=identifier) NSString* identifier;
@property (readonly, getter=categoryId) NSString* categoryId;
@property (readonly, getter=title) NSString* title;
@property (readonly, getter=subtitle) NSString* subtitle;
@property (readonly, getter=badge) NSNumber* badge;
@property (readonly, getter=text) NSString* text;
@property (readonly, getter=sound) UNNotificationSound* sound;
@property (readonly, getter=attachments) NSArray<UNNotificationAttachment *> * attachments;
@property (readonly, getter=userInfo) NSDictionary* userInfo;
@property (readonly, getter=actions) NSArray<UNNotificationAction *> * actions;
@property (readonly, getter=attachments) NSArray<UNNotificationAttachment *> * attachments;
- (id) initWithDict:(NSDictionary*)dict;
- (UNNotificationTrigger*) trigger;
......
......@@ -22,6 +22,7 @@
*/
#import "APPNotificationOptions.h"
#import "UNUserNotificationCenter+APPLocalNotification.h"
@import UserNotifications;
......@@ -52,6 +53,8 @@
self.dict = dictionary;
[self actions];
return self;
}
......@@ -119,7 +122,21 @@
*/
- (NSNumber*) badge
{
return [NSNumber numberWithInt:[[dict objectForKey:@"badge"] intValue]];
id value = [dict objectForKey:@"badge"];
return (value == NULL) ? NULL : [NSNumber numberWithInt:[value intValue]];
}
/**
* The category of the notification.
*
* @return [ NSString* ]
*/
- (NSString*) categoryId
{
NSString* value = [dict objectForKey:@"actionGroupId"];
return value.length ? value : kAPPGeneralCategory;
}
/**
......@@ -148,19 +165,12 @@
return [UNNotificationSound soundNamed:file];
}
/**
* The date when to fire the notification.
* Additional content to attach.
*
* @return [ NSDate* ]
* @return [ UNNotificationSound* ]
*/
- (NSDate*) fireDate
{
double timestamp = [[dict objectForKey:@"at"]
doubleValue];
return [NSDate dateWithTimeIntervalSince1970:timestamp];
}
- (NSArray<UNNotificationAttachment *> *) attachments
{
NSArray* paths = [dict objectForKey:@"attachments"];
......@@ -186,6 +196,47 @@
return attachments;
}
/**
* Additional actions for the notification.
*
* @return [ NSArray* ]
*/
- (NSArray<UNNotificationAction *> *) actions
{
NSArray* items = [dict objectForKey:@"actions"];
NSMutableArray* actions = [[NSMutableArray alloc] init];
if (!items)
return actions;
for (NSDictionary* item in items) {
NSString* id = [item objectForKey:@"id"];
NSString* title = [item objectForKey:@"title"];
UNNotificationActionOptions options = UNNotificationActionOptionNone;
if ([[item objectForKey:@"launch"] boolValue]) {
options = UNNotificationActionOptionForeground;
}
if ([[item objectForKey:@"ui"] isEqualToString:@"decline"]) {
options = options | UNNotificationActionOptionDestructive;
}
if ([[item objectForKey:@"needsAuth"] boolValue]) {
options = options | UNNotificationActionOptionAuthenticationRequired;
}
UNNotificationAction* action;
action = [UNNotificationAction actionWithIdentifier:id
title:title
options:options];
[actions addObject:action];
}
return actions;
}
#pragma mark -
#pragma mark Public
......@@ -224,6 +275,19 @@
#pragma mark Private
/**
* The date when to fire the notification.
*
* @return [ NSDate* ]
*/
- (NSDate*) triggerDate
{
double timestamp = [[dict objectForKey:@"at"]
doubleValue];
return [NSDate dateWithTimeIntervalSince1970:timestamp];
}
/**
* If the notification shall be repeating.
*
* @return [ BOOL ]
......@@ -299,7 +363,7 @@
initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *date = [cal components:[self repeatInterval]
fromDate:[self fireDate]];
fromDate:[self triggerDate]];
date.timeZone = [NSTimeZone defaultTimeZone];
......@@ -333,7 +397,7 @@
*/
- (double) timeInterval
{
return MAX(0.01f, [self.fireDate timeIntervalSinceDate:[NSDate date]]);
return MAX(0.01f, [self.triggerDate timeIntervalSinceDate:[NSDate date]]);
}
/**
......@@ -464,10 +528,6 @@
{
return [self urlForAsset:path];
}
else if ([path hasPrefix:@"app://"])
{
return [self urlForAppInternalPath:path];
}
else if ([path hasPrefix:@"base64:"])
{
return [self urlFromBase64:path];
......@@ -515,7 +575,7 @@
{
NSFileManager* fm = [NSFileManager defaultManager];
NSBundle* mainBundle = [NSBundle mainBundle];
NSString* bundlePath = [mainBundle bundlePath];
NSString* bundlePath = [mainBundle resourcePath];
if ([path isEqualToString:@"res://icon"]) {
path = @"res://AppIcon60x60@3x.png";
......@@ -561,26 +621,6 @@
}
/**
* URL for an internal app path.
*
* @param [ NSString* ] path A relative file path from main bundle dir.
*
* @return [ NSURL* ]
*/
- (NSURL*) urlForAppInternalPath:(NSString*)path
{
NSFileManager* fm = [NSFileManager defaultManager];
NSBundle* mainBundle = [NSBundle mainBundle];
NSString* absPath = [mainBundle bundlePath];
if (![fm fileExistsAtPath:absPath]) {
NSLog(@"File not found: %@", absPath);
}
return [NSURL fileURLWithPath:absPath];
}
/**
* URL for a base64 encoded string.
*
* @param [ NSString* ] base64String Base64 encoded string.
......
......@@ -38,7 +38,10 @@ typedef NS_ENUM(NSUInteger, APPNotificationType) {
@property (readonly, getter=getNotifications) NSArray* localNotifications;
@property (readonly, getter=getNotificationIds) NSArray* localNotificationIds;
// Register general notification category to listen for dismiss actions
- (void) registerGeneralNotificationCategory;
// Add the specified category to the list of categories
- (void) addNotificationCategory:(UNNotificationCategory*)category;
// List of all notification IDs from given type
- (NSArray*) getNotificationIdsByType:(APPNotificationType)type;
......
......@@ -48,7 +48,34 @@ NSString * const kAPPGeneralCategory = @"GENERAL";
intentIdentifiers:@[]
options:UNNotificationCategoryOptionCustomDismissAction];
[self setNotificationCategories:[NSSet setWithObjects:category, nil]];
[self setNotificationCategories:[NSSet setWithObject:category]];
}
/**
* Add the specified category to the list of categories.
*
* @param [ UNNotificationCategory* ] category The category to add.
*
* @return [ Void ]
*/
- (void) addNotificationCategory:(UNNotificationCategory*)category
{
if (!category)
return;
[self getNotificationCategoriesWithCompletionHandler:^(NSSet<UNNotificationCategory *> *set) {
NSMutableSet* categories = [NSMutableSet setWithSet:set];
for (UNNotificationCategory* item in categories) {
if ([category.identifier isEqualToString:item.identifier]) {
[categories removeObject:item];
break;
}
}
[categories addObject:category];
[self setNotificationCategories:categories];
}];
}
#pragma mark -
......
......@@ -398,6 +398,21 @@ exports.getAllTriggered = function (callback, scope) {
};
/**
* Register an group of actions by id.
*
* @param [ String ] id The Id of the group.
* @param [ Array] actions The action config settings.
* @param [ Function ] callback The function to be exec as the callback.
* @param [ Object ] scope The callback function's scope.
*
* @return [ Void ]
*/
exports.addActionGroup = function (id, actions, callback, scope) {
var config = { actionGroupId: id, actions: actions };
this.exec('registerCategory', config, callback, scope);
};
/**
* The (platform specific) default settings.
*
* @return [ Object ]
......
......@@ -21,14 +21,16 @@ var exec = require('cordova/exec'),
// Default values
exports._defaults = {
id: 0,
text: '',
title: '',
sound: 'res://platform_default',
badge: 0,
id: 0,
badge: undefined,
data: undefined,
every: undefined,
at: undefined
at: undefined,
actions: undefined,
actionGroupId: undefined
};
// Listener
......@@ -150,10 +152,53 @@ exports.convertProperties = function (options) {
options.data = JSON.stringify(options.data);
}
if (options.actions) {
this.convertActions(options);
}
return options;
};
/**
* Convert the passed values to their required type, modifying them
* directly for Android and passing the converted list back for iOS.
*
* @param [ Map ] options Set of custom values.
*
* @return [ Map ] Interaction object with category & actions.
*/
exports.convertActions = function (options) {
if (!options.actions)
return null;
var MAX_ACTIONS = (device.platform === 'iOS') ? 4 : 3,
actions = [];
if (options.actions.length > MAX_ACTIONS)
console.warn('Count of actions exceeded count of ' + MAX_ACTIONS);
for (var i = 0; i < options.actions.length && MAX_ACTIONS > 0; i++) {
var action = options.actions[i];
if (!action.id) {
console.warn(
'Action with title ' + action.title + ' has no id and will not be added.');
continue;
}
action.id = action.id.toString();
action.title = (action.title || action.id).toString();
actions.push(action);
MAX_ACTIONS--;
}
options.category = (options.category || 'DEFAULT_GROUP').toString();
options.actions = actions;
};
/**
* Create a callback function to get executed within a specific scope.
*
* @param [ Function ] fn The function to be exec as the callback.
......
......@@ -278,6 +278,20 @@ exports.getAllTriggered = function (callback, scope) {
};
/**
* Register an group of actions by id.
*
* @param [ String ] id The Id of the group.
* @param [ Array] actions The action config settings.
* @param [ Function ] callback The function to be exec as the callback.
* @param [ Object ] scope The callback function's scope.
*
* @return [ Void ]
*/
exports.addActionGroup = function (id, actions, callback, scope) {
this.core.registerActionGroup(id, actions, callback, scope);
};
/**
* The (platform specific) default settings.
*
* @return [ Object ]
......
......@@ -50,6 +50,9 @@
<a id="sched_delayed" class="button orange">Delayed</a>
<a id="sched_interval" class="button orange">Interval</a>
</div>
<div class="container">
<a id="sched_actions" class="button orange">Actions</a>
</div>
<h2 class="section">Update</h2>
<div class="container">
<a id="update_text" class="button orange">Text</a>
......
......@@ -57,6 +57,7 @@ var app = {
document.getElementById('sched_multi').onclick = app.scheduleMultiple;
document.getElementById('sched_delayed').onclick = app.scheduleDelayed;
document.getElementById('sched_interval').onclick = app.scheduleInterval;
document.getElementById('sched_actions').onclick = app.scheduleActions;
document.getElementById('clear_single').onclick = app.clearSingle;
document.getElementById('clear_multi').onclick = app.clearMulti;
document.getElementById('clear_all').onclick = app.clearAll;
......@@ -95,7 +96,6 @@ var app = {
text: 'Test Message 1',
icon: 'http://3.bp.blogspot.com/-Qdsy-GpempY/UU_BN9LTqSI/AAAAAAAAAMA/LkwLW2yNBJ4/s1600/supersu.png',
smallIcon: 'res://cordova',
attachments: ['file://img/logo.png'],
sound: null,
badge: 1,
data: { test: 1 }
......@@ -148,6 +148,24 @@ var app = {
smallIcon: 'res://ic_popup_sync'
});
},
// Schedule with actions
scheduleActions: function () {
cordova.plugins.notification.local.schedule({
title: 'Local Notification Plugin',
text: 'Made by appPlant from Leipzig/Germany',
attachments: ['file://img/logo.png'],
actionGroupId: 'like-dislike',
actions: [{
id: 'like',
title: 'Like',
launch: true
},{
id: 'dislike',
title: 'Dislike',
ui: 'decline'
}]
});
},
// Clear a single notification
clearSingle: function () {
cordova.plugins.notification.local.clear(1, app.ids);
......
......@@ -398,6 +398,21 @@ exports.getAllTriggered = function (callback, scope) {
};
/**
* Register an group of actions by id.
*
* @param [ String ] id The Id of the group.
* @param [ Array] actions The action config settings.
* @param [ Function ] callback The function to be exec as the callback.
* @param [ Object ] scope The callback function's scope.
*
* @return [ Void ]
*/
exports.addActionGroup = function (id, actions, callback, scope) {
var config = { actionGroupId: id, actions: actions };
this.exec('registerCategory', config, callback, scope);
};
/**
* The (platform specific) default settings.
*
* @return [ Object ]
......
......@@ -21,14 +21,16 @@ var exec = require('cordova/exec'),
// Default values
exports._defaults = {
id: 0,
text: '',
title: '',
sound: 'res://platform_default',
badge: 0,
id: 0,
badge: undefined,
data: undefined,
every: undefined,
at: undefined
at: undefined,
actions: undefined,
actionGroupId: undefined
};
// Listener
......@@ -150,10 +152,53 @@ exports.convertProperties = function (options) {
options.data = JSON.stringify(options.data);
}
if (options.actions) {
this.convertActions(options);
}
return options;
};
/**
* Convert the passed values to their required type, modifying them
* directly for Android and passing the converted list back for iOS.
*
* @param [ Map ] options Set of custom values.
*
* @return [ Map ] Interaction object with category & actions.
*/
exports.convertActions = function (options) {
if (!options.actions)
return null;
var MAX_ACTIONS = (device.platform === 'iOS') ? 4 : 3,
actions = [];
if (options.actions.length > MAX_ACTIONS)
console.warn('Count of actions exceeded count of ' + MAX_ACTIONS);
for (var i = 0; i < options.actions.length && MAX_ACTIONS > 0; i++) {
var action = options.actions[i];
if (!action.id) {
console.warn(
'Action with title ' + action.title + ' has no id and will not be added.');
continue;
}
action.id = action.id.toString();
action.title = (action.title || action.id).toString();
actions.push(action);
MAX_ACTIONS--;
}
options.category = (options.category || 'DEFAULT_GROUP').toString();
options.actions = actions;
};
/**
* Create a callback function to get executed within a specific scope.
*
* @param [ Function ] fn The function to be exec as the callback.
......
......@@ -278,6 +278,20 @@ exports.getAllTriggered = function (callback, scope) {
};
/**
* Register an group of actions by id.
*
* @param [ String ] id The Id of the group.
* @param [ Array] actions The action config settings.
* @param [ Function ] callback The function to be exec as the callback.
* @param [ Object ] scope The callback function's scope.
*
* @return [ Void ]
*/
exports.addActionGroup = function (id, actions, callback, scope) {
this.core.registerActionGroup(id, actions, callback, scope);
};
/**
* The (platform specific) default settings.
*
* @return [ Object ]
......
......@@ -35,6 +35,9 @@
// Request permission to show notifications
- (void) request:(CDVInvokedUrlCommand*)command;
// Register/update an action group
- (void) registerCategory:(CDVInvokedUrlCommand*)command;
// Schedule notifications
- (void) schedule:(CDVInvokedUrlCommand*)command;
//// Update set of notifications
......
......@@ -100,7 +100,7 @@
// NSNumber* id = [options objectForKey:@"id"];
// UNNotificationRequest* notification;
//
// notification = [self.center getNotificationWithId:id];
// notification = [_center getNotificationWithId:id];
//
// if (!notification)
// continue;
......@@ -119,7 +119,7 @@
// NSNumber* id = [options objectForKey:@"id"];
// UILocalNotification* notification;
//
// notification = [self.app localNotificationWithId:id];
// notification = [_app localNotificationWithId:id];
//
// if (!notification)
// continue;
......@@ -152,12 +152,12 @@
for (NSNumber* id in command.arguments) {
UNNotificationRequest* notification;
notification = [self.center getNotificationWithId:id];
notification = [_center getNotificationWithId:id];
if (!notification)
continue;
[self.center clearNotification:notification];
[_center clearNotification:notification];
[self fireEvent:@"clear" notification:notification];
}
......@@ -173,8 +173,8 @@
- (void) clearAll:(CDVInvokedUrlCommand*)command
{
[self.commandDelegate runInBackground:^{
[self.center clearAllNotifications];
[self.app setApplicationIconBadgeNumber:0];
[_center clearAllNotifications];
[_app setApplicationIconBadgeNumber:0];
[self fireEvent:@"clearall"];
[self execCallback:command];
}];
......@@ -193,12 +193,12 @@
for (NSNumber* id in command.arguments) {
UNNotificationRequest* notification;
notification = [self.center getNotificationWithId:id];
notification = [_center getNotificationWithId:id];
if (!notification)
continue;
[self.center cancelNotification:notification];
[_center cancelNotification:notification];
[self fireEvent:@"cancel" notification:notification];
}
......@@ -214,8 +214,8 @@
- (void) cancelAll:(CDVInvokedUrlCommand*)command
{
[self.commandDelegate runInBackground:^{
[self.center cancelAllNotifications];
[self.app setApplicationIconBadgeNumber:0];
[_center cancelAllNotifications];
[_app setApplicationIconBadgeNumber:0];
[self fireEvent:@"cancelall"];
[self execCallback:command];
}];
......@@ -321,7 +321,7 @@
byType:(APPNotificationType)type;
{
[self.commandDelegate runInBackground:^{
NSArray* ids = [self.center getNotificationIdsByType:type];
NSArray* ids = [_center getNotificationIdsByType:type];
CDVPluginResult* result;
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
......@@ -497,11 +497,30 @@
UNAuthorizationOptions options =
(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert);
[self.center requestAuthorizationWithOptions:options completionHandler:^(BOOL granted, NSError* e) {
[_center requestAuthorizationWithOptions:options completionHandler:^(BOOL granted, NSError* e) {
[self check:command];
}];
}
/**
* Register/update an action group.
*
* @return [ Void ]
*/
- (void) registerCategory:(CDVInvokedUrlCommand *)command
{
[self.commandDelegate runInBackground:^{
NSDictionary* options = command.arguments[0];
APPNotificationContent* notification;
notification = [[APPNotificationContent alloc]
initWithOptions:options];
[_center addNotificationCategory:notification.category];
[self execCallback:command];
}];
}
#pragma mark -
#pragma mark Private
......@@ -513,6 +532,8 @@
__weak APPLocalNotification* weakSelf = self;
UNNotificationRequest* request = notification.request;
[_center addNotificationCategory:notification.category];
[_center addNotificationRequest:request withCompletionHandler:^(NSError* e) {
__strong APPLocalNotification* strongSelf = weakSelf;
[strongSelf fireEvent:@"add" notification:request];
......
......@@ -30,5 +30,6 @@
- (id) initWithOptions:(NSDictionary*)dict;
- (APPNotificationOptions*) options;
- (UNNotificationRequest*) request;
- (UNNotificationCategory*) category;
@end
......@@ -23,7 +23,6 @@
#import "APPNotificationContent.h"
#import "APPNotificationOptions.h"
#import "UNUserNotificationCenter+APPLocalNotification.h"
#import <objc/runtime.h>
@import UserNotifications;
......@@ -67,7 +66,7 @@ static char optionsKey;
self.sound = options.sound;
self.badge = options.badge;
self.attachments = options.attachments;
self.categoryIdentifier = kAPPGeneralCategory;
self.categoryIdentifier = options.categoryId;
}
#pragma mark -
......@@ -107,6 +106,25 @@ static char optionsKey;
trigger:opts.trigger];
}
/**
* The category for the notification with all the actions.
*
* @return [ UNNotificationCategory* ]
*/
- (UNNotificationCategory*) category
{
NSString* categoryId = self.categoryIdentifier;
NSArray* actions = self.options.actions;
if (!actions.count)
return NULL;
return [UNNotificationCategory categoryWithIdentifier:categoryId
actions:actions
intentIdentifiers:@[]
options:UNNotificationCategoryOptionCustomDismissAction];
}
#pragma mark -
#pragma mark Private
......
......@@ -27,13 +27,15 @@
@property (readonly, getter=id) NSNumber* id;
@property (readonly, getter=identifier) NSString* identifier;
@property (readonly, getter=categoryId) NSString* categoryId;
@property (readonly, getter=title) NSString* title;
@property (readonly, getter=subtitle) NSString* subtitle;
@property (readonly, getter=badge) NSNumber* badge;
@property (readonly, getter=text) NSString* text;
@property (readonly, getter=sound) UNNotificationSound* sound;
@property (readonly, getter=attachments) NSArray<UNNotificationAttachment *> * attachments;
@property (readonly, getter=userInfo) NSDictionary* userInfo;
@property (readonly, getter=actions) NSArray<UNNotificationAction *> * actions;
@property (readonly, getter=attachments) NSArray<UNNotificationAttachment *> * attachments;
- (id) initWithDict:(NSDictionary*)dict;
- (UNNotificationTrigger*) trigger;
......
......@@ -22,6 +22,7 @@
*/
#import "APPNotificationOptions.h"
#import "UNUserNotificationCenter+APPLocalNotification.h"
@import UserNotifications;
......@@ -52,6 +53,8 @@
self.dict = dictionary;
[self actions];
return self;
}
......@@ -119,7 +122,21 @@
*/
- (NSNumber*) badge
{
return [NSNumber numberWithInt:[[dict objectForKey:@"badge"] intValue]];
id value = [dict objectForKey:@"badge"];
return (value == NULL) ? NULL : [NSNumber numberWithInt:[value intValue]];
}
/**
* The category of the notification.
*
* @return [ NSString* ]
*/
- (NSString*) categoryId
{
NSString* value = [dict objectForKey:@"actionGroupId"];
return value.length ? value : kAPPGeneralCategory;
}
/**
......@@ -148,19 +165,12 @@
return [UNNotificationSound soundNamed:file];
}
/**
* The date when to fire the notification.
* Additional content to attach.
*
* @return [ NSDate* ]
* @return [ UNNotificationSound* ]
*/
- (NSDate*) fireDate
{
double timestamp = [[dict objectForKey:@"at"]
doubleValue];
return [NSDate dateWithTimeIntervalSince1970:timestamp];
}
- (NSArray<UNNotificationAttachment *> *) attachments
{
NSArray* paths = [dict objectForKey:@"attachments"];
......@@ -186,6 +196,47 @@
return attachments;
}
/**
* Additional actions for the notification.
*
* @return [ NSArray* ]
*/
- (NSArray<UNNotificationAction *> *) actions
{
NSArray* items = [dict objectForKey:@"actions"];
NSMutableArray* actions = [[NSMutableArray alloc] init];
if (!items)
return actions;
for (NSDictionary* item in items) {
NSString* id = [item objectForKey:@"id"];
NSString* title = [item objectForKey:@"title"];
UNNotificationActionOptions options = UNNotificationActionOptionNone;
if ([[item objectForKey:@"launch"] boolValue]) {
options = UNNotificationActionOptionForeground;
}
if ([[item objectForKey:@"ui"] isEqualToString:@"decline"]) {
options = options | UNNotificationActionOptionDestructive;
}
if ([[item objectForKey:@"needsAuth"] boolValue]) {
options = options | UNNotificationActionOptionAuthenticationRequired;
}
UNNotificationAction* action;
action = [UNNotificationAction actionWithIdentifier:id
title:title
options:options];
[actions addObject:action];
}
return actions;
}
#pragma mark -
#pragma mark Public
......@@ -224,6 +275,19 @@
#pragma mark Private
/**
* The date when to fire the notification.
*
* @return [ NSDate* ]
*/
- (NSDate*) triggerDate
{
double timestamp = [[dict objectForKey:@"at"]
doubleValue];
return [NSDate dateWithTimeIntervalSince1970:timestamp];
}
/**
* If the notification shall be repeating.
*
* @return [ BOOL ]
......@@ -299,7 +363,7 @@
initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *date = [cal components:[self repeatInterval]
fromDate:[self fireDate]];
fromDate:[self triggerDate]];
date.timeZone = [NSTimeZone defaultTimeZone];
......@@ -333,7 +397,7 @@
*/
- (double) timeInterval
{
return MAX(0.01f, [self.fireDate timeIntervalSinceDate:[NSDate date]]);
return MAX(0.01f, [self.triggerDate timeIntervalSinceDate:[NSDate date]]);
}
/**
......@@ -464,10 +528,6 @@
{
return [self urlForAsset:path];
}
else if ([path hasPrefix:@"app://"])
{
return [self urlForAppInternalPath:path];
}
else if ([path hasPrefix:@"base64:"])
{
return [self urlFromBase64:path];
......@@ -515,7 +575,7 @@
{
NSFileManager* fm = [NSFileManager defaultManager];
NSBundle* mainBundle = [NSBundle mainBundle];
NSString* bundlePath = [mainBundle bundlePath];
NSString* bundlePath = [mainBundle resourcePath];
if ([path isEqualToString:@"res://icon"]) {
path = @"res://AppIcon60x60@3x.png";
......@@ -561,26 +621,6 @@
}
/**
* URL for an internal app path.
*
* @param [ NSString* ] path A relative file path from main bundle dir.
*
* @return [ NSURL* ]
*/
- (NSURL*) urlForAppInternalPath:(NSString*)path
{
NSFileManager* fm = [NSFileManager defaultManager];
NSBundle* mainBundle = [NSBundle mainBundle];
NSString* absPath = [mainBundle bundlePath];
if (![fm fileExistsAtPath:absPath]) {
NSLog(@"File not found: %@", absPath);
}
return [NSURL fileURLWithPath:absPath];
}
/**
* URL for a base64 encoded string.
*
* @param [ NSString* ] base64String Base64 encoded string.
......
......@@ -38,7 +38,10 @@ typedef NS_ENUM(NSUInteger, APPNotificationType) {
@property (readonly, getter=getNotifications) NSArray* localNotifications;
@property (readonly, getter=getNotificationIds) NSArray* localNotificationIds;
// Register general notification category to listen for dismiss actions
- (void) registerGeneralNotificationCategory;
// Add the specified category to the list of categories
- (void) addNotificationCategory:(UNNotificationCategory*)category;
// List of all notification IDs from given type
- (NSArray*) getNotificationIdsByType:(APPNotificationType)type;
......
......@@ -48,7 +48,34 @@ NSString * const kAPPGeneralCategory = @"GENERAL";
intentIdentifiers:@[]
options:UNNotificationCategoryOptionCustomDismissAction];
[self setNotificationCategories:[NSSet setWithObjects:category, nil]];
[self setNotificationCategories:[NSSet setWithObject:category]];
}
/**
* Add the specified category to the list of categories.
*
* @param [ UNNotificationCategory* ] category The category to add.
*
* @return [ Void ]
*/
- (void) addNotificationCategory:(UNNotificationCategory*)category
{
if (!category)
return;
[self getNotificationCategoriesWithCompletionHandler:^(NSSet<UNNotificationCategory *> *set) {
NSMutableSet* categories = [NSMutableSet setWithSet:set];
for (UNNotificationCategory* item in categories) {
if ([category.identifier isEqualToString:item.identifier]) {
[categories removeObject:item];
break;
}
}
[categories addObject:category];
[self setNotificationCategories:categories];
}];
}
#pragma mark -
......
......@@ -397,6 +397,21 @@ exports.getAllTriggered = function (callback, scope) {
};
/**
* Register an group of actions by id.
*
* @param [ String ] id The Id of the group.
* @param [ Array] actions The action config settings.
* @param [ Function ] callback The function to be exec as the callback.
* @param [ Object ] scope The callback function's scope.
*
* @return [ Void ]
*/
exports.addActionGroup = function (id, actions, callback, scope) {
var config = { actionGroupId: id, actions: actions };
this.exec('registerCategory', config, callback, scope);
};
/**
* The (platform specific) default settings.
*
* @return [ Object ]
......
......@@ -20,14 +20,16 @@ var exec = require('cordova/exec'),
// Default values
exports._defaults = {
id: 0,
text: '',
title: '',
sound: 'res://platform_default',
badge: 0,
id: 0,
badge: undefined,
data: undefined,
every: undefined,
at: undefined
at: undefined,
actions: undefined,
actionGroupId: undefined
};
// Listener
......@@ -149,10 +151,53 @@ exports.convertProperties = function (options) {
options.data = JSON.stringify(options.data);
}
if (options.actions) {
this.convertActions(options);
}
return options;
};
/**
* Convert the passed values to their required type, modifying them
* directly for Android and passing the converted list back for iOS.
*
* @param [ Map ] options Set of custom values.
*
* @return [ Map ] Interaction object with category & actions.
*/
exports.convertActions = function (options) {
if (!options.actions)
return null;
var MAX_ACTIONS = (device.platform === 'iOS') ? 4 : 3,
actions = [];
if (options.actions.length > MAX_ACTIONS)
console.warn('Count of actions exceeded count of ' + MAX_ACTIONS);
for (var i = 0; i < options.actions.length && MAX_ACTIONS > 0; i++) {
var action = options.actions[i];
if (!action.id) {
console.warn(
'Action with title ' + action.title + ' has no id and will not be added.');
continue;
}
action.id = action.id.toString();
action.title = (action.title || action.id).toString();
actions.push(action);
MAX_ACTIONS--;
}
options.category = (options.category || 'DEFAULT_GROUP').toString();
options.actions = actions;
};
/**
* Create a callback function to get executed within a specific scope.
*
* @param [ Function ] fn The function to be exec as the callback.
......
......@@ -277,6 +277,20 @@ exports.getAllTriggered = function (callback, scope) {
};
/**
* Register an group of actions by id.
*
* @param [ String ] id The Id of the group.
* @param [ Array] actions The action config settings.
* @param [ Function ] callback The function to be exec as the callback.
* @param [ Object ] scope The callback function's scope.
*
* @return [ Void ]
*/
exports.addActionGroup = function (id, actions, callback, scope) {
this.core.registerActionGroup(id, actions, callback, scope);
};
/**
* The (platform specific) default settings.
*
* @return [ Object ]
......
......@@ -50,6 +50,9 @@
<a id="sched_delayed" class="button orange">Delayed</a>
<a id="sched_interval" class="button orange">Interval</a>
</div>
<div class="container">
<a id="sched_actions" class="button orange">Actions</a>
</div>
<h2 class="section">Update</h2>
<div class="container">
<a id="update_text" class="button orange">Text</a>
......
......@@ -57,6 +57,7 @@ var app = {
document.getElementById('sched_multi').onclick = app.scheduleMultiple;
document.getElementById('sched_delayed').onclick = app.scheduleDelayed;
document.getElementById('sched_interval').onclick = app.scheduleInterval;
document.getElementById('sched_actions').onclick = app.scheduleActions;
document.getElementById('clear_single').onclick = app.clearSingle;
document.getElementById('clear_multi').onclick = app.clearMulti;
document.getElementById('clear_all').onclick = app.clearAll;
......@@ -95,7 +96,6 @@ var app = {
text: 'Test Message 1',
icon: 'http://3.bp.blogspot.com/-Qdsy-GpempY/UU_BN9LTqSI/AAAAAAAAAMA/LkwLW2yNBJ4/s1600/supersu.png',
smallIcon: 'res://cordova',
attachments: ['file://img/logo.png'],
sound: null,
badge: 1,
data: { test: 1 }
......@@ -148,6 +148,24 @@ var app = {
smallIcon: 'res://ic_popup_sync'
});
},
// Schedule with actions
scheduleActions: function () {
cordova.plugins.notification.local.schedule({
title: 'Local Notification Plugin',
text: 'Made by appPlant from Leipzig/Germany',
attachments: ['file://img/logo.png'],
actionGroupId: 'like-dislike',
actions: [{
id: 'like',
title: 'Like',
launch: true
},{
id: 'dislike',
title: 'Dislike',
ui: 'decline'
}]
});
},
// Clear a single notification
clearSingle: function () {
cordova.plugins.notification.local.clear(1, app.ids);
......
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