Explorar o código

Support actions (iOS)

Sebastián Katzer %!s(int64=8) %!d(string=hai) anos
pai
achega
7648e4076d

+ 3 - 0
src/ios/APPLocalNotification.h

@@ -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

+ 21 - 0
src/ios/APPLocalNotification.m

@@ -502,6 +502,25 @@
     }];
 }
 
+/**
+ * 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];

+ 1 - 0
src/ios/APPNotificationContent.h

@@ -30,5 +30,6 @@
 - (id) initWithOptions:(NSDictionary*)dict;
 - (APPNotificationOptions*) options;
 - (UNNotificationRequest*) request;
+- (UNNotificationCategory*) category;
 
 @end

+ 20 - 2
src/ios/APPNotificationContent.m

@@ -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
 

+ 3 - 1
src/ios/APPNotificationOptions.h

@@ -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;

+ 74 - 12
src/ios/APPNotificationOptions.m

@@ -22,6 +22,7 @@
  */
 
 #import "APPNotificationOptions.h"
+#import "UNUserNotificationCenter+APPLocalNotification.h"
 
 @import UserNotifications;
 
@@ -51,6 +52,8 @@
     self = [self init];
 
     self.dict = dictionary;
+    
+    [self actions];
 
     return self;
 }
@@ -124,6 +127,18 @@
     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;
+}
+
 /**
  * The sound file for the notification.
  *
@@ -150,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"];
@@ -188,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
 
@@ -225,6 +274,19 @@
 #pragma mark -
 #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.
  *
@@ -301,7 +363,7 @@
                        initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
 
     NSDateComponents *date = [cal components:[self repeatInterval]
-                                    fromDate:[self fireDate]];
+                                    fromDate:[self triggerDate]];
 
     date.timeZone = [NSTimeZone defaultTimeZone];
 
@@ -335,7 +397,7 @@
  */
 - (double) timeInterval
 {
-    return MAX(0.01f, [self.fireDate timeIntervalSinceDate:[NSDate date]]);
+    return MAX(0.01f, [self.triggerDate timeIntervalSinceDate:[NSDate date]]);
 }
 
 /**

+ 3 - 0
src/ios/UNUserNotificationCenter+APPLocalNotification.h

@@ -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;

+ 28 - 1
src/ios/UNUserNotificationCenter+APPLocalNotification.m

@@ -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 -

+ 15 - 0
www/local-notification-core.js

@@ -396,6 +396,21 @@ exports.getAllTriggered = function (callback, scope) {
     this.exec('triggeredNotifications', null, 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.
  *

+ 53 - 8
www/local-notification-util.js

@@ -20,14 +20,16 @@ var exec    = require('cordova/exec'),
 
 // Default values
 exports._defaults = {
-    text:  '',
-    title: '',
-    sound: 'res://platform_default',
-    badge: undefined,
-    id:    0,
-    data:  undefined,
-    every: undefined,
-    at:    undefined
+    id:      0,
+    text:    '',
+    title:   '',
+    sound:   'res://platform_default',
+    badge:   undefined,
+    data:    undefined,
+    every:   undefined,
+    at:      undefined,
+    actions: undefined,
+    actionGroupId: undefined
 };
 
 // Listener
@@ -149,9 +151,52 @@ 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.
  *

+ 14 - 0
www/local-notification.js

@@ -276,6 +276,20 @@ exports.getAllTriggered = function (callback, scope) {
     this.core.getAllTriggered(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.
  *