2020-04-14 12:54:36 +00:00
/ *
Copyright 2014 OpenMarket Ltd
Copyright 2020 Vector Creations Ltd
Licensed 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 .
* /
# import "PushNotificationService.h"
# import < MatrixKit / MatrixKit . h >
2020-08-05 11:13:53 +00:00
# import < PushKit / PushKit . h >
2020-08-31 17:38:32 +00:00
# import "Riot-Swift.h"
2020-08-05 11:13:53 +00:00
@ interface PushNotificationService ( ) < PKPushRegistryDelegate >
2020-04-14 12:54:36 +00:00
2020-08-20 14:50:15 +00:00
/ * *
Matrix session observer used to detect new opened sessions .
* /
@ property ( nonatomic , weak ) id matrixSessionStateObserver ;
2020-04-14 12:54:36 +00:00
@ property ( nonatomic , nullable , copy ) void ( ^ registrationForRemoteNotificationsCompletion ) ( NSError * ) ;
2020-08-05 11:13:53 +00:00
@ property ( nonatomic , strong ) PKPushRegistry * pushRegistry ;
2020-08-20 14:33:03 +00:00
@ property ( nonatomic , strong ) PushNotificationStore * pushNotificationStore ;
2020-04-14 12:54:36 +00:00
2020-08-11 18:19:47 +00:00
// / Should PushNotificationService receive VoIP pushes
@ property ( nonatomic , assign ) BOOL shouldReceiveVoIPPushes ;
2020-04-14 12:54:36 +00:00
@ end
@ implementation PushNotificationService
2020-08-20 14:33:03 +00:00
- ( instancetype ) initWithPushNotificationStore : ( PushNotificationStore * ) pushNotificationStore
2020-08-11 18:19:47 +00:00
{
if ( self = [ super init ] )
{
2020-08-20 14:33:03 +00:00
self . pushNotificationStore = pushNotificationStore ;
2020-08-11 18:19:47 +00:00
_pushRegistry = [ [ PKPushRegistry alloc ] initWithQueue : dispatch_get _main _queue ( ) ] ;
self . shouldReceiveVoIPPushes = YES ;
}
return self ;
}
2020-06-03 12:08:53 +00:00
# pragma mark - Public Methods
2020-04-14 12:54:36 +00:00
- ( void ) registerUserNotificationSettings
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] registerUserNotificationSettings: isPushRegistered: %@" , @ ( _isPushRegistered ) ) ;
2020-04-14 12:54:36 +00:00
if ( ! _isPushRegistered )
{
UNTextInputNotificationAction * quickReply = [ UNTextInputNotificationAction
actionWithIdentifier : @ "inline-reply"
title : NSLocalizedStringFromTable ( @ "room_message_short_placeholder" , @ "Vector" , nil )
options : UNNotificationActionOptionAuthenticationRequired
] ;
UNNotificationCategory * quickReplyCategory = [ UNNotificationCategory
categoryWithIdentifier : @ "QUICK_REPLY"
actions : @ [ quickReply ]
intentIdentifiers : @ [ ]
options : UNNotificationCategoryOptionNone ] ;
UNUserNotificationCenter * center = [ UNUserNotificationCenter currentNotificationCenter ] ;
[ center setNotificationCategories : [ [ NSSet alloc ] initWithArray : @ [ quickReplyCategory ] ] ] ;
[ center setDelegate : self ] ;
UNAuthorizationOptions authorizationOptions = ( UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge ) ;
[ center requestAuthorizationWithOptions : authorizationOptions
completionHandler : ^ ( BOOL granted , NSError * error )
{ // code here is equivalent to self : application : didRegisterUserNotificationSettings :
if ( granted )
{
[ self registerForRemoteNotificationsWithCompletion : nil ] ;
}
else
{
// Clear existing token
[ self clearPushNotificationToken ] ;
}
} ] ;
}
}
- ( void ) registerForRemoteNotificationsWithCompletion : ( nullable void ( ^ ) ( NSError * ) ) completion
{
self . registrationForRemoteNotificationsCompletion = completion ;
2020-05-22 11:27:30 +00:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ [ UIApplication sharedApplication ] registerForRemoteNotifications ] ;
} ) ;
}
- ( void ) didRegisterForRemoteNotificationsWithDeviceToken : ( NSData * ) deviceToken
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] didRegisterForRemoteNotificationsWithDeviceToken" ) ;
2020-09-02 15:00:05 +00:00
2020-05-22 11:27:30 +00:00
MXKAccountManager * accountManager = [ MXKAccountManager sharedManager ] ;
[ accountManager setApnsDeviceToken : deviceToken ] ;
2020-08-27 16:29:44 +00:00
2020-09-02 15:00:05 +00:00
// Resurrect old PushKit token to better kill it
2020-08-27 16:29:44 +00:00
if ( ! accountManager . pushDeviceToken )
2020-05-22 11:27:30 +00:00
{
2020-08-27 16:29:44 +00:00
// If we don ' t have the pushDeviceToken , we may have migrated it into the shared user defaults .
NSString * pushDeviceToken = [ MXKAppSettings . standardAppSettings . sharedUserDefaults objectForKey : @ "pushDeviceToken" ] ;
if ( pushDeviceToken )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] didRegisterForRemoteNotificationsWithDeviceToken: Move PushKit token to user defaults" ) ;
2020-08-31 14:42:21 +00:00
// Set the token in standard user defaults , as MXKAccount will read it from there when removing the pusher .
// This will allow to remove the PushKit pusher in the next step
2020-08-27 16:29:44 +00:00
[ [ NSUserDefaults standardUserDefaults ] setObject : pushDeviceToken forKey : @ "pushDeviceToken" ] ;
2020-08-31 14:42:21 +00:00
[ MXKAppSettings . standardAppSettings . sharedUserDefaults removeObjectForKey : @ "pushDeviceToken" ] ;
[ MXKAppSettings . standardAppSettings . sharedUserDefaults removeObjectForKey : @ "pushOptions" ] ;
2020-08-27 16:29:44 +00:00
}
2020-05-22 11:27:30 +00:00
}
2020-08-27 14:57:48 +00:00
2020-09-02 15:00:05 +00:00
// If we already have pushDeviceToken or recovered it in above step , remove its PushKit pusher
2020-08-27 16:29:44 +00:00
if ( accountManager . pushDeviceToken )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] didRegisterForRemoteNotificationsWithDeviceToken: A PushKit pusher still exists. Remove it" ) ;
2020-08-31 14:42:21 +00:00
2020-08-27 16:29:44 +00:00
// Attempt to remove PushKit pushers explicitly
2020-08-31 15:51:20 +00:00
[ self clearPushNotificationToken ] ;
2020-08-27 16:29:44 +00:00
}
2020-05-22 11:27:30 +00:00
_isPushRegistered = YES ;
2020-08-05 11:13:53 +00:00
2020-08-20 14:33:03 +00:00
if ( ! _pushNotificationStore . pushKitToken )
2020-08-11 18:19:47 +00:00
{
[ self configurePushKit ] ;
}
2020-05-22 11:27:30 +00:00
if ( self . registrationForRemoteNotificationsCompletion )
{
self . registrationForRemoteNotificationsCompletion ( nil ) ;
self . registrationForRemoteNotificationsCompletion = nil ;
}
}
- ( void ) didFailToRegisterForRemoteNotificationsWithError : ( NSError * ) error
{
[ self clearPushNotificationToken ] ;
if ( self . registrationForRemoteNotificationsCompletion )
{
self . registrationForRemoteNotificationsCompletion ( error ) ;
self . registrationForRemoteNotificationsCompletion = nil ;
}
}
- ( void ) didReceiveRemoteNotification : ( NSDictionary * ) userInfo
fetchCompletionHandler : ( void ( ^ ) ( UIBackgroundFetchResult ) ) completionHandler
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] didReceiveRemoteNotification: applicationState: %tu - payload: %@" , [ UIApplication sharedApplication ] . applicationState , userInfo ) ;
2020-05-26 15:04:18 +00:00
2020-05-22 11:27:30 +00:00
completionHandler ( UIBackgroundFetchResultNewData ) ;
2020-04-14 12:54:36 +00:00
}
- ( void ) deregisterRemoteNotifications
{
_isPushRegistered = NO ;
2020-08-11 18:19:47 +00:00
self . shouldReceiveVoIPPushes = NO ;
2020-04-14 12:54:36 +00:00
}
2020-08-11 18:19:47 +00:00
- ( void ) applicationWillResignActive
2020-04-14 12:54:36 +00:00
{
2020-05-25 14:52:29 +00:00
[ [ UNUserNotificationCenter currentNotificationCenter ] removeUnwantedNotifications ] ;
2020-08-11 18:19:47 +00:00
[ [ UNUserNotificationCenter currentNotificationCenter ] removeCallNotificationsFor : nil ] ;
2020-08-20 14:33:03 +00:00
if ( _pushNotificationStore . pushKitToken )
2020-08-11 18:19:47 +00:00
{
self . shouldReceiveVoIPPushes = YES ;
}
2020-04-14 12:54:36 +00:00
}
2021-06-10 08:43:08 +00:00
- ( void ) applicationDidEnterBackground
{
}
2020-08-10 15:20:02 +00:00
- ( void ) applicationDidBecomeActive
{
[ [ UNUserNotificationCenter currentNotificationCenter ] removeUnwantedNotifications ] ;
2020-08-11 09:53:51 +00:00
[ [ UNUserNotificationCenter currentNotificationCenter ] removeCallNotificationsFor : nil ] ;
2020-08-20 14:33:03 +00:00
if ( _pushNotificationStore . pushKitToken )
2020-08-11 18:19:47 +00:00
{
self . shouldReceiveVoIPPushes = NO ;
}
2020-08-10 15:20:02 +00:00
}
2020-09-02 15:00:05 +00:00
- ( void ) checkPushKitPushersInSession : ( MXSession * ) session
{
[ session . matrixRestClient pushers : ^ ( NSArray < MXPusher * > * pushers ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] checkPushKitPushers: %@ has %@ pushers:" , session . myUserId , @ ( pushers . count ) ) ;
2020-09-02 15:00:05 +00:00
for ( MXPusher * pusher in pushers )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ " - %@" , pusher . appId ) ;
2020-09-02 15:00:05 +00:00
// We do not want anymore PushKit pushers the app used to use
if ( [ pusher . appId isEqualToString : BuildSettings . pushKitAppIdProd ]
|| [ pusher . appId isEqualToString : BuildSettings . pushKitAppIdDev ] )
{
[ self removePusher : pusher inSession : session ] ;
}
}
} failure : ^ ( NSError * error ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] checkPushKitPushers: Error: %@" , error ) ;
2020-09-02 15:00:05 +00:00
} ] ;
}
2020-08-05 11:13:53 +00:00
# pragma mark - Private Methods
2020-08-11 18:19:47 +00:00
- ( void ) setShouldReceiveVoIPPushes : ( BOOL ) shouldReceiveVoIPPushes
2020-08-05 11:13:53 +00:00
{
2020-08-11 18:19:47 +00:00
_shouldReceiveVoIPPushes = shouldReceiveVoIPPushes ;
2021-06-11 11:38:07 +00:00
MXLogDebug ( @ "[PushNotificationService] setShouldReceiveVoIPPushes: %u" , _shouldReceiveVoIPPushes )
2020-08-20 14:33:03 +00:00
if ( _shouldReceiveVoIPPushes && _pushNotificationStore . pushKitToken )
2020-08-05 11:13:53 +00:00
{
2020-08-11 18:19:47 +00:00
MXSession * session = [ AppDelegate theDelegate ] . mxSessions . firstObject ;
if ( session . state >= MXSessionStateStoreDataReady )
{
[ self configurePushKit ] ;
}
else
{
// add an observer for session state
2020-08-20 14:50:15 +00:00
MXWeakify ( self ) ;
NSNotificationCenter * __weak notificationCenter = [ NSNotificationCenter defaultCenter ] ;
self . matrixSessionStateObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXSessionStateDidChangeNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
MXStrongifyAndReturnIfNil ( self ) ;
2020-08-11 18:19:47 +00:00
MXSession * mxSession = ( MXSession * ) notif . object ;
if ( [ [ AppDelegate theDelegate ] . mxSessions containsObject : mxSession ]
&& mxSession . state >= MXSessionStateStoreDataReady
&& self -> _shouldReceiveVoIPPushes )
{
[ self configurePushKit ] ;
2020-08-20 14:50:15 +00:00
[ notificationCenter removeObserver : self . matrixSessionStateObserver ] ;
2020-08-11 18:19:47 +00:00
}
} ] ;
}
2020-08-05 11:13:53 +00:00
}
2020-08-11 18:19:47 +00:00
else
2020-08-05 11:13:53 +00:00
{
2021-06-11 11:38:07 +00:00
[ self deconfigurePushKit ] ;
2020-08-05 11:13:53 +00:00
}
}
2020-08-11 18:19:47 +00:00
- ( void ) configurePushKit
{
2021-06-11 11:38:07 +00:00
MXLogDebug ( @ "[PushNotificationService] configurePushKit" )
2020-08-11 18:19:47 +00:00
_pushRegistry . delegate = self ;
_pushRegistry . desiredPushTypes = [ NSSet setWithObject : PKPushTypeVoIP ] ;
}
2021-06-11 11:38:07 +00:00
- ( void ) deconfigurePushKit
{
MXLogDebug ( @ "[PushNotificationService] deconfigurePushKit" )
_pushRegistry . delegate = nil ;
}
2020-09-02 15:00:05 +00:00
- ( void ) removePusher : ( MXPusher * ) pusher inSession : ( MXSession * ) session
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] removePusher: %@" , pusher . appId ) ;
2020-09-02 15:00:05 +00:00
// Shortcut MatrixKit and its complex logic and call directly the API
[ session . matrixRestClient setPusherWithPushkey : pusher . pushkey
kind : [ NSNull null ] // This is how we remove a pusher
appId : pusher . appId
appDisplayName : pusher . appDisplayName
deviceDisplayName : pusher . deviceDisplayName
profileTag : pusher . profileTag
lang : pusher . lang
data : pusher . data . JSONDictionary
append : NO
success : ^ {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] removePusher: Success" ) ;
2020-09-02 15:00:05 +00:00
// Brute clean remaining MatrixKit data
[ [ NSUserDefaults standardUserDefaults ] removeObjectForKey : @ "pushDeviceToken" ] ;
[ [ NSUserDefaults standardUserDefaults ] removeObjectForKey : @ "pushOptions" ] ;
} failure : ^ ( NSError * error ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] removePusher: Error: %@" , error ) ;
2020-09-02 15:00:05 +00:00
} ] ;
}
2020-08-10 15:19:08 +00:00
- ( void ) launchBackgroundSync
{
// Launch a background sync for all existing matrix sessions
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
for ( MXKAccount * account in mxAccounts )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService] launchBackgroundSync" ) ;
2021-05-10 12:51:26 +00:00
[ account backgroundSync : 20000 success : ^ {
[ [ UNUserNotificationCenter currentNotificationCenter ] removeUnwantedNotifications ] ;
[ [ UNUserNotificationCenter currentNotificationCenter ] removeCallNotificationsFor : nil ] ;
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService] launchBackgroundSync: the background sync succeeds" ) ;
2021-05-10 12:51:26 +00:00
} failure : ^ ( NSError * error ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService] launchBackgroundSync: the background sync failed. Error: %@ (%@)." , error . domain , @ ( error . code ) ) ;
2021-05-10 12:51:26 +00:00
} ] ;
2020-08-10 15:19:08 +00:00
}
}
2020-04-14 12:54:36 +00:00
# pragma mark - UNUserNotificationCenterDelegate
2021-04-14 13:30:21 +00:00
- ( void ) userNotificationCenter : ( UNUserNotificationCenter * ) center willPresentNotification : ( UNNotification * ) notification withCompletionHandler : ( void ( ^ ) ( UNNotificationPresentationOptions ) ) completionHandler
{
2021-04-20 12:51:46 +00:00
NSDictionary * userInfo = notification . request . content . userInfo ;
if ( userInfo [ Constants . userInfoKeyPresentNotificationOnForeground ] )
2021-04-14 13:30:21 +00:00
{
2021-04-20 12:51:46 +00:00
if ( ! userInfo [ Constants . userInfoKeyPresentNotificationInRoom ]
&& [ [ AppDelegate theDelegate ] . visibleRoomId isEqualToString : userInfo [ @ "room_id" ] ] )
{
// do not show the notification when we ' re in the notified room
completionHandler ( UNNotificationPresentationOptionNone ) ;
}
else
{
completionHandler ( UNNotificationPresentationOptionBadge
| UNNotificationPresentationOptionSound
| UNNotificationPresentationOptionAlert ) ;
}
2021-04-14 13:30:21 +00:00
}
else
{
completionHandler ( UNNotificationPresentationOptionNone ) ;
}
}
2020-04-14 12:54:36 +00:00
// iOS 10 + , see application : handleActionWithIdentifier : forLocalNotification : withResponseInfo : completionHandler :
- ( void ) userNotificationCenter : ( UNUserNotificationCenter * ) center didReceiveNotificationResponse : ( UNNotificationResponse * ) response withCompletionHandler : ( void ( ^ ) ( void ) ) completionHandler
{
UNNotification * notification = response . notification ;
UNNotificationContent * content = notification . request . content ;
NSString * actionIdentifier = [ response actionIdentifier ] ;
NSString * roomId = content . userInfo [ @ "room_id" ] ;
if ( [ actionIdentifier isEqualToString : @ "inline-reply" ] )
{
if ( [ response isKindOfClass : [ UNTextInputNotificationResponse class ] ] )
{
UNTextInputNotificationResponse * textInputNotificationResponse = ( UNTextInputNotificationResponse * ) response ;
NSString * responseText = [ textInputNotificationResponse userText ] ;
[ self handleNotificationInlineReplyForRoomId : roomId withResponseText : responseText success : ^ ( NSString * eventId ) {
completionHandler ( ) ;
} failure : ^ ( NSError * error ) {
UNMutableNotificationContent * failureNotificationContent = [ [ UNMutableNotificationContent alloc ] init ] ;
failureNotificationContent . userInfo = content . userInfo ;
failureNotificationContent . body = NSLocalizedStringFromTable ( @ "room_event_failed_to_send" , @ "Vector" , nil ) ;
failureNotificationContent . threadIdentifier = roomId ;
NSString * uuid = [ [ NSUUID UUID ] UUIDString ] ;
UNNotificationRequest * failureNotificationRequest = [ UNNotificationRequest requestWithIdentifier : uuid
content : failureNotificationContent
trigger : nil ] ;
[ center addNotificationRequest : failureNotificationRequest withCompletionHandler : nil ] ;
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] didReceiveNotificationResponse: error sending text message: %@" , error ) ;
2020-04-14 12:54:36 +00:00
completionHandler ( ) ;
} ] ;
}
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] didReceiveNotificationResponse: error, expect a response of type UNTextInputNotificationResponse" ) ;
2020-04-14 12:54:36 +00:00
completionHandler ( ) ;
}
}
else if ( [ actionIdentifier isEqualToString : UNNotificationDefaultActionIdentifier ] )
{
2020-06-15 10:49:22 +00:00
[ self notifyNavigateToRoomById : roomId ] ;
2020-04-14 12:54:36 +00:00
completionHandler ( ) ;
}
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] didReceiveNotificationResponse: unhandled identifier %@" , actionIdentifier ) ;
2020-04-14 12:54:36 +00:00
completionHandler ( ) ;
}
}
# pragma mark - Other Methods
- ( void ) handleNotificationInlineReplyForRoomId : ( NSString * ) roomId
withResponseText : ( NSString * ) responseText
success : ( void ( ^ ) ( NSString * eventId ) ) success
failure : ( void ( ^ ) ( NSError * error ) ) failure
{
if ( ! roomId . length )
{
failure ( nil ) ;
return ;
}
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
2020-06-03 11:20:06 +00:00
__block MXKRoomDataSourceManager * manager ;
dispatch_group _t group = dispatch_group _create ( ) ;
2020-04-14 12:54:36 +00:00
for ( MXKAccount * account in mxAccounts )
{
2020-06-03 11:20:06 +00:00
void ( ^ storeDataReadyBlock ) ( void ) = ^ {
MXRoom * room = [ account . mxSession roomWithRoomId : roomId ] ;
if ( room )
{
manager = [ MXKRoomDataSourceManager sharedManagerForMatrixSession : account . mxSession ] ;
}
} ;
if ( account . mxSession . state >= MXSessionStateStoreDataReady )
2020-04-14 12:54:36 +00:00
{
2020-06-03 11:20:06 +00:00
storeDataReadyBlock ( ) ;
2020-04-14 12:54:36 +00:00
if ( manager )
{
break ;
}
}
2020-06-03 11:20:06 +00:00
else
{
dispatch_group _enter ( group ) ;
// wait for session state to be store data ready
id sessionStateObserver = nil ;
sessionStateObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXSessionStateDidChangeNotification object : account . mxSession queue : nil usingBlock : ^ ( NSNotification * _Nonnull note ) {
if ( manager )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : sessionStateObserver ] ;
return ;
}
if ( account . mxSession . state >= MXSessionStateStoreDataReady )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : sessionStateObserver ] ;
storeDataReadyBlock ( ) ;
dispatch_group _leave ( group ) ;
}
} ] ;
}
2020-04-14 12:54:36 +00:00
}
2020-06-03 11:20:06 +00:00
dispatch_group _notify ( group , dispatch_get _main _queue ( ) , ^ {
if ( manager = = nil )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] didReceiveNotificationResponse: room with id %@ not found" , roomId ) ;
2020-06-03 11:20:06 +00:00
failure ( nil ) ;
}
else
{
[ manager roomDataSourceForRoom : roomId create : YES onComplete : ^ ( MXKRoomDataSource * roomDataSource ) {
if ( responseText ! = nil && responseText . length ! = 0 )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] didReceiveNotificationResponse: sending message to room: %@" , roomId ) ;
2020-06-03 11:20:06 +00:00
[ roomDataSource sendTextMessage : responseText success : ^ ( NSString * eventId ) {
success ( eventId ) ;
} failure : ^ ( NSError * error ) {
failure ( error ) ;
} ] ;
}
else
{
failure ( nil ) ;
}
} ] ;
}
} ) ;
2020-04-14 12:54:36 +00:00
}
- ( void ) clearPushNotificationToken
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] clearPushNotificationToken: Clear existing token" ) ;
2020-08-31 15:51:20 +00:00
2020-09-03 06:20:26 +00:00
// Clear existing pushkit token registered on the HS
MXKAccountManager * accountManager = [ MXKAccountManager sharedManager ] ;
[ accountManager setPushDeviceToken : nil withPushOptions : nil ] ;
2020-04-14 12:54:36 +00:00
}
// Remove delivred notifications for a given room id except call notifications
- ( void ) removeDeliveredNotificationsWithRoomId : ( NSString * ) roomId completion : ( dispatch_block _t ) completion
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService][Push] removeDeliveredNotificationsWithRoomId: Remove potential delivered notifications for room id: %@" , roomId ) ;
2020-04-14 12:54:36 +00:00
NSMutableArray < NSString * > * notificationRequestIdentifiersToRemove = [ NSMutableArray new ] ;
UNUserNotificationCenter * notificationCenter = [ UNUserNotificationCenter currentNotificationCenter ] ;
[ notificationCenter getDeliveredNotificationsWithCompletionHandler : ^ ( NSArray < UNNotification * > * _Nonnull notifications ) {
for ( UNNotification * notification in notifications )
{
NSString * threadIdentifier = notification . request . content . threadIdentifier ;
if ( [ threadIdentifier isEqualToString : roomId ] )
{
[ notificationRequestIdentifiersToRemove addObject : notification . request . identifier ] ;
}
}
[ notificationCenter removeDeliveredNotificationsWithIdentifiers : notificationRequestIdentifiersToRemove ] ;
if ( completion )
{
completion ( ) ;
}
} ] ;
}
# pragma mark - Delegate Notifiers
2020-06-15 10:49:22 +00:00
- ( void ) notifyNavigateToRoomById : ( NSString * ) roomId
2020-04-14 12:54:36 +00:00
{
2020-06-15 10:49:22 +00:00
if ( [ _delegate respondsToSelector : @ selector ( pushNotificationService : shouldNavigateToRoomWithId : ) ] )
2020-04-14 12:54:36 +00:00
{
2020-06-15 10:49:22 +00:00
[ _delegate pushNotificationService : self shouldNavigateToRoomWithId : roomId ] ;
2020-04-14 12:54:36 +00:00
}
}
2020-08-05 11:13:53 +00:00
# pragma mark - PKPushRegistryDelegate
- ( void ) pushRegistry : ( PKPushRegistry * ) registry didUpdatePushCredentials : ( PKPushCredentials * ) pushCredentials forType : ( PKPushType ) type
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService] did update PushKit credentials" ) ;
2020-08-20 14:33:03 +00:00
_pushNotificationStore . pushKitToken = pushCredentials . token ;
2020-08-11 18:19:47 +00:00
if ( [ UIApplication sharedApplication ] . applicationState = = UIApplicationStateActive )
{
self . shouldReceiveVoIPPushes = NO ;
}
2020-08-05 11:13:53 +00:00
}
- ( void ) pushRegistry : ( PKPushRegistry * ) registry didReceiveIncomingPushWithPayload : ( PKPushPayload * ) payload forType : ( PKPushType ) type withCompletionHandler : ( void ( ^ ) ( void ) ) completion
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[PushNotificationService] didReceiveIncomingPushWithPayload: %@" , payload . dictionaryPayload ) ;
2020-08-11 18:19:47 +00:00
NSString * roomId = payload . dictionaryPayload [ @ "room_id" ] ;
NSString * eventId = payload . dictionaryPayload [ @ "event_id" ] ;
2020-08-10 15:19:08 +00:00
[ [ UNUserNotificationCenter currentNotificationCenter ] removeUnwantedNotifications ] ;
2020-08-11 18:19:47 +00:00
[ [ UNUserNotificationCenter currentNotificationCenter ] removeCallNotificationsFor : roomId ] ;
2020-08-05 11:13:53 +00:00
2021-06-10 08:43:08 +00:00
if ( @ available ( iOS 13.0 , * ) )
{
// for iOS 13 , we ' ll just report the incoming call in the same runloop . It means we cannot call an async API here .
MXEvent * callInvite = [ _pushNotificationStore callInviteForEventId : eventId ] ;
// remove event
[ _pushNotificationStore removeCallInviteWithEventId : eventId ] ;
MXSession * session = [ AppDelegate theDelegate ] . mxSessions . firstObject ;
// when we have a VoIP push while the application is killed , session . callManager will not be ready yet . Configure it .
[ [ AppDelegate theDelegate ] configureCallManagerIfRequiredForSession : session ] ;
2020-08-11 18:19:47 +00:00
2021-06-11 11:37:53 +00:00
MXLogDebug ( @ "[PushNotificationService] didReceiveIncomingPushWithPayload: iOS 13+, callInvite: %@" , callInvite ) ;
2021-06-10 08:43:08 +00:00
if ( callInvite )
2020-08-11 18:19:47 +00:00
{
2021-06-10 08:43:08 +00:00
// We ' re using this dispatch_group to continue event stream after cache fully processed .
dispatch_group _t dispatchGroup = dispatch_group _create ( ) ;
2020-08-11 18:19:47 +00:00
2021-06-10 08:43:08 +00:00
dispatch_group _enter ( dispatchGroup ) ;
2021-09-28 12:45:34 +00:00
session . updateSpaces = NO ;
2021-06-10 08:43:08 +00:00
// Not continuing in completion block here , because PushKit mandates reporting a new call in the same run loop .
// ' handleBackgroundSyncCacheIfRequiredWithCompletion ' is processing to - device events synchronously .
2021-09-28 12:45:34 +00:00
[ session handleBackgroundSyncCacheIfRequiredWithCompletion : ^ {
session . updateSpaces = YES ;
2021-06-10 08:43:08 +00:00
dispatch_group _leave ( dispatchGroup ) ;
} ] ;
2020-08-11 18:19:47 +00:00
2021-06-10 08:43:08 +00:00
if ( callInvite . eventType = = MXEventTypeCallInvite )
2020-08-11 18:19:47 +00:00
{
2021-06-10 08:43:08 +00:00
// process the call invite synchronously
[ session . callManager handleCallEvent : callInvite ] ;
MXCallInviteEventContent * content = [ MXCallInviteEventContent modelFromJSON : callInvite . content ] ;
MXCall * call = [ session . callManager callWithCallId : content . callId ] ;
if ( call )
2021-03-19 00:00:14 +00:00
{
2021-06-10 08:43:08 +00:00
[ session . callManager . callKitAdapter reportIncomingCall : call ] ;
MXLogDebug ( @ "[PushNotificationService] didReceiveIncomingPushWithPayload: Reporting new call in room %@ for the event: %@" , roomId , eventId ) ;
// Wait for the sync response in cache to be processed for data integrity .
dispatch_group _notify ( dispatchGroup , dispatch_get _main _queue ( ) , ^ {
// After reporting the call , we can continue async . Launch a background sync to handle call answers / declines on other devices of the user .
[ self launchBackgroundSync ] ;
} ) ;
2020-08-11 18:19:47 +00:00
}
else
{
2021-06-10 08:43:08 +00:00
MXLogDebug ( @ "[PushNotificationService] didReceiveIncomingPushWithPayload: Error on call object on room %@ for the event: %@" , roomId , eventId ) ;
2020-08-11 18:19:47 +00:00
}
}
2021-06-10 08:43:08 +00:00
else if ( [ callInvite . type isEqualToString : kWidgetMatrixEventTypeString ] ||
[ callInvite . type isEqualToString : kWidgetModularEventTypeString ] )
{
[ [ AppDelegate theDelegate ] . callPresenter processWidgetEvent : callInvite
inSession : session ] ;
}
2020-08-11 18:19:47 +00:00
else
{
// It ' s a serious error . There is nothing to avoid iOS to kill us here .
2021-06-10 08:43:08 +00:00
MXLogDebug ( @ "[PushNotificationService] didReceiveIncomingPushWithPayload: We have an unknown type of event for %@. There is something wrong." , eventId ) ;
2020-08-11 18:19:47 +00:00
}
}
else
{
2021-06-10 08:43:08 +00:00
// It ' s a serious error . There is nothing to avoid iOS to kill us here .
2021-06-11 11:37:53 +00:00
MXLogDebug ( @ "[PushNotificationService] didReceiveIncomingPushWithPayload: iOS 13+, but we don't have the callInvite event for the eventId: %@." , eventId ) ;
2020-08-11 18:19:47 +00:00
}
}
else
{
2021-06-11 11:37:53 +00:00
if ( [ UIApplication sharedApplication ] . applicationState = = UIApplicationStateActive )
{
// below iOS 13 , we don ' t have to report a call immediately .
// We can wait for a call invite from event stream and process .
MXLogDebug ( @ "[PushNotificationService] didReceiveIncomingPushWithPayload: Below iOS 13 and active app. Do nothing." ) ;
completion ( ) ;
return ;
}
2021-06-10 08:43:08 +00:00
// below iOS 13 , we can call an async API . After background sync , we ' ll hopefully fetch the call invite and report a new call to the CallKit .
[ self launchBackgroundSync ] ;
2020-08-10 15:19:08 +00:00
}
2020-08-11 18:19:47 +00:00
completion ( ) ;
2020-08-05 11:13:53 +00:00
}
2020-04-14 12:54:36 +00:00
@ end