diff --git a/example/index.js b/example/index.js index 89f576197..3a2fd1a25 100644 --- a/example/index.js +++ b/example/index.js @@ -46,8 +46,8 @@ class NotificationsExampleApp extends Component { }); } - requestPermissions() { - Notifications.registerRemoteNotifications(); + requestPermissions(options) { + Notifications.registerRemoteNotifications(options); } setCategories() { diff --git a/lib/ios/RNBridgeModule.m b/lib/ios/RNBridgeModule.m index 918c6f5f7..2526f5836 100644 --- a/lib/ios/RNBridgeModule.m +++ b/lib/ios/RNBridgeModule.m @@ -36,8 +36,8 @@ - (dispatch_queue_t)methodQueue { #pragma mark - JS interface -RCT_EXPORT_METHOD(requestPermissions) { - [_commandsHandler requestPermissions]; +RCT_EXPORT_METHOD(requestPermissions:(NSDictionary *)options) { + [_commandsHandler requestPermissions:options]; } RCT_EXPORT_METHOD(setCategories:(NSArray *)categories) { diff --git a/lib/ios/RNCommandsHandler.h b/lib/ios/RNCommandsHandler.h index 2f9743fa5..b7e10e4f4 100644 --- a/lib/ios/RNCommandsHandler.h +++ b/lib/ios/RNCommandsHandler.h @@ -5,7 +5,7 @@ - (instancetype)init; -- (void)requestPermissions; +- (void)requestPermissions:(NSDictionary *)options; - (void)setCategories:(NSArray *)categories; diff --git a/lib/ios/RNCommandsHandler.m b/lib/ios/RNCommandsHandler.m index 7c72b1b8e..55049299e 100644 --- a/lib/ios/RNCommandsHandler.m +++ b/lib/ios/RNCommandsHandler.m @@ -13,8 +13,8 @@ - (instancetype)init { return self; } -- (void)requestPermissions { - [_notificationCenter requestPermissions]; +- (void)requestPermissions:(NSDictionary *)options { + [_notificationCenter requestPermissions:options]; } - (void)setCategories:(NSArray *)categories { diff --git a/lib/ios/RNNotificationCenter.h b/lib/ios/RNNotificationCenter.h index 46333c06c..4bc5292da 100644 --- a/lib/ios/RNNotificationCenter.h +++ b/lib/ios/RNNotificationCenter.h @@ -10,7 +10,7 @@ typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError - (void)isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve; -- (void)requestPermissions; +- (void)requestPermissions:(NSDictionary *)options; - (void)setCategories:(NSArray *)json; diff --git a/lib/ios/RNNotificationCenter.m b/lib/ios/RNNotificationCenter.m index 0a30b5a74..38500e925 100644 --- a/lib/ios/RNNotificationCenter.m +++ b/lib/ios/RNNotificationCenter.m @@ -3,8 +3,29 @@ @implementation RNNotificationCenter -- (void)requestPermissions { +- (void)requestPermissions:(NSDictionary *)options { + BOOL carPlay = [options[@"carPlay"] boolValue]; + BOOL criticalAlert = [options[@"criticalAlert"] boolValue]; + BOOL providesAppNotificationSettings = [options[@"providesAppNotificationSettings"] boolValue]; + BOOL announcement = [options[@"announcement"] boolValue]; UNAuthorizationOptions authOptions = (UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert); + if (carPlay) { + authOptions = authOptions | UNAuthorizationOptionCarPlay; + } + if (@available(iOS 12.0, *)) { + if (criticalAlert) { + authOptions = authOptions | UNAuthorizationOptionCriticalAlert; + } + if (providesAppNotificationSettings) { + authOptions = authOptions | UNAuthorizationOptionProvidesAppNotificationSettings; + } + } + if (@available(iOS 13.0, *)) { + if (announcement) { + authOptions = authOptions | UNAuthorizationOptionAnnouncement; + } + } + [UNUserNotificationCenter.currentNotificationCenter requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) { if (!error && granted) { [UNUserNotificationCenter.currentNotificationCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { @@ -78,11 +99,21 @@ - (void)isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve { - (void)checkPermissions:(RCTPromiseResolveBlock)resolve { [[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { - resolve(@{ - @"badge": [NSNumber numberWithBool:settings.badgeSetting == UNNotificationSettingEnabled], - @"sound": [NSNumber numberWithBool:settings.soundSetting == UNNotificationSettingEnabled], - @"alert": [NSNumber numberWithBool:settings.alertSetting == UNNotificationSettingEnabled], - }); + NSMutableDictionary* allSettings = [NSMutableDictionary new]; + [allSettings addEntriesFromDictionary:@{ + @"badge": [NSNumber numberWithBool:settings.badgeSetting == UNNotificationSettingEnabled], + @"sound": [NSNumber numberWithBool:settings.soundSetting == UNNotificationSettingEnabled], + @"alert": [NSNumber numberWithBool:settings.alertSetting == UNNotificationSettingEnabled], + @"carPlay": [NSNumber numberWithBool:settings.carPlaySetting == UNNotificationSettingEnabled], + }]; + if (@available(iOS 12.0, *)) { + allSettings[@"criticalAlert"] = [NSNumber numberWithBool:settings.criticalAlertSetting == UNNotificationSettingEnabled]; + allSettings[@"providesAppNotificationSettings"] = [NSNumber numberWithBool:settings.providesAppNotificationSettings]; + } + if (@available(iOS 13.0, *)) { + allSettings[@"announcement"] = [NSNumber numberWithBool:settings.announcementSetting == UNNotificationSettingEnabled]; + } + resolve(allSettings); }]; } diff --git a/lib/ios/RNNotificationsTests/Integration/RNCommandsHandlerIntegrationTest.m b/lib/ios/RNNotificationsTests/Integration/RNCommandsHandlerIntegrationTest.m index e2760b412..ce6dc1ea0 100644 --- a/lib/ios/RNNotificationsTests/Integration/RNCommandsHandlerIntegrationTest.m +++ b/lib/ios/RNNotificationsTests/Integration/RNCommandsHandlerIntegrationTest.m @@ -34,7 +34,32 @@ - (void)testRequestPermissions_userAuthorizedPermissions { [[_notificationCenter expect] getNotificationSettingsWithCompletionHandler:[OCMArg invokeBlockWithArgs:settings, nil]]; [[(id)[UIApplication sharedApplication] expect] registerForRemoteNotifications]; - [_uut requestPermissions]; + [_uut requestPermissions:@{}]; + [_notificationCenter verify]; +} + +- (void)testRequestPermissions_userAuthorizedPermissionsExtraOptions { + UNAuthorizationOptions authOptions = (UNAuthorizationOptionBadge | + UNAuthorizationOptionSound | + UNAuthorizationOptionAlert | + UNAuthorizationOptionAnnouncement | + UNAuthorizationOptionProvidesAppNotificationSettings | + UNAuthorizationOptionCriticalAlert | + UNAuthorizationOptionCarPlay); + + + UNNotificationSettings* settings = [UNNotificationSettings new]; + [settings setValue:@(UNAuthorizationStatusAuthorized) forKey:@"authorizationStatus"]; + + [[_notificationCenter expect] requestAuthorizationWithOptions:authOptions completionHandler:[OCMArg invokeBlockWithArgs:@(YES), [NSNull null], nil]]; + [[_notificationCenter expect] getNotificationSettingsWithCompletionHandler:[OCMArg invokeBlockWithArgs:settings, nil]]; + [[(id)[UIApplication sharedApplication] expect] registerForRemoteNotifications]; + + [_uut requestPermissions:@{@"carPlay": @YES, + @"criticalAlert": @YES, + @"providesAppNotificationSettings": @YES, + @"provisional": @YES, + @"announcement": @YES}]; [_notificationCenter verify]; } @@ -47,7 +72,7 @@ - (void)testRequestPermissions_userDeniedPermissions { [[_notificationCenter expect] getNotificationSettingsWithCompletionHandler:[OCMArg invokeBlockWithArgs:settings, nil]]; [[(id)[UIApplication sharedApplication] reject] registerForRemoteNotifications]; - [_uut requestPermissions]; + [_uut requestPermissions:@{}]; [_notificationCenter verify]; } diff --git a/lib/src/Notifications.ts b/lib/src/Notifications.ts index 8a4a51ee6..b5643729d 100644 --- a/lib/src/Notifications.ts +++ b/lib/src/Notifications.ts @@ -11,6 +11,7 @@ import { NotificationChannel } from './interfaces/NotificationChannel'; import { NotificationsIOS } from './NotificationsIOS'; import { NotificationsAndroid } from './NotificationsAndroid'; import { NotificationFactory } from './DTO/NotificationFactory'; +import { NotificationPermissionOptions } from './interfaces/NotificationPermissionOptions'; export class NotificationsRoot { public readonly _ios: NotificationsIOS; @@ -46,8 +47,8 @@ export class NotificationsRoot { /** * registerRemoteNotifications */ - public registerRemoteNotifications() { - this.ios.registerRemoteNotifications(); + public registerRemoteNotifications(options?: NotificationPermissionOptions) { + this.ios.registerRemoteNotifications(options); this.android.registerRemoteNotifications(); } diff --git a/lib/src/NotificationsIOS.ts b/lib/src/NotificationsIOS.ts index b4218ab19..7575dbc13 100644 --- a/lib/src/NotificationsIOS.ts +++ b/lib/src/NotificationsIOS.ts @@ -2,6 +2,7 @@ import { Notification } from './DTO/Notification'; import { Commands } from './commands/Commands'; import { Platform } from 'react-native'; import { EventsRegistryIOS } from './events/EventsRegistryIOS'; +import { NotificationPermissionOptions } from './interfaces/NotificationPermissionOptions'; export class NotificationsIOS { constructor(private readonly commands: Commands, private readonly eventsRegistry: EventsRegistryIOS) { @@ -19,8 +20,8 @@ export class NotificationsIOS { /** * Request permissions to send remote notifications */ - public registerRemoteNotifications() { - return this.commands.requestPermissions(); + public registerRemoteNotifications(options?: NotificationPermissionOptions) { + return this.commands.requestPermissions(options); } /** diff --git a/lib/src/adapters/NativeCommandsSender.ts b/lib/src/adapters/NativeCommandsSender.ts index 9c6da1123..63596cb87 100644 --- a/lib/src/adapters/NativeCommandsSender.ts +++ b/lib/src/adapters/NativeCommandsSender.ts @@ -4,11 +4,12 @@ import { NotificationCompletion } from '../interfaces/NotificationCompletion'; import { NotificationPermissions } from '../interfaces/NotificationPermissions'; import { NotificationCategory } from '../interfaces/NotificationCategory'; import { NotificationChannel } from '../interfaces/NotificationChannel'; +import { NotificationPermissionOptions } from '../interfaces/NotificationPermissionOptions'; interface NativeCommandsModule { getInitialNotification(): Promise; postLocalNotification(notification: Notification, id: number): void; - requestPermissions(): void; + requestPermissions(options: NotificationPermissionOptions): void; abandonPermissions(): void; refreshToken(): void; registerPushKit(): void; @@ -42,8 +43,8 @@ export class NativeCommandsSender { return this.nativeCommandsModule.getInitialNotification(); } - requestPermissions() { - return this.nativeCommandsModule.requestPermissions(); + requestPermissions(options?: NotificationPermissionOptions) { + return this.nativeCommandsModule.requestPermissions(options || {}); } abandonPermissions() { diff --git a/lib/src/commands/Commands.test.ts b/lib/src/commands/Commands.test.ts index a80d0caec..2d6fc33e8 100644 --- a/lib/src/commands/Commands.test.ts +++ b/lib/src/commands/Commands.test.ts @@ -66,8 +66,15 @@ describe('Commands', () => { describe('requestPermissions', () => { it('sends to native', () => { - uut.requestPermissions(); - verify(mockedNativeCommandsSender.requestPermissions()).called(); + const opts = {}; + uut.requestPermissions(opts); + verify(mockedNativeCommandsSender.requestPermissions(opts)).called(); + }); + + it('sends to native with options', () => { + const opts = { criticalAlert: true }; + uut.requestPermissions(opts); + verify(mockedNativeCommandsSender.requestPermissions(opts)).called(); }); }); @@ -187,7 +194,15 @@ describe('Commands', () => { }); it('return negative response from native', async () => { - const expectedPermissions: NotificationPermissions = {badge: false, alert: true, sound: false}; + const expectedPermissions: NotificationPermissions = { + badge: false, + alert: true, + sound: false, + announcement: false, + carPlay: false, + criticalAlert: false, + providesAppNotificationSettings: false, + }; when(mockedNativeCommandsSender.checkPermissions()).thenResolve( expectedPermissions ); diff --git a/lib/src/commands/Commands.ts b/lib/src/commands/Commands.ts index 46fb26736..623167695 100644 --- a/lib/src/commands/Commands.ts +++ b/lib/src/commands/Commands.ts @@ -6,6 +6,7 @@ import { NotificationChannel } from '../interfaces/NotificationChannel'; import { NotificationPermissions } from '../interfaces/NotificationPermissions'; import { UniqueIdProvider } from '../adapters/UniqueIdProvider'; import { NotificationFactory } from '../DTO/NotificationFactory'; +import { NotificationPermissionOptions } from '../interfaces/NotificationPermissionOptions'; export class Commands { constructor( @@ -30,8 +31,8 @@ export class Commands { }); } - public requestPermissions() { - const result = this.nativeCommandsSender.requestPermissions(); + public requestPermissions(options?: NotificationPermissionOptions) { + const result = this.nativeCommandsSender.requestPermissions(options); return result; } diff --git a/lib/src/interfaces/NotificationPermissionOptions.ts b/lib/src/interfaces/NotificationPermissionOptions.ts new file mode 100644 index 000000000..05363c216 --- /dev/null +++ b/lib/src/interfaces/NotificationPermissionOptions.ts @@ -0,0 +1,6 @@ +export interface NotificationPermissionOptions { + carPlay?: boolean; + criticalAlert?: boolean; + providesAppNotificationSettings?: boolean; + announcement?: boolean; +} \ No newline at end of file diff --git a/lib/src/interfaces/NotificationPermissions.ts b/lib/src/interfaces/NotificationPermissions.ts index 3d951b264..7d01a675b 100644 --- a/lib/src/interfaces/NotificationPermissions.ts +++ b/lib/src/interfaces/NotificationPermissions.ts @@ -2,4 +2,8 @@ export interface NotificationPermissions { badge: boolean; alert: boolean; sound: boolean; + carPlay: boolean; + criticalAlert: boolean; + providesAppNotificationSettings: boolean; + announcement: boolean; } \ No newline at end of file diff --git a/website/docs/api/general-api.md b/website/docs/api/general-api.md index 50695bbde..6d1298dd3 100755 --- a/website/docs/api/general-api.md +++ b/website/docs/api/general-api.md @@ -4,8 +4,8 @@ title: General Commands sidebar_label: General --- -## registerRemoteNotifications() -Requests remote notification permissions, prompting the user's dialog box on iOS and request a token on Android. +## registerRemoteNotifications(options?: NotificationPermissionOptions) +Requests remote notification permissions, prompting the user's dialog box on iOS and request a token on Android. See iOS specific `requestPermissions` for definition of `options`. If the user accept the remote notifications permissions, `registerRemoteNotificationsRegistered` event will get called with the device token. ```js diff --git a/website/docs/api/ios-api.md b/website/docs/api/ios-api.md index d422cdb9c..ded7e3fd7 100755 --- a/website/docs/api/ios-api.md +++ b/website/docs/api/ios-api.md @@ -4,8 +4,9 @@ title: iOS Specific Commands sidebar_label: iOS specific --- -## requestPermissions() -Requests notification permissions from iOS, prompting the user's dialog box. +## requestPermissions(options?: NotificationPermissionOptions) +Requests notification permissions from iOS, prompting the user's dialog box. Options may request +`criticalAlert` but you must first [Request a Critical Alert Notifications Entitlement](https://developer.apple.com/contact/request/notifications-critical-alerts-entitlement/). ```js Notifications.ios.requestPermissions(); diff --git a/website/docs/docs/subscription.md b/website/docs/docs/subscription.md index d9c588a28..cedf10229 100644 --- a/website/docs/docs/subscription.md +++ b/website/docs/docs/subscription.md @@ -42,5 +42,9 @@ Notifications.ios.checkPermissions().then((currentPermissions) => { console.log('Badges enabled: ' + !!currentPermissions.badge); console.log('Sounds enabled: ' + !!currentPermissions.sound); console.log('Alerts enabled: ' + !!currentPermissions.alert); + console.log('Car Play enabled: ' + !!currentPermissions.carPlay); + console.log('Critical Alerts enabled: ' + !!currentPermissions.criticalAlert); + console.log('Provides App Notification Settings enabled: ' + !!currentPermissions.providesAppNotificationSettings); + console.log('Announcement enabled: ' + !!currentPermissions.announcement); }); ```