Skip to content

Commit

Permalink
Merge pull request #69 from Telerik-Verified-Plugins/feature/ios9
Browse files Browse the repository at this point in the history
add features for ios9
  • Loading branch information
mitkodev committed Feb 15, 2016
2 parents 290259a + 5798193 commit e26a813
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 38 deletions.
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ For further information you can take a look into the Backend Services hybrid pus
pushNotification.unregister(successHandler, errorHandler, options);

- iOS 8 interactive push notifications support (available from v.2.5 and above)
- iOS 8 interactive push notifications support (available from v2.5 and above)

// Get the push plugin instance
var pushPlugin = window.plugins.pushNotification;
Expand Down Expand Up @@ -98,6 +98,24 @@ For further information you can take a look into the Backend Services hybrid pus
);

- iOS 9 text input support (available from v3.1 and above)

// Define a Text Input Action
var replyAction = {
identifier: 'REPLY_IDENTIFIER',
title: 'Reply',
activationMode: window.plugins.pushNotification.UserNotificationActivationMode.Background,
destructive: false,
authenticationRequired: true,
behavior: window.plugins.pushNotification.ActionBehavior.TextInput
};

- **IMPORTANT**: When using interactive iOS push notifications with background activation mode, you **must** call the following function, once you are done processing the push notification object:

pushPlugin.notificationProcessed()
This way you'll be able to execute your javascript callback and then notify the operating system to put back your app in background, which is the correct approach to handle such notifications by iOS.


- Set an application icon badge number (iOS only)

// sets the application badge to the provided value
Expand Down
4 changes: 2 additions & 2 deletions plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="com.phonegap.plugins.PushPlugin"
version="3.0.6">
version="3.1.0">

<name>PushPlugin</name>

Expand Down Expand Up @@ -80,7 +80,7 @@
<feature name="PushPlugin">
<param name="ios-package" value="PushPlugin"/>
</feature>
</config-file>
</config-file>

<source-file src="src/ios/AppDelegate+notification.m"/>
<source-file src="src/ios/PushPlugin.m"/>
Expand Down
98 changes: 91 additions & 7 deletions src/ios/AppDelegate+notification.m
Original file line number Diff line number Diff line change
Expand Up @@ -103,27 +103,111 @@ - (void)application:(UIApplication *)application didReceiveRemoteNotification:(N
}
}

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
/*- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void(^)(UIBackgroundFetchResult result))completionHandler
{
NSLog(@"didReceiveRemoteNotification with fetchCompletionHandler");
if (application.applicationState == UIApplicationStateActive) {
PushPlugin *pushHandler = [self getCommandInstance:@"PushPlugin"];
pushHandler.notificationMessage = userInfo;
pushHandler.isInline = YES;
[pushHandler notificationReceived];
completionHandler(UIBackgroundFetchResultNewData);
}
else {
long silent = 0;
id aps = [userInfo objectForKey:@"aps"];
id contentAvailable = [aps objectForKey:@"content-available"];
if ([contentAvailable isKindOfClass:[NSString class]] && [contentAvailable isEqualToString:@"1"]) {
silent = 1;
} else if ([contentAvailable isKindOfClass:[NSNumber class]]) {
silent = [contentAvailable integerValue];
}
if (silent == 1) {
void (^safeHandler)(UIBackgroundFetchResult) = ^(UIBackgroundFetchResult result){
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(result);
});
};
NSMutableDictionary *mutableNotification = [userInfo mutableCopy];
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithCapacity:2];
[params setObject:safeHandler forKey:@"silentNotificationHandler"];
PushPlugin *pushHandler = [self getCommandInstance:@"PushPlugin"];
pushHandler.notificationMessage = mutableNotification;
pushHandler.params= params;
[pushHandler notificationReceived];
} else {
self.launchNotification = userInfo;
completionHandler(UIBackgroundFetchResultNewData);
}
}
}*/

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000
// this method is invoked when:
// - one of the buttons of an interactive notification is tapped
// see https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/IPhoneOSClientImp.html#//apple_ref/doc/uid/TP40008194-CH103-SW1
- (void)application:(UIApplication *) application handleActionWithIdentifier: (NSString *) identifier forRemoteNotification: (NSDictionary *) notification completionHandler: (void (^)()) completionHandler {
- (void)application:(UIApplication *) application handleActionWithIdentifier: (NSString *) identifier forRemoteNotification: (NSDictionary *) notification withResponseInfo:(NSDictionary *)responseInfo completionHandler: (void (^)()) completionHandler {

// the notification already contains the category, but the client also needs the identifier (action button)
NSMutableDictionary *mutableNotification = [notification mutableCopy];
[mutableNotification setObject:identifier forKey:@"identifier"];

if(responseInfo != nil){
NSString *textInput = [[NSString alloc]initWithFormat:@"%@",[responseInfo objectForKey:@"UIUserNotificationActionResponseTypedTextKey"]];
[mutableNotification setValue:textInput forKey:@"textInput"];
}

NSLog(@"handleActionWithIdentifier");
if (application.applicationState == UIApplicationStateActive) {
PushPlugin *pushHandler = [self getCommandInstance:@"PushPlugin"];
pushHandler.notificationMessage = mutableNotification;
pushHandler.isInline = YES;
[pushHandler notificationReceived];
} else {
void (^safeHandler)() = ^(void){
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler();
});
};
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithCapacity:2];
[params setObject:safeHandler forKey:@"remoteNotificationHandler"];
PushPlugin *pushHandler = [self getCommandInstance:@"PushPlugin"];
pushHandler.notificationMessage = mutableNotification;
pushHandler.params= params;
[pushHandler notificationReceived];
}
}
#elif __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
// this method is invoked when:
// - one of the buttons of an interactive notification is tapped
// see https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/IPhoneOSClientImp.html#//apple_ref/doc/uid/TP40008194-CH103-SW1
- (void)application:(UIApplication *) application handleActionWithIdentifier: (NSString *) identifier forRemoteNotification: (NSDictionary *) notification completionHandler: (void (^)()) completionHandler {

[mutableNotification setObject:identifier forKey:@"identifier"];
NSMutableDictionary *mutableNotification = [notification mutableCopy];
[mutableNotification setObject:identifier forKey:@"identifier"];

NSLog(@"handleActionWithIdentifier");
if (application.applicationState == UIApplicationStateActive) {
PushPlugin *pushHandler = [self getCommandInstance:@"PushPlugin"];
pushHandler.notificationMessage = mutableNotification;
pushHandler.isInline = YES;
[pushHandler notificationReceived];
} else {
void (^safeHandler)() = ^(void){
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler();
});
};
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithCapacity:2];
[params setObject:safeHandler forKey:@"remoteNotificationHandler"];
PushPlugin *pushHandler = [self getCommandInstance:@"PushPlugin"];
pushHandler.notificationMessage = mutableNotification;
[pushHandler performSelectorOnMainThread:@selector(notificationReceived) withObject:pushHandler waitUntilDone:NO];
pushHandler.params= params;
[pushHandler notificationReceived];
}
completionHandler();
}
#endif

Expand All @@ -142,7 +226,7 @@ - (void)setLaunchNotification:(NSDictionary *)aDictionary

- (void)dealloc
{
self.launchNotification = nil; // clear the association and release the object
self.launchNotification = nil; // clear the association and release the object
}

@end
4 changes: 4 additions & 0 deletions src/ios/PushPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@
@interface PushPlugin : CDVPlugin
{
NSDictionary *notificationMessage;
NSDictionary *params;
BOOL isInline;
NSString *notificationCallbackId;
NSString *callback;
void (^remoteNotificationHandler)();
void (^silentNotificationHandler)(UIBackgroundFetchResult);
BOOL ready;
}

Expand All @@ -41,6 +44,7 @@
@property (nonatomic, copy) NSString *callback;

@property (nonatomic, strong) NSDictionary *notificationMessage;
@property (nonatomic, strong) NSDictionary *params;
@property BOOL isInline;

- (void)register:(CDVInvokedUrlCommand*)command;
Expand Down
100 changes: 72 additions & 28 deletions src/ios/PushPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
@implementation PushPlugin

@synthesize notificationMessage;
@synthesize params;
@synthesize isInline;

@synthesize callbackId;
Expand Down Expand Up @@ -174,6 +175,13 @@ - (BOOL)createNotificationAction:(NSDictionary *)category
// Set whether the action requires the user to authenticate
BOOL isAuthRequired = [[action objectForKey:@"authenticationRequired"] isEqual:[NSNumber numberWithBool:YES]];
nsAction.authenticationRequired = isAuthRequired;

// Check if the action is actually a text input and behavior is supported
BOOL isTextInput = [@"textInput" isEqualToString:[action objectForKey:@"behavior"]];
if(isTextInput && [nsAction respondsToSelector:NSSelectorFromString(@"setBehavior:")]){
nsAction.behavior = UIUserNotificationActionBehaviorTextInput;
}

[nsActions addObject:nsAction];
}
return YES;
Expand Down Expand Up @@ -342,34 +350,6 @@ - (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
[self failWithMessage:@"" withError:error];
}

- (void)notificationReceived {
NSLog(@"Notification received");

if (notificationMessage && self.callback)
{
NSMutableString *jsonStr = [NSMutableString stringWithString:@"{"];

[self parseDictionary:notificationMessage intoJSON:jsonStr];

if (isInline)
{
[jsonStr appendFormat:@"foreground:\"%d\"", 1];
isInline = NO;
}
else
[jsonStr appendFormat:@"foreground:\"%d\"", 0];

[jsonStr appendString:@"}"];

NSLog(@"Msg: %@", jsonStr);

NSString * jsCallBack = [NSString stringWithFormat:@"%@(%@);", self.callback, jsonStr];
[self.webView stringByEvaluatingJavaScriptFromString:jsCallBack];

self.notificationMessage = nil;
}
}

// reentrant method to drill down and surface all sub-dictionaries' key/value pairs into the top level json
-(void)parseDictionary:(NSDictionary *)inDictionary intoJSON:(NSMutableString *)jsonString
{
Expand Down Expand Up @@ -423,4 +403,68 @@ -(void)failWithMessage:(NSString *)message withError:(NSError *)error
[self.commandDelegate sendPluginResult:commandResult callbackId:self.callbackId];
}

- (void)notificationReceived {
NSLog(@"Notification received");

if (notificationMessage && self.callback)
{
NSMutableString *jsonStr = [NSMutableString stringWithString:@"{"];

[self parseDictionary:notificationMessage intoJSON:jsonStr];

if (isInline)
{
[jsonStr appendFormat:@"foreground:\"%d\"", 1];
isInline = NO;
}
else
[jsonStr appendFormat:@"foreground:\"%d\"", 0];

[jsonStr appendString:@"}"];

NSString * jsCallBack = [NSString stringWithFormat:@"%@(%@);", self.callback, jsonStr];
[self.webView performSelectorOnMainThread:@selector(stringByEvaluatingJavaScriptFromString:) withObject:jsCallBack waitUntilDone:NO];

self.notificationMessage = nil;
}
}

-(void) notificationProcessed:(CDVInvokedUrlCommand*)command
{
NSLog(@"Push Plugin notificationProcessed called");

[self.commandDelegate runInBackground:^ {
UIApplication *app = [UIApplication sharedApplication];

dispatch_async(dispatch_get_main_queue(), ^{
[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:@selector(stopBackgroundTask:)
userInfo:nil
repeats:NO];
});
}];
}

-(void)stopBackgroundTask:(NSTimer*)timer
{
UIApplication *app = [UIApplication sharedApplication];

if (self.params) {
remoteNotificationHandler = [self.params[@"remoteNotificationHandler"] copy];
if (remoteNotificationHandler) {
remoteNotificationHandler();
NSLog(@"remoteNotificationHandler called");
remoteNotificationHandler = nil;
}

silentNotificationHandler = [self.params[@"silentNotificationHandler"] copy];
if (silentNotificationHandler) {
silentNotificationHandler(UIBackgroundFetchResultNewData);
NSLog(@"silentNotificationHandler called");
silentNotificationHandler = nil;
}
}
}

@end
9 changes: 9 additions & 0 deletions www/PushNotification.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ PushNotification.prototype.UserNotificationActivationMode = {
Background : "background"
};

PushNotification.prototype.ActionBehavior = {
TextInput : "textInput"
};

PushNotification.prototype.registerUserNotificationSettings = function(successCallback, errorCallback, options) {
if (errorCallback == null) { errorCallback = function() {}}

Expand Down Expand Up @@ -110,6 +114,11 @@ PushNotification.prototype.setApplicationIconBadgeNumber = function(successCallb
cordova.exec(successCallback, errorCallback, "PushPlugin", "setApplicationIconBadgeNumber", [{badge: badge}]);
};

// Call this to notifiy iOS native code that the push notification was processed
PushNotification.prototype.notificationProcessed = function() {
cordova.exec({}, {}, "PushPlugin", "notificationProcessed");
};

//-------------------------------------------------------------------

if(!window.plugins) {
Expand Down

0 comments on commit e26a813

Please sign in to comment.