2014-10-02 15:02:47 +00:00
/ *
Copyright 2014 OpenMarket Ltd
2017-03-08 15:14:41 +00:00
Copyright 2017 Vector Creations Ltd
2018-03-30 10:04:50 +00:00
Copyright 2018 New Vector Ltd
2014-10-02 15:02:47 +00:00
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 "AppDelegate.h"
2015-08-21 18:00:39 +00:00
2017-08-22 13:02:16 +00:00
# import < Intents / Intents . h >
2017-08-21 10:28:05 +00:00
# import < PushKit / PushKit . h >
2019-02-05 00:36:13 +00:00
# import < Contacts / Contacts . h >
2017-08-22 13:02:16 +00:00
2015-11-20 17:14:15 +00:00
# import "RecentsDataSource.h"
2015-08-21 18:00:39 +00:00
# import "RoomDataSource.h"
2015-11-17 23:15:52 +00:00
2015-08-21 18:00:39 +00:00
# import "EventFormatter.h"
2015-11-17 23:15:52 +00:00
# import "RoomViewController.h"
2016-06-21 19:47:20 +00:00
# import "DirectoryViewController.h"
# import "SettingsViewController.h"
# import "ContactDetailsViewController.h"
2017-06-29 15:33:08 +00:00
# import "BugReportViewController.h"
2017-11-14 17:21:01 +00:00
# import "RoomKeyRequestViewController.h"
2017-06-29 15:33:08 +00:00
2017-11-23 15:09:19 +00:00
# import < MatrixKit / MatrixKit . h >
2015-08-07 08:42:47 +00:00
2016-04-29 14:19:13 +00:00
# import "Tools.h"
2017-08-04 11:05:07 +00:00
# import "WidgetManager.h"
2016-04-29 14:19:13 +00:00
2015-02-12 10:16:28 +00:00
# import "AFNetworkReachabilityManager.h"
2015-04-30 14:19:12 +00:00
# import < AudioToolbox / AudioToolbox . h >
2017-06-21 10:02:49 +00:00
# include < MatrixSDK / MXUIKitBackgroundModeHandler . h >
2018-05-23 15:01:18 +00:00
# import "WebViewViewController.h"
2017-06-21 10:02:49 +00:00
// Calls
2016-08-19 15:43:29 +00:00
# import "CallViewController.h"
2017-08-22 07:48:59 +00:00
# import "MXSession+Riot.h"
2017-10-12 09:42:10 +00:00
# import "MXRoom+Riot.h"
2017-08-22 07:48:59 +00:00
2018-07-02 12:41:55 +00:00
# import "Riot-Swift.h"
2015-11-16 09:30:57 +00:00
// # define MX_CALL _STACK _OPENWEBRTC
2015-08-07 14:20:46 +00:00
# ifdef MX_CALL _STACK _OPENWEBRTC
2015-07-21 09:30:37 +00:00
# import < MatrixOpenWebRTCWrapper / MatrixOpenWebRTCWrapper . h >
2015-08-07 14:20:46 +00:00
# endif
# ifdef MX_CALL _STACK _ENDPOINT
# import < MatrixEndpointWrapper / MatrixEndpointWrapper . h >
# endif
2015-07-21 09:30:37 +00:00
2017-11-24 15:28:48 +00:00
# if __has _include ( < MatrixSDK / MXJingleCallStack . h > )
# define CALL_STACK _JINGLE
# endif
# ifdef CALL_STACK _JINGLE
2017-06-20 20:16:54 +00:00
# import < MatrixSDK / MXJingleCallStack . h >
2019-01-30 16:36:00 +00:00
# import < UserNotifications / UserNotifications . h >
2017-06-21 10:02:49 +00:00
# endif
2017-06-14 15:37:22 +00:00
2016-09-02 20:21:02 +00:00
# define CALL_STATUS _BAR _HEIGHT 44
2015-02-04 23:16:11 +00:00
# define MAKE_STRING ( x ) # x
# define MAKE_NS _STRING ( x ) @ MAKE_STRING ( x )
2015-02-04 17:18:38 +00:00
2016-05-19 15:22:29 +00:00
NSString * const kAppDelegateDidTapStatusBarNotification = @ "kAppDelegateDidTapStatusBarNotification" ;
2016-08-11 08:38:10 +00:00
NSString * const kAppDelegateNetworkStatusDidChangeNotification = @ "kAppDelegateNetworkStatusDidChangeNotification" ;
2016-05-19 15:22:29 +00:00
2019-09-10 17:34:40 +00:00
NSString * const AppDelegateDidValidateEmailNotification = @ "AppDelegateDidValidateEmailNotification" ;
NSString * const AppDelegateDidValidateEmailNotificationSIDKey = @ "AppDelegateDidValidateEmailNotificationSIDKey" ;
NSString * const AppDelegateDidValidateEmailNotificationClientSecretKey = @ "AppDelegateDidValidateEmailNotificationClientSecretKey" ;
2019-08-30 09:15:06 +00:00
@ interface AppDelegate ( ) < PKPushRegistryDelegate , GDPRConsentViewControllerDelegate , DeviceVerificationCoordinatorBridgePresenterDelegate , ServiceTermsModalCoordinatorBridgePresenterDelegate >
2015-06-18 09:19:42 +00:00
{
2015-05-11 21:11:23 +00:00
/ * *
Reachability observer
* /
2015-02-18 16:40:55 +00:00
id reachabilityObserver ;
2015-04-17 13:45:32 +00:00
2015-08-04 16:37:48 +00:00
/ * *
MatrixKit error observer
* /
id matrixKitErrorObserver ;
2015-05-11 21:11:23 +00:00
/ * *
matrix session observer used to detect new opened sessions .
* /
2015-04-17 13:45:32 +00:00
id matrixSessionStateObserver ;
2015-04-30 14:19:12 +00:00
2015-05-11 21:11:23 +00:00
/ * *
2015-06-18 13:20:33 +00:00
matrix account observers .
2015-05-11 21:11:23 +00:00
* /
2015-06-18 13:20:33 +00:00
id addedAccountObserver ;
id removedAccountObserver ;
2015-05-11 21:11:23 +00:00
/ * *
matrix call observer used to handle incoming / outgoing call .
* /
id matrixCallObserver ;
/ * *
The current call view controller ( if any ) .
* /
2016-08-19 15:43:29 +00:00
CallViewController * currentCallViewController ;
2017-11-14 17:21:01 +00:00
/ * *
Incoming room key requests observers
* /
id roomKeyRequestObserver ;
id roomKeyRequestCancellationObserver ;
/ * *
If any the currently displayed sharing key dialog
* /
RoomKeyRequestViewController * roomKeyRequestViewController ;
2019-04-12 21:19:07 +00:00
/ * *
Incoming device verification requests observers
* /
id incomingDeviceVerificationObserver ;
/ * *
If any the currently displayed device verification dialog
* /
DeviceVerificationCoordinatorBridgePresenter * deviceVerificationCoordinatorBridgePresenter ;
2015-05-28 16:31:08 +00:00
/ * *
Account picker used in case of multiple account .
* /
2017-07-14 14:41:25 +00:00
UIAlertController * accountPicker ;
2015-11-17 23:15:52 +00:00
/ * *
Array of ` MXSession` instances .
* /
NSMutableArray * mxSessionArray ;
2016-04-06 09:28:12 +00:00
2016-04-12 15:44:06 +00:00
/ * *
The fragment of the universal link being processing .
Only one fragment is handled at a time .
* /
NSString * universalLinkFragmentPending ;
2017-02-13 16:58:29 +00:00
/ * *
The potential room alias related to the fragment of the universal link being processing .
Only one alias is handled at a time , the key is the room id and the value is the alias .
* /
NSDictionary * universalLinkFragmentPendingRoomAlias ;
2017-07-14 14:41:25 +00:00
2016-04-12 15:44:06 +00:00
/ * *
An universal link may need to wait for an account to be logged in or for a
session to be running . Hence , this observer .
* /
id universalLinkWaitingObserver ;
2017-07-14 14:41:25 +00:00
2016-06-16 14:50:41 +00:00
/ * *
Suspend the error notifications when the navigation stack of the root view controller is updating .
* /
BOOL isErrorNotificationSuspended ;
2017-07-14 14:41:25 +00:00
2016-04-13 15:08:02 +00:00
/ * *
Completion block called when [ self popToHomeViewControllerAnimated : ] has been
completed .
* /
2019-01-05 02:28:03 +00:00
void ( ^ popToHomeViewControllerCompletion ) ( void ) ;
2017-07-14 14:41:25 +00:00
2016-05-20 12:31:23 +00:00
/ * *
The listeners to call events .
There is one listener per MXSession .
2016-05-20 13:45:29 +00:00
The key is an identifier of the MXSession . The value , the listener .
2016-05-20 12:31:23 +00:00
* /
NSMutableDictionary * callEventsListeners ;
2017-07-14 14:41:25 +00:00
2017-10-18 17:18:14 +00:00
/ * *
The notification listener blocks .
There is one block per MXSession .
The key is an identifier of the MXSession . The value , the listener block .
* /
NSMutableDictionary < NSNumber * , MXOnNotification > * notificationListenerBlocks ;
/ * *
The list of the events which need to be notified at the end of the background sync .
There is one list per MXSession .
The key is an identifier of the MXSession . The value , an array of dictionaries ( eventId , roomId . . . for each event ) .
* /
NSMutableDictionary < NSNumber * , NSMutableArray < NSDictionary * > * > * eventsToNotify ;
2017-12-29 12:04:57 +00:00
/ * *
Cache for payloads received with incoming push notifications .
The key is the event id . The value , the payload .
* /
NSMutableDictionary < NSString * , NSDictionary * > * incomingPushPayloads ;
2016-05-20 12:31:23 +00:00
/ * *
Currently displayed "Call not supported" alert .
* /
2017-07-14 14:41:25 +00:00
UIAlertController * noCallSupportAlert ;
2016-12-16 12:43:08 +00:00
/ * *
Prompt to ask the user to log in again .
* /
2017-07-14 14:41:25 +00:00
UIAlertController * cryptoDataCorruptedAlert ;
2019-02-15 16:29:48 +00:00
/ * *
Prompt to warn the user about a new backup on the homeserver .
* /
UIAlertController * wrongBackupVersionAlert ;
2017-01-31 23:18:01 +00:00
/ * *
The launch animation container view
* /
UIView * launchAnimationContainerView ;
NSDate * launchAnimationStart ;
2015-02-18 16:40:55 +00:00
}
2014-10-02 15:02:47 +00:00
2017-07-14 14:41:25 +00:00
@ property ( strong , nonatomic ) UIAlertController * mxInAppNotification ;
2015-04-30 14:19:12 +00:00
2018-04-17 19:39:31 +00:00
@ property ( strong , nonatomic ) UIAlertController * logoutConfirmation ;
2018-05-23 15:01:18 +00:00
@ property ( weak , nonatomic ) UIAlertController * gdprConsentNotGivenAlertController ;
2018-08-08 09:30:50 +00:00
@ property ( weak , nonatomic ) UIViewController * gdprConsentController ;
2018-05-23 15:01:18 +00:00
2019-08-30 09:15:06 +00:00
@ property ( nonatomic , strong ) ServiceTermsModalCoordinatorBridgePresenter * serviceTermsModalCoordinatorBridgePresenter ;
2019-11-28 16:38:19 +00:00
@ property ( nonatomic , strong ) SlidingModalPresenter * slidingModalPresenter ;
2019-08-30 09:15:06 +00:00
2018-08-08 16:36:24 +00:00
/ * *
Used to manage on boarding steps , like create DM with riot bot
* /
@ property ( strong , nonatomic ) OnBoardingManager * onBoardingManager ;
2017-08-23 16:07:14 +00:00
@ property ( nonatomic , nullable , copy ) void ( ^ registrationForRemoteNotificationsCompletion ) ( NSError * ) ;
2015-04-30 14:19:12 +00:00
2017-09-04 12:06:48 +00:00
2017-08-21 10:28:05 +00:00
@ property ( nonatomic , strong ) PKPushRegistry * pushRegistry ;
2017-10-23 14:58:55 +00:00
@ property ( nonatomic ) NSMutableDictionary < NSNumber * , NSMutableArray < NSString * > * > * incomingPushEventIds ;
2017-08-21 10:28:05 +00:00
2014-10-02 15:02:47 +00:00
@ end
@ implementation AppDelegate
2014-10-08 12:14:12 +00:00
# pragma mark -
2018-01-02 10:56:30 +00:00
+ ( void ) initialize
{
NSLog ( @ "[AppDelegate] initialize" ) ;
// Set the App Group identifier .
MXSDKOptions * sdkOptions = [ MXSDKOptions sharedInstance ] ;
sdkOptions . applicationGroupIdentifier = @ "group.im.vector" ;
// Redirect NSLogs to files only if we are not debugging
if ( ! isatty ( STDERR_FILENO ) )
{
[ MXLogger redirectNSLogToFiles : YES ] ;
}
NSLog ( @ "[AppDelegate] initialize: Done" ) ;
}
2015-06-18 09:19:42 +00:00
+ ( AppDelegate * ) theDelegate
{
2014-10-08 12:14:12 +00:00
return ( AppDelegate * ) [ [ UIApplication sharedApplication ] delegate ] ;
}
2015-02-04 17:18:38 +00:00
# pragma mark -
2015-06-18 09:19:42 +00:00
- ( NSString * ) appVersion
{
if ( ! _appVersion )
{
2015-02-04 17:18:38 +00:00
_appVersion = [ [ NSBundle mainBundle ] objectForInfoDictionaryKey : @ "CFBundleShortVersionString" ] ;
}
return _appVersion ;
}
2015-06-18 09:19:42 +00:00
- ( NSString * ) build
{
if ( ! _build )
{
2015-02-04 17:18:38 +00:00
NSString * buildBranch = nil ;
NSString * buildNumber = nil ;
// Check whether GIT_BRANCH and BUILD_NUMBER were provided during compilation in command line argument .
2015-02-04 23:16:11 +00:00
# ifdef GIT_BRANCH
buildBranch = MAKE_NS _STRING ( GIT_BRANCH ) ;
2015-02-04 17:18:38 +00:00
# endif
2015-02-04 23:16:11 +00:00
# ifdef BUILD_NUMBER
2019-01-24 16:45:43 +00:00
buildNumber = [ NSString stringWithFormat : @ "#%@" , @ ( BUILD_NUMBER ) ] ;
2015-02-04 17:18:38 +00:00
# endif
2015-06-18 09:19:42 +00:00
if ( buildBranch && buildNumber )
{
2015-02-04 17:18:38 +00:00
_build = [ NSString stringWithFormat : @ "%@ %@" , buildBranch , buildNumber ] ;
} else if ( buildNumber ) {
_build = buildNumber ;
2015-06-18 09:19:42 +00:00
} else
{
2015-08-12 07:32:45 +00:00
_build = buildBranch ? buildBranch : NSLocalizedStringFromTable ( @ "settings_config_no_build_info" , @ "Vector" , nil ) ;
2015-02-04 17:18:38 +00:00
}
}
return _build ;
}
2016-02-11 15:09:35 +00:00
- ( void ) setIsOffline : ( BOOL ) isOffline
{
if ( ! reachabilityObserver )
{
// Define reachability observer when isOffline property is set for the first time
reachabilityObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : AFNetworkingReachabilityDidChangeNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * note ) {
NSNumber * statusItem = note . userInfo [ AFNetworkingReachabilityNotificationStatusItem ] ;
if ( statusItem )
{
AFNetworkReachabilityStatus reachabilityStatus = statusItem . integerValue ;
if ( reachabilityStatus = = AFNetworkReachabilityStatusNotReachable )
{
[ AppDelegate theDelegate ] . isOffline = YES ;
}
else
{
[ AppDelegate theDelegate ] . isOffline = NO ;
}
}
} ] ;
}
2016-08-11 08:38:10 +00:00
if ( _isOffline ! = isOffline )
{
_isOffline = isOffline ;
[ [ NSNotificationCenter defaultCenter ] postNotificationName : kAppDelegateNetworkStatusDidChangeNotification object : nil ] ;
}
2016-02-11 15:09:35 +00:00
}
2017-03-14 16:25:46 +00:00
- ( UINavigationController * ) secondaryNavigationController
{
UIViewController * rootViewController = self . window . rootViewController ;
if ( [ rootViewController isKindOfClass : [ UISplitViewController class ] ] )
{
UISplitViewController * splitViewController = ( UISplitViewController * ) rootViewController ;
if ( splitViewController . viewControllers . count = = 2 )
{
UIViewController * secondViewController = [ splitViewController . viewControllers lastObject ] ;
if ( [ secondViewController isKindOfClass : [ UINavigationController class ] ] )
{
return ( UINavigationController * ) secondViewController ;
}
}
}
return nil ;
}
2015-02-12 10:16:28 +00:00
# pragma mark - UIApplicationDelegate
2014-10-02 15:02:47 +00:00
2017-11-15 14:49:54 +00:00
- ( BOOL ) application : ( UIApplication * ) application willFinishLaunchingWithOptions : ( nullable NSDictionary * ) launchOptions
{
2018-04-08 14:00:33 +00:00
// Create message sound
NSURL * messageSoundURL = [ [ NSBundle mainBundle ] URLForResource : @ "message" withExtension : @ "mp3" ] ;
AudioServicesCreateSystemSoundID ( ( __bridge CFURLRef ) messageSoundURL , & _messageSound ) ;
2017-12-19 13:44:10 +00:00
NSLog ( @ "[AppDelegate] willFinishLaunchingWithOptions: Done" ) ;
2017-11-15 14:49:54 +00:00
return YES ;
}
2015-06-18 09:19:42 +00:00
- ( BOOL ) application : ( UIApplication * ) application didFinishLaunchingWithOptions : ( NSDictionary * ) launchOptions
{
2017-12-28 13:23:12 +00:00
NSDate * startDate = [ NSDate date ] ;
2016-03-23 13:45:16 +00:00
# ifdef DEBUG
// log the full launchOptions only in DEBUG
2016-02-03 11:21:45 +00:00
NSLog ( @ "[AppDelegate] didFinishLaunchingWithOptions: %@" , launchOptions ) ;
2016-03-23 13:45:16 +00:00
# else
NSLog ( @ "[AppDelegate] didFinishLaunchingWithOptions" ) ;
# endif
2017-12-19 13:44:10 +00:00
2017-12-27 14:40:25 +00:00
// User credentials ( in MXKAccount ) are no more stored in NSUserDefaults but in a file
// as advised at https : // forums . developer . apple . com / thread / 15685 #45849 .
// So , there is no more need to loop ( sometimes forever ) until
// [ application isProtectedDataAvailable ] becomes YES .
2017-12-29 16:01:23 +00:00
// But , as we are not so sure , loop but no more than 10 s .
2017-12-31 09:21:22 +00:00
// // TODO : Remove this loop .
// NSUInteger loopCount = 0 ;
//
// // Check whether the content protection is active before going further .
// // Should fix the spontaneous logout .
// while ( ! [ application isProtectedDataAvailable ] && loopCount + + < 50 )
// {
// // Wait for protected data .
// [ [ NSRunLoop currentRunLoop ] runUntilDate : [ NSDate dateWithTimeIntervalSinceNow : 0.2 f ] ] ;
// }
//
// NSLog ( @ "[AppDelegate] didFinishLaunchingWithOptions: isProtectedDataAvailable: %@ (%tu)" , @ ( [ application isProtectedDataAvailable ] ) , loopCount ) ;
NSLog ( @ "[AppDelegate] didFinishLaunchingWithOptions: isProtectedDataAvailable: %@" , @ ( [ application isProtectedDataAvailable ] ) ) ;
2017-12-19 13:44:10 +00:00
2017-01-24 14:46:46 +00:00
// Log app information
2019-01-07 23:24:11 +00:00
NSString * appDisplayName = [ [ NSBundle mainBundle ] infoDictionary ] [ @ "CFBundleDisplayName" ] ;
2017-01-24 14:46:46 +00:00
NSString * appVersion = [ AppDelegate theDelegate ] . appVersion ;
NSString * build = [ AppDelegate theDelegate ] . build ;
2017-07-14 14:41:25 +00:00
2017-01-24 14:46:46 +00:00
NSLog ( @ "------------------------------" ) ;
NSLog ( @ "Application info:" ) ;
NSLog ( @ "%@ version: %@" , appDisplayName , appVersion ) ;
NSLog ( @ "MatrixKit version: %@" , MatrixKitVersion ) ;
NSLog ( @ "MatrixSDK version: %@" , MatrixSDKVersion ) ;
NSLog ( @ "Build: %@\n" , build ) ;
NSLog ( @ "------------------------------\n" ) ;
2018-08-22 13:36:03 +00:00
[ self setupUserDefaults ] ;
2017-07-21 09:08:33 +00:00
2019-01-14 09:53:43 +00:00
// Set up theme
ThemeService . shared . themeId = RiotSettings . shared . userInterfaceTheme ;
2017-12-27 13:00:16 +00:00
// Set up runtime language and fallback by considering the userDefaults object shared within the application group .
NSUserDefaults * sharedUserDefaults = [ MXKAppSettings standardAppSettings ] . sharedUserDefaults ;
NSString * language = [ sharedUserDefaults objectForKey : @ "appLanguage" ] ;
if ( ! language )
{
// Check whether a langage was only defined at the Riot application level .
language = [ [ NSUserDefaults standardUserDefaults ] objectForKey : @ "appLanguage" ] ;
if ( language )
{
// Move this setting into the shared userDefaults object to apply it to the extensions .
[ sharedUserDefaults setObject : language forKey : @ "appLanguage" ] ;
2018-10-15 21:49:17 +00:00
2017-12-27 13:00:16 +00:00
[ [ NSUserDefaults standardUserDefaults ] removeObjectForKey : @ "appLanguage" ] ;
}
}
[ NSBundle mxk_setLanguage : language ] ;
2017-07-21 09:08:33 +00:00
[ NSBundle mxk_setFallbackLanguage : @ "en" ] ;
2015-11-26 17:23:04 +00:00
2015-12-21 09:35:48 +00:00
// Customize the localized string table
[ NSBundle mxk_customizeLocalizedStringTableName : @ "Vector" ] ;
2015-11-17 23:15:52 +00:00
mxSessionArray = [ NSMutableArray array ] ;
2016-05-20 12:31:23 +00:00
callEventsListeners = [ NSMutableDictionary dictionary ] ;
2017-10-18 17:18:14 +00:00
notificationListenerBlocks = [ NSMutableDictionary dictionary ] ;
eventsToNotify = [ NSMutableDictionary dictionary ] ;
2017-12-29 12:04:57 +00:00
incomingPushPayloads = [ NSMutableDictionary dictionary ] ;
2015-11-17 23:15:52 +00:00
2017-03-28 16:17:14 +00:00
// To simplify navigation into the app , we retrieve here the main navigation controller and the tab bar controller .
UISplitViewController * splitViewController = ( UISplitViewController * ) self . window . rootViewController ;
splitViewController . delegate = self ;
2015-11-17 23:15:52 +00:00
2019-01-07 23:24:11 +00:00
_masterNavigationController = splitViewController . viewControllers [ 0 ] ;
2017-03-28 16:17:14 +00:00
_masterTabBarController = _masterNavigationController . viewControllers . firstObject ;
2015-11-17 23:15:52 +00:00
2017-08-14 20:35:16 +00:00
// Force the background color of the fake view controller displayed when there is no details .
UINavigationController * secondNavController = self . secondaryNavigationController ;
if ( secondNavController )
{
2019-02-15 14:21:33 +00:00
[ ThemeService . shared . theme applyStyleOnNavigationBar : secondNavController . navigationBar ] ;
2019-01-11 10:45:27 +00:00
secondNavController . topViewController . view . backgroundColor = ThemeService . shared . theme . backgroundColor ;
2017-08-14 20:35:16 +00:00
}
2017-03-28 16:17:14 +00:00
// on IOS 8 iPad devices , force to display the primary and the secondary viewcontroller
// to avoid empty room View Controller in portrait orientation
// else , the user cannot select a room
// shouldHideViewController delegate method is also implemented
2017-04-21 14:28:57 +00:00
if ( [ ( NSString * ) [ UIDevice currentDevice ] . model hasPrefix : @ "iPad" ] )
2015-11-17 23:15:52 +00:00
{
2017-03-28 16:17:14 +00:00
splitViewController . preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible ;
2014-10-02 15:02:47 +00:00
}
2015-11-17 23:15:52 +00:00
// Sanity check
2017-03-23 16:48:05 +00:00
NSAssert ( _masterTabBarController , @ "Something wrong in Main.storyboard" ) ;
2015-11-17 23:15:52 +00:00
_isAppForeground = NO ;
2018-06-27 07:55:06 +00:00
// Configure our analytics . It will indeed start if the option is enabled
2018-06-27 15:57:40 +00:00
[ MXSDKOptions sharedInstance ] . analyticsDelegate = [ Analytics sharedInstance ] ;
2018-06-29 05:35:31 +00:00
[ DecryptionFailureTracker sharedInstance ] . delegate = [ Analytics sharedInstance ] ;
2018-06-27 07:55:06 +00:00
[ [ Analytics sharedInstance ] start ] ;
2017-07-14 14:41:25 +00:00
2017-10-18 09:58:24 +00:00
// Prepare Pushkit handling
2017-10-23 14:58:55 +00:00
_incomingPushEventIds = [ NSMutableDictionary dictionary ] ;
2017-10-18 09:58:24 +00:00
2016-03-23 13:45:16 +00:00
// Add matrix observers , and initialize matrix sessions if the app is not launched in background .
2015-11-17 23:15:52 +00:00
[ self initMatrixSessions ] ;
2019-04-19 16:33:21 +00:00
// Setup Jitsi
NSString * jitsiServerStringURL = [ [ NSUserDefaults standardUserDefaults ] objectForKey : @ "jitsiServerURL" ] ;
NSURL * jitsiServerURL = [ NSURL URLWithString : jitsiServerStringURL ] ;
[ JitsiService . shared configureDefaultConferenceOptionsWith : jitsiServerURL ] ;
[ JitsiService . shared application : application didFinishLaunchingWithOptions : launchOptions ] ;
2017-12-28 13:23:12 +00:00
NSLog ( @ "[AppDelegate] didFinishLaunchingWithOptions: Done in %.0fms" , [ [ NSDate date ] timeIntervalSinceDate : startDate ] * 1000 ) ;
2014-10-02 15:02:47 +00:00
return YES ;
}
2015-06-18 09:19:42 +00:00
- ( void ) applicationWillResignActive : ( UIApplication * ) application
{
2016-02-03 11:21:45 +00:00
NSLog ( @ "[AppDelegate] applicationWillResignActive" ) ;
2014-10-02 15:02:47 +00:00
// Sent when the application is about to move from active to inactive state . This can occur for certain types of temporary interruptions ( such as an incoming phone call or SMS message ) or when the user quits the application and it begins the transition to the background state .
// Use this method to pause ongoing tasks , disable timers , and throttle down OpenGL ES frame rates . Games should use this method to pause the game .
2015-08-04 16:37:48 +00:00
// Release MatrixKit error observer
if ( matrixKitErrorObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : matrixKitErrorObserver ] ;
matrixKitErrorObserver = nil ;
}
2015-06-18 09:19:42 +00:00
if ( self . errorNotification )
{
2017-07-14 14:41:25 +00:00
[ self . errorNotification dismissViewControllerAnimated : NO completion : nil ] ;
2014-10-31 17:54:32 +00:00
self . errorNotification = nil ;
}
2015-05-28 16:31:08 +00:00
2015-06-18 09:19:42 +00:00
if ( accountPicker )
{
2017-07-14 14:41:25 +00:00
[ accountPicker dismissViewControllerAnimated : NO completion : nil ] ;
2015-05-28 16:31:08 +00:00
accountPicker = nil ;
}
2017-07-14 14:41:25 +00:00
2016-05-20 12:31:23 +00:00
if ( noCallSupportAlert )
{
2017-07-14 14:41:25 +00:00
[ noCallSupportAlert dismissViewControllerAnimated : NO completion : nil ] ;
2016-05-20 12:31:23 +00:00
noCallSupportAlert = nil ;
}
2017-07-14 14:41:25 +00:00
2016-12-16 12:43:08 +00:00
if ( cryptoDataCorruptedAlert )
{
2017-07-14 14:41:25 +00:00
[ cryptoDataCorruptedAlert dismissViewControllerAnimated : NO completion : nil ] ;
2016-12-16 12:43:08 +00:00
cryptoDataCorruptedAlert = nil ;
}
2019-02-15 16:29:48 +00:00
if ( wrongBackupVersionAlert )
{
[ wrongBackupVersionAlert dismissViewControllerAnimated : NO completion : nil ] ;
wrongBackupVersionAlert = nil ;
}
2014-12-09 17:21:39 +00:00
}
2015-06-18 09:19:42 +00:00
- ( void ) applicationDidEnterBackground : ( UIApplication * ) application
{
2016-02-03 11:21:45 +00:00
NSLog ( @ "[AppDelegate] applicationDidEnterBackground" ) ;
2014-12-09 17:21:39 +00:00
// Use this method to release shared resources , save user data , invalidate timers , and store enough application state information to restore your application to its current state in case it is terminated later .
// If your application supports background execution , this method is called instead of applicationWillTerminate : when the user quits .
2015-01-12 13:19:23 +00:00
2015-02-12 10:16:28 +00:00
// Stop reachability monitoring
2016-02-11 14:05:24 +00:00
if ( reachabilityObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : reachabilityObserver ] ;
reachabilityObserver = nil ;
}
2016-02-11 15:09:35 +00:00
[ [ AFNetworkReachabilityManager sharedManager ] setReachabilityStatusChangeBlock : nil ] ;
2015-02-12 10:16:28 +00:00
[ [ AFNetworkReachabilityManager sharedManager ] stopMonitoring ] ;
2015-04-30 14:19:12 +00:00
// check if some media must be released to reduce the cache size
2016-12-01 13:55:29 +00:00
[ MXMediaManager reduceCacheSizeToInsert : 0 ] ;
2015-04-30 14:19:12 +00:00
// Hide potential notification
2015-06-18 09:19:42 +00:00
if ( self . mxInAppNotification )
{
2017-07-14 14:41:25 +00:00
[ self . mxInAppNotification dismissViewControllerAnimated : NO completion : nil ] ;
2015-04-30 14:19:12 +00:00
self . mxInAppNotification = nil ;
}
2017-07-14 14:41:25 +00:00
2016-04-12 15:44:06 +00:00
// Discard any process on pending universal link
[ self resetPendingUniversalLink ] ;
2015-04-30 14:19:12 +00:00
// Suspend all running matrix sessions
2015-06-19 08:31:27 +00:00
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
2015-06-18 09:19:42 +00:00
for ( MXKAccount * account in mxAccounts )
{
2015-04-30 14:19:12 +00:00
[ account pauseInBackgroundTask ] ;
}
2015-01-30 08:05:15 +00:00
2016-03-03 08:15:44 +00:00
// Refresh the notifications counter
[ self refreshApplicationIconBadgeNumber ] ;
2015-10-20 08:19:21 +00:00
2015-02-02 15:43:47 +00:00
_isAppForeground = NO ;
2016-06-03 14:07:34 +00:00
2018-01-29 14:26:44 +00:00
// Analytics : Force to send the pending actions
2018-06-29 05:50:06 +00:00
[ [ DecryptionFailureTracker sharedInstance ] dispatch ] ;
2018-06-27 07:55:06 +00:00
[ [ Analytics sharedInstance ] dispatch ] ;
2014-10-02 15:02:47 +00:00
}
2015-06-18 09:19:42 +00:00
- ( void ) applicationWillEnterForeground : ( UIApplication * ) application
{
2016-02-03 11:21:45 +00:00
NSLog ( @ "[AppDelegate] applicationWillEnterForeground" ) ;
2018-02-04 16:13:44 +00:00
// Called as part of the transition from the background to the inactive state ; here you can undo many of the changes made on entering the background .
2017-10-18 09:58:24 +00:00
// Flush all the pending push notifications .
2017-10-23 15:34:42 +00:00
for ( NSMutableArray * array in self . incomingPushEventIds . allValues )
2017-10-23 14:58:55 +00:00
{
[ array removeAllObjects ] ;
}
2017-12-29 14:20:12 +00:00
[ incomingPushPayloads removeAllObjects ] ;
2017-10-18 09:58:24 +00:00
2018-02-04 16:13:44 +00:00
// Force each session to refresh here their publicised groups by user dictionary .
// When these publicised groups are retrieved for a user , they are cached and reused until the app is backgrounded and enters in the foreground again
for ( MXSession * session in mxSessionArray )
{
[ session markOutdatedPublicisedGroupsByUserData ] ;
}
2016-02-26 11:27:22 +00:00
2015-02-02 15:43:47 +00:00
_isAppForeground = YES ;
2014-10-02 15:02:47 +00:00
}
2015-06-18 09:19:42 +00:00
- ( void ) applicationDidBecomeActive : ( UIApplication * ) application
{
2016-02-03 11:21:45 +00:00
NSLog ( @ "[AppDelegate] applicationDidBecomeActive" ) ;
2016-04-06 09:28:12 +00:00
2016-08-01 12:21:58 +00:00
// Check if there is crash log to send
2018-07-02 12:41:55 +00:00
if ( RiotSettings . shared . enableCrashReport )
2016-08-01 12:21:58 +00:00
{
[ self checkExceptionToReport ] ;
}
2017-07-14 14:41:25 +00:00
2014-10-02 15:02:47 +00:00
// Restart any tasks that were paused ( or not yet started ) while the application was inactive . If the application was previously in the background , optionally refresh the user interface .
2018-08-13 15:14:32 +00:00
// Check if an initial sync failure occured while the app was in background
MXSession * mainSession = self . mxSessions . firstObject ;
if ( mainSession . state = = MXSessionStateInitialSyncFailed )
{
// Inform the end user why the app appears blank
NSError * error = [ NSError errorWithDomain : NSURLErrorDomain
code : NSURLErrorCannotConnectToHost
userInfo : @ { NSLocalizedDescriptionKey : NSLocalizedStringFromTable ( @ "homeserver_connection_lost" , @ "Vector" , nil ) } ] ;
[ self showErrorAsAlert : error ] ;
}
2015-02-12 10:16:28 +00:00
2018-05-23 15:01:18 +00:00
// Register to GDPR consent not given notification
[ self registerUserConsentNotGivenNotification ] ;
2019-08-30 09:15:06 +00:00
// Register to identity server terms not signed notification
[ self registerIdentityServiceTermsNotSignedNotification ] ;
2015-02-12 10:16:28 +00:00
// Start monitoring reachability
2016-02-11 15:09:35 +00:00
[ [ AFNetworkReachabilityManager sharedManager ] setReachabilityStatusChangeBlock : ^ ( AFNetworkReachabilityStatus status ) {
2016-02-11 14:05:24 +00:00
2016-02-11 15:09:35 +00:00
// Check whether monitoring is ready
if ( status ! = AFNetworkReachabilityStatusUnknown )
2016-02-11 14:05:24 +00:00
{
2016-02-11 15:09:35 +00:00
if ( status = = AFNetworkReachabilityStatusNotReachable )
2016-02-11 14:05:24 +00:00
{
2016-02-11 15:09:35 +00:00
// Prompt user
2016-02-11 14:05:24 +00:00
[ [ AppDelegate theDelegate ] showErrorAsAlert : [ NSError errorWithDomain : NSURLErrorDomain code : NSURLErrorNotConnectedToInternet userInfo : @ { NSLocalizedDescriptionKey : NSLocalizedStringFromTable ( @ "network_offline_prompt" , @ "Vector" , nil ) } ] ] ;
}
else
{
self . isOffline = NO ;
}
2017-07-14 14:41:25 +00:00
2016-08-01 15:07:39 +00:00
// Use a dispatch to avoid to kill ourselves
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ [ AFNetworkReachabilityManager sharedManager ] setReachabilityStatusChangeBlock : nil ] ;
} ) ;
2016-02-11 14:05:24 +00:00
}
} ] ;
2015-02-12 10:16:28 +00:00
[ [ AFNetworkReachabilityManager sharedManager ] startMonitoring ] ;
2015-08-04 16:37:48 +00:00
// Observe matrixKit error to alert user on error
matrixKitErrorObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXKErrorNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * note ) {
[ self showErrorAsAlert : note . object ] ;
} ] ;
2017-07-14 14:41:25 +00:00
2016-12-16 12:43:08 +00:00
// Observe crypto data storage corruption
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( onSessionCryptoDidCorruptData : ) name : kMXSessionCryptoDidCorruptDataNotification object : nil ] ;
2019-02-15 16:29:48 +00:00
// Observe wrong backup version
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( keyBackupStateDidChangeNotification : ) name : kMXKeyBackupDidStateChangeNotification object : nil ] ;
2016-04-05 13:31:52 +00:00
// Resume all existing matrix sessions
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
for ( MXKAccount * account in mxAccounts )
2015-06-18 09:19:42 +00:00
{
2016-04-05 13:31:52 +00:00
[ account resume ] ;
2015-03-10 13:15:02 +00:00
}
2016-04-05 13:31:52 +00:00
_isAppForeground = YES ;
2017-09-21 13:29:46 +00:00
2017-09-27 07:26:34 +00:00
if ( @ available ( iOS 11.0 , * ) )
2017-09-21 13:29:46 +00:00
{
// Riot has its own dark theme . Prevent iOS from applying its one
2017-11-24 16:30:55 +00:00
[ application keyWindow ] . accessibilityIgnoresInvertColors = YES ;
2017-09-21 13:29:46 +00:00
}
2017-01-31 23:18:01 +00:00
[ self handleLaunchAnimation ] ;
2014-10-02 15:02:47 +00:00
}
2015-06-18 09:19:42 +00:00
- ( void ) applicationWillTerminate : ( UIApplication * ) application
{
2016-02-03 11:21:45 +00:00
NSLog ( @ "[AppDelegate] applicationWillTerminate" ) ;
2014-10-02 15:02:47 +00:00
// Called when the application is about to terminate . Save data if appropriate . See also applicationDidEnterBackground : .
}
2017-10-05 07:36:40 +00:00
- ( void ) applicationDidReceiveMemoryWarning : ( UIApplication * ) application
{
NSLog ( @ "[AppDelegate] applicationDidReceiveMemoryWarning" ) ;
}
2019-04-19 16:33:21 +00:00
- ( BOOL ) application : ( UIApplication * ) application continueUserActivity : ( NSUserActivity * ) userActivity restorationHandler : ( void ( ^ ) ( NSArray < id < UIUserActivityRestoring > > * _Nullable ) ) restorationHandler
2016-04-06 16:05:26 +00:00
{
2016-08-04 09:56:06 +00:00
BOOL continueUserActivity = NO ;
2017-07-14 14:41:25 +00:00
2016-04-06 16:05:26 +00:00
if ( [ userActivity . activityType isEqualToString : NSUserActivityTypeBrowsingWeb ] )
{
2016-04-12 08:23:48 +00:00
continueUserActivity = [ self handleUniversalLink : userActivity ] ;
2016-04-06 16:05:26 +00:00
}
2017-08-22 13:02:16 +00:00
else if ( [ userActivity . activityType isEqualToString : INStartAudioCallIntentIdentifier ] ||
[ userActivity . activityType isEqualToString : INStartVideoCallIntentIdentifier ] )
{
INInteraction * interaction = userActivity . interaction ;
// roomID provided by Siri intent
NSString * roomID = userActivity . userInfo [ @ "roomID" ] ;
// We ' ve launched from calls history list
if ( ! roomID )
{
INPerson * person ;
if ( [ interaction . intent isKindOfClass : INStartAudioCallIntent . class ] )
{
person = [ [ ( INStartAudioCallIntent * ) ( interaction . intent ) contacts ] firstObject ] ;
}
else if ( [ interaction . intent isKindOfClass : INStartVideoCallIntent . class ] )
{
person = [ [ ( INStartVideoCallIntent * ) ( interaction . intent ) contacts ] firstObject ] ;
}
roomID = person . personHandle . value ;
}
BOOL isVideoCall = [ userActivity . activityType isEqualToString : INStartVideoCallIntentIdentifier ] ;
2017-08-28 15:14:10 +00:00
UIApplication * application = UIApplication . sharedApplication ;
2019-11-05 16:23:17 +00:00
id < MXBackgroundModeHandler > handler = [ MXSDKOptions sharedInstance ] . backgroundModeHandler ;
id < MXBackgroundTask > backgroundTask ;
2017-08-28 15:14:10 +00:00
// Start background task since we need time for MXSession preparasion because our app can be launched in the background
if ( application . applicationState = = UIApplicationStateBackground )
2019-11-05 16:23:17 +00:00
{
backgroundTask = [ handler startBackgroundTaskWithName : @ "[AppDelegate] application:continueUserActivity:restorationHandler: Audio or video call" expirationHandler : nil ] ;
}
2017-08-28 15:14:10 +00:00
2017-08-22 13:02:16 +00:00
MXSession * session = mxSessionArray . firstObject ;
[ session . callManager placeCallInRoom : roomID
withVideo : isVideoCall
2017-08-28 15:14:10 +00:00
success : ^ ( MXCall * call ) {
if ( application . applicationState = = UIApplicationStateBackground )
{
__weak NSNotificationCenter * center = NSNotificationCenter . defaultCenter ;
__block id token =
[ center addObserverForName : kMXCallStateDidChange
object : call
queue : nil
usingBlock : ^ ( NSNotification * _Nonnull note ) {
if ( call . state = = MXCallStateEnded )
{
2019-11-05 16:23:17 +00:00
[ backgroundTask stop ] ;
2017-08-28 15:14:10 +00:00
[ center removeObserver : token ] ;
}
} ] ;
}
}
failure : ^ ( NSError * error ) {
2019-11-05 16:23:17 +00:00
if ( backgroundTask )
{
[ backgroundTask stop ] ;
}
2017-08-28 15:14:10 +00:00
} ] ;
2017-08-22 13:02:16 +00:00
continueUserActivity = YES ;
}
2017-07-14 14:41:25 +00:00
2016-04-07 15:38:19 +00:00
return continueUserActivity ;
2016-04-06 16:05:26 +00:00
}
2015-11-17 23:15:52 +00:00
# pragma mark - Application layout handling
2019-01-05 02:28:03 +00:00
- ( void ) restoreInitialDisplay : ( void ( ^ ) ( void ) ) completion
2015-11-17 23:15:52 +00:00
{
2016-06-16 14:50:41 +00:00
// Suspend error notifications during navigation stack change .
isErrorNotificationSuspended = YES ;
2016-05-23 14:01:11 +00:00
// Dismiss potential view controllers that were presented modally ( like the media picker ) .
2016-04-05 16:05:50 +00:00
if ( self . window . rootViewController . presentedViewController )
2015-11-17 23:15:52 +00:00
{
2016-04-05 16:05:50 +00:00
// Do it asynchronously to avoid hasardous dispatch_async after calling restoreInitialDisplay
[ self . window . rootViewController dismissViewControllerAnimated : NO completion : ^ {
2016-05-23 14:01:11 +00:00
[ self popToHomeViewControllerAnimated : NO completion : ^ {
if ( completion )
{
completion ( ) ;
}
2016-09-06 15:20:16 +00:00
// Enable error notifications
isErrorNotificationSuspended = NO ;
2017-08-27 13:43:42 +00:00
if ( noCallSupportAlert )
2016-05-23 14:01:11 +00:00
{
NSLog ( @ "[AppDelegate] restoreInitialDisplay: keep visible noCall support alert" ) ;
2016-09-06 15:20:16 +00:00
[ self showNotificationAlert : noCallSupportAlert ] ;
2016-05-23 14:01:11 +00:00
}
2016-12-16 12:43:08 +00:00
else if ( cryptoDataCorruptedAlert )
{
NSLog ( @ "[AppDelegate] restoreInitialDisplay: keep visible log in again" ) ;
[ self showNotificationAlert : cryptoDataCorruptedAlert ] ;
}
2019-02-15 16:29:48 +00:00
else if ( wrongBackupVersionAlert )
{
NSLog ( @ "[AppDelegate] restoreInitialDisplay: keep visible wrongBackupVersionAlert" ) ;
[ self showNotificationAlert : wrongBackupVersionAlert ] ;
}
2016-09-06 15:20:16 +00:00
// Check whether an error notification is pending
else if ( _errorNotification )
2016-06-16 14:50:41 +00:00
{
2016-09-06 15:20:16 +00:00
[ self showNotificationAlert : _errorNotification ] ;
2016-06-16 14:50:41 +00:00
}
2016-05-23 14:01:11 +00:00
} ] ;
2016-05-23 13:39:48 +00:00
2016-04-05 16:05:50 +00:00
} ] ;
}
else
{
2016-06-16 14:50:41 +00:00
[ self popToHomeViewControllerAnimated : NO completion : ^ {
if ( completion )
{
completion ( ) ;
}
// Enable error notification ( Check whether a notification is pending )
isErrorNotificationSuspended = NO ;
2016-09-06 15:20:16 +00:00
if ( _errorNotification )
2016-06-16 14:50:41 +00:00
{
2016-09-06 15:20:16 +00:00
[ self showNotificationAlert : _errorNotification ] ;
2016-06-16 14:50:41 +00:00
}
} ] ;
2015-11-17 23:15:52 +00:00
}
}
2017-12-21 16:19:36 +00:00
- ( void ) restoreEmptyDetailsViewController
{
UIViewController * rootViewController = self . window . rootViewController ;
if ( [ rootViewController isKindOfClass : [ UISplitViewController class ] ] )
{
UISplitViewController * splitViewController = ( UISplitViewController * ) rootViewController ;
// Be sure that the primary is then visible too .
if ( splitViewController . displayMode = = UISplitViewControllerDisplayModePrimaryHidden )
{
splitViewController . preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible ;
}
if ( splitViewController . viewControllers . count = = 2 )
{
UIViewController * mainViewController = splitViewController . viewControllers [ 0 ] ;
UIStoryboard * storyboard = [ UIStoryboard storyboardWithName : @ "Main" bundle : [ NSBundle mainBundle ] ] ;
UIViewController * emptyDetailsViewController = [ storyboard instantiateViewControllerWithIdentifier : @ "EmptyDetailsViewControllerStoryboardId" ] ;
2019-01-11 10:45:27 +00:00
emptyDetailsViewController . view . backgroundColor = ThemeService . shared . theme . backgroundColor ;
2017-12-21 16:19:36 +00:00
splitViewController . viewControllers = @ [ mainViewController , emptyDetailsViewController ] ;
}
}
// Release the current selected item ( room / contact / group . . . ) .
[ _masterTabBarController releaseSelectedItem ] ;
}
2015-11-17 23:15:52 +00:00
2017-07-14 14:41:25 +00:00
- ( UIAlertController * ) showErrorAsAlert : ( NSError * ) error
2015-11-17 23:15:52 +00:00
{
// Ignore fake error , or connection cancellation error
if ( ! error || ( [ error . domain isEqualToString : NSURLErrorDomain ] && error . code = = NSURLErrorCancelled ) )
{
return nil ;
}
// Ignore network reachability error when the app is already offline
if ( self . isOffline && [ error . domain isEqualToString : NSURLErrorDomain ] && error . code = = NSURLErrorNotConnectedToInternet )
{
return nil ;
}
2018-05-31 16:39:15 +00:00
// Ignore GDPR Consent not given error . Already caught by kMXHTTPClientUserConsentNotGivenErrorNotification observation
if ( [ MXError isMXError : error ] )
{
MXError * mxError = [ [ MXError alloc ] initWithNSError : error ] ;
if ( [ mxError . errcode isEqualToString : kMXErrCodeStringConsentNotGiven ] )
{
return nil ;
}
}
2019-01-17 16:46:49 +00:00
2015-11-17 23:15:52 +00:00
NSString * title = [ error . userInfo valueForKey : NSLocalizedFailureReasonErrorKey ] ;
2016-02-11 13:06:48 +00:00
NSString * msg = [ error . userInfo valueForKey : NSLocalizedDescriptionKey ] ;
2015-11-17 23:15:52 +00:00
if ( ! title )
{
2016-02-11 13:06:48 +00:00
if ( msg )
{
title = msg ;
msg = nil ;
}
else
{
title = [ NSBundle mxk_localizedStringForKey : @ "error" ] ;
}
2015-11-17 23:15:52 +00:00
}
2019-01-17 16:46:49 +00:00
// Switch in offline mode in case of network reachability error
if ( [ error . domain isEqualToString : NSURLErrorDomain ] && error . code = = NSURLErrorNotConnectedToInternet )
{
self . isOffline = YES ;
}
2019-01-07 22:37:35 +00:00
2019-01-17 16:46:49 +00:00
return [ self showAlertWithTitle : title message : msg ] ;
}
- ( UIAlertController * ) showAlertWithTitle : ( NSString * ) title message : ( NSString * ) message
{
[ _errorNotification dismissViewControllerAnimated : NO completion : nil ] ;
_errorNotification = [ UIAlertController alertControllerWithTitle : title message : message preferredStyle : UIAlertControllerStyleAlert ] ;
2017-07-14 14:41:25 +00:00
[ _errorNotification addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "ok" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2019-01-17 16:46:49 +00:00
2017-07-14 14:41:25 +00:00
[ AppDelegate theDelegate ] . errorNotification = nil ;
2019-01-17 16:46:49 +00:00
2017-07-14 14:41:25 +00:00
} ] ] ;
2016-06-16 14:50:41 +00:00
// Display the error notification
2016-09-06 15:20:16 +00:00
if ( ! isErrorNotificationSuspended )
{
2017-07-14 14:41:25 +00:00
[ _errorNotification mxk_setAccessibilityIdentifier : @ "AppDelegateErrorAlert" ] ;
2016-09-06 15:20:16 +00:00
[ self showNotificationAlert : _errorNotification ] ;
}
2019-01-17 16:46:49 +00:00
2015-11-17 23:15:52 +00:00
return self . errorNotification ;
}
2017-07-14 14:41:25 +00:00
- ( void ) showNotificationAlert : ( UIAlertController * ) alert
2016-06-16 14:50:41 +00:00
{
2016-09-06 15:20:16 +00:00
if ( self . window . rootViewController . presentedViewController )
2016-06-16 14:50:41 +00:00
{
2017-07-14 14:41:25 +00:00
[ alert popoverPresentationController ] . sourceView = self . window . rootViewController . presentedViewController . view ;
[ alert popoverPresentationController ] . sourceRect = self . window . rootViewController . presentedViewController . view . bounds ;
[ self . window . rootViewController . presentedViewController presentViewController : alert animated : YES completion : nil ] ;
2016-09-06 15:20:16 +00:00
}
else
{
2017-07-14 14:41:25 +00:00
[ alert popoverPresentationController ] . sourceView = self . window . rootViewController . view ;
[ alert popoverPresentationController ] . sourceRect = self . window . rootViewController . view . bounds ;
[ self . window . rootViewController presentViewController : alert animated : YES completion : nil ] ;
2016-06-16 14:50:41 +00:00
}
}
2016-12-16 12:43:08 +00:00
- ( void ) onSessionCryptoDidCorruptData : ( NSNotification * ) notification
{
NSString * userId = notification . object ;
2017-07-14 14:41:25 +00:00
2016-12-16 12:43:08 +00:00
MXKAccount * account = [ [ MXKAccountManager sharedManager ] accountForUserId : userId ] ;
if ( account )
{
if ( cryptoDataCorruptedAlert )
{
2017-07-14 14:41:25 +00:00
[ cryptoDataCorruptedAlert dismissViewControllerAnimated : NO completion : nil ] ;
2016-12-16 12:43:08 +00:00
}
2017-07-14 14:41:25 +00:00
cryptoDataCorruptedAlert = [ UIAlertController alertControllerWithTitle : nil
message : NSLocalizedStringFromTable ( @ "e2e_need_log_in_again" , @ "Vector" , nil )
preferredStyle : UIAlertControllerStyleAlert ] ;
2016-12-16 12:43:08 +00:00
__weak typeof ( self ) weakSelf = self ;
2017-07-14 14:41:25 +00:00
[ cryptoDataCorruptedAlert addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "later" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> cryptoDataCorruptedAlert = nil ;
}
} ] ] ;
[ cryptoDataCorruptedAlert addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "settings_sign_out" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> cryptoDataCorruptedAlert = nil ;
[ [ MXKAccountManager sharedManager ] removeAccount : account completion : nil ] ;
}
} ] ] ;
2016-12-16 12:43:08 +00:00
[ self showNotificationAlert : cryptoDataCorruptedAlert ] ;
}
}
2019-02-15 16:29:48 +00:00
- ( void ) keyBackupStateDidChangeNotification : ( NSNotification * ) notification
{
MXKeyBackup * keyBackup = notification . object ;
if ( keyBackup . state = = MXKeyBackupStateWrongBackUpVersion )
{
if ( wrongBackupVersionAlert )
{
[ wrongBackupVersionAlert dismissViewControllerAnimated : NO completion : nil ] ;
}
wrongBackupVersionAlert = [ UIAlertController
alertControllerWithTitle : NSLocalizedStringFromTable ( @ "e2e_key_backup_wrong_version_title" , @ "Vector" , nil )
message : NSLocalizedStringFromTable ( @ "e2e_key_backup_wrong_version" , @ "Vector" , nil )
preferredStyle : UIAlertControllerStyleAlert ] ;
MXWeakify ( self ) ;
[ wrongBackupVersionAlert addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "e2e_key_backup_wrong_version_button_settings" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action )
{
MXStrongifyAndReturnIfNil ( self ) ;
self -> wrongBackupVersionAlert = nil ;
// TODO : Open settings
} ] ] ;
[ wrongBackupVersionAlert addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "e2e_key_backup_wrong_version_button_wasme" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action )
{
MXStrongifyAndReturnIfNil ( self ) ;
self -> wrongBackupVersionAlert = nil ;
} ] ] ;
[ self showNotificationAlert : wrongBackupVersionAlert ] ;
}
}
2016-04-05 16:05:50 +00:00
# pragma mark
2019-01-05 02:28:03 +00:00
- ( void ) popToHomeViewControllerAnimated : ( BOOL ) animated completion : ( void ( ^ ) ( void ) ) completion
2016-04-05 16:05:50 +00:00
{
2017-03-14 16:25:46 +00:00
UINavigationController * secondNavController = self . secondaryNavigationController ;
if ( secondNavController )
{
[ secondNavController popToRootViewControllerAnimated : animated ] ;
}
2017-07-14 14:41:25 +00:00
2017-03-23 16:48:05 +00:00
// Force back to the main screen if this is not the one that is displayed
if ( _masterTabBarController && _masterTabBarController ! = _masterNavigationController . visibleViewController )
2016-04-05 16:05:50 +00:00
{
2017-03-23 16:48:05 +00:00
// Listen to the masterNavigationController changes
// We need to be sure that masterTabBarController is back to the screen
2016-04-13 15:08:02 +00:00
popToHomeViewControllerCompletion = completion ;
2017-03-23 16:48:05 +00:00
_masterNavigationController . delegate = self ;
2017-07-14 14:41:25 +00:00
2017-03-23 16:48:05 +00:00
[ _masterNavigationController popToViewController : _masterTabBarController animated : animated ] ;
2016-04-05 16:05:50 +00:00
}
2016-04-13 15:08:02 +00:00
else
{
2017-03-23 16:48:05 +00:00
// Select the Home tab
_masterTabBarController . selectedIndex = TABBAR_HOME _INDEX ;
2016-06-16 14:50:41 +00:00
if ( completion )
{
completion ( ) ;
}
2016-04-13 15:08:02 +00:00
}
}
# pragma mark - UINavigationController delegate
- ( void ) navigationController : ( UINavigationController * ) navigationController didShowViewController : ( UIViewController * ) viewController animated : ( BOOL ) animated
{
2017-03-23 16:48:05 +00:00
if ( viewController = = _masterTabBarController )
2016-04-13 15:08:02 +00:00
{
2017-03-23 16:48:05 +00:00
_masterNavigationController . delegate = nil ;
2016-06-10 14:11:09 +00:00
// For unknown reason , the navigation bar is not restored correctly by [ popToViewController : animated : ]
// when a ViewController has hidden it ( see MXKAttachmentsViewController ) .
// Patch : restore navigation bar by default here .
2017-03-23 16:48:05 +00:00
_masterNavigationController . navigationBarHidden = NO ;
2016-06-10 14:11:09 +00:00
2017-03-28 15:26:50 +00:00
// Release the current selected item ( room / contact /. . . ) .
[ _masterTabBarController releaseSelectedItem ] ;
2017-07-14 14:41:25 +00:00
2016-04-13 15:08:02 +00:00
if ( popToHomeViewControllerCompletion )
{
2019-01-05 02:28:03 +00:00
void ( ^ popToHomeViewControllerCompletion2 ) ( void ) = popToHomeViewControllerCompletion ;
2016-04-19 07:14:32 +00:00
popToHomeViewControllerCompletion = nil ;
2017-07-14 14:41:25 +00:00
2016-04-19 07:14:32 +00:00
// Dispatch the completion in order to let navigation stack refresh itself .
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
popToHomeViewControllerCompletion2 ( ) ;
} ) ;
2016-04-13 15:08:02 +00:00
}
}
2016-04-05 16:05:50 +00:00
}
2018-06-27 07:55:06 +00:00
# pragma mark - Crash handling
2017-12-22 12:19:40 +00:00
2016-06-03 14:07:34 +00:00
// Check if there is a crash log to send to server
- ( void ) checkExceptionToReport
{
// Check if the app crashed last time
NSString * filePath = [ MXLogger crashLog ] ;
if ( filePath )
{
2017-06-29 15:33:08 +00:00
// Do not show the crash report dialog if it is already displayed
2017-06-29 15:45:54 +00:00
if ( [ self . window . rootViewController . childViewControllers [ 0 ] isKindOfClass : [ UINavigationController class ] ]
2017-07-14 14:41:25 +00:00
&& [ ( ( UINavigationController * ) self . window . rootViewController . childViewControllers [ 0 ] ) . visibleViewController isKindOfClass : [ BugReportViewController class ] ] )
2017-06-29 15:33:08 +00:00
{
return ;
}
2017-07-14 14:41:25 +00:00
2016-06-03 14:07:34 +00:00
NSString * description = [ [ NSString alloc ] initWithContentsOfFile : filePath
usedEncoding : nil
error : nil ] ;
2018-01-29 14:26:44 +00:00
NSLog ( @ "[AppDelegate] Promt user to report crash:\n%@" , description ) ;
// Ask the user to send a crash report
2016-08-01 12:21:58 +00:00
[ [ RageShakeManager sharedManager ] promptCrashReportInViewController : self . window . rootViewController ] ;
2016-06-03 14:07:34 +00:00
}
}
2017-09-19 14:06:44 +00:00
# pragma mark - Push notifications
2014-12-12 09:49:15 +00:00
2015-06-18 09:19:42 +00:00
- ( void ) registerUserNotificationSettings
{
2019-03-27 10:53:31 +00:00
NSLog ( @ "[AppDelegate][Push] registerUserNotificationSettings: isPushRegistered: %@" , @ ( isPushRegistered ) ) ;
2017-09-19 14:06:44 +00:00
if ( ! isPushRegistered )
2015-06-18 09:19:42 +00:00
{
2019-04-25 09:52:33 +00:00
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 ] ; // commenting this out will fall back to using the same AppDelegate methods as the iOS 9 way of doing this
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 ] ;
}
} ] ;
2014-12-18 15:29:34 +00:00
}
2014-12-12 09:49:15 +00:00
}
2017-08-23 16:07:14 +00:00
- ( void ) registerForRemoteNotificationsWithCompletion : ( nullable void ( ^ ) ( NSError * ) ) completion
{
2019-03-27 10:53:31 +00:00
NSLog ( @ "[AppDelegate][Push] registerForRemoteNotificationsWithCompletion" ) ;
2017-08-23 16:07:14 +00:00
self . registrationForRemoteNotificationsCompletion = completion ;
2017-08-21 10:28:05 +00:00
self . pushRegistry = [ [ PKPushRegistry alloc ] initWithQueue : nil ] ;
self . pushRegistry . delegate = self ;
self . pushRegistry . desiredPushTypes = [ NSSet setWithObject : PKPushTypeVoIP ] ;
2014-12-12 09:49:15 +00:00
}
2019-01-30 21:01:55 +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 ;
2019-03-22 09:21:16 +00:00
NSString * actionIdentifier = [ response actionIdentifier ] ;
NSString * roomId = content . userInfo [ @ "room_id" ] ;
2019-01-30 21:01:55 +00:00
2019-03-22 09:21:16 +00:00
if ( [ actionIdentifier isEqualToString : @ "inline-reply" ] )
{
if ( [ response isKindOfClass : [ UNTextInputNotificationResponse class ] ] )
{
UNTextInputNotificationResponse * textInputNotificationResponse = ( UNTextInputNotificationResponse * ) response ;
NSString * responseText = [ textInputNotificationResponse userText ] ;
2019-01-30 21:01:55 +00:00
2019-03-22 09:21:16 +00:00
[ self handleNotificationInlineReplyForRoomId : roomId withResponseText : responseText success : ^ ( NSString * eventId ) {
completionHandler ( ) ;
} failure : ^ ( NSError * error ) {
2019-01-30 21:01:55 +00:00
2019-03-22 09:21:16 +00:00
UNMutableNotificationContent * failureNotificationContent = [ [ UNMutableNotificationContent alloc ] init ] ;
failureNotificationContent . userInfo = content . userInfo ;
failureNotificationContent . body = NSLocalizedStringFromTable ( @ "room_event_failed_to_send" , @ "Vector" , nil ) ;
2019-03-22 10:18:07 +00:00
failureNotificationContent . threadIdentifier = roomId ;
2019-04-02 22:29:54 +00:00
2019-03-22 09:21:16 +00:00
NSString * uuid = [ [ NSUUID UUID ] UUIDString ] ;
UNNotificationRequest * failureNotificationRequest = [ UNNotificationRequest requestWithIdentifier : uuid
content : failureNotificationContent
trigger : nil ] ;
2019-04-02 22:29:54 +00:00
2019-03-22 09:21:16 +00:00
[ center addNotificationRequest : failureNotificationRequest withCompletionHandler : nil ] ;
NSLog ( @ "[AppDelegate][Push] didReceiveNotificationResponse: error sending text message: %@" , error ) ;
2019-04-02 22:29:54 +00:00
2019-03-22 09:21:16 +00:00
completionHandler ( ) ;
} ] ;
}
else
{
NSLog ( @ "[AppDelegate][Push] didReceiveNotificationResponse: error, expect a response of type UNTextInputNotificationResponse" ) ;
completionHandler ( ) ;
2019-01-30 21:01:55 +00:00
}
}
2019-03-22 09:21:16 +00:00
else if ( [ actionIdentifier isEqualToString : UNNotificationDefaultActionIdentifier ] )
2019-03-14 00:05:30 +00:00
{
[ self navigateToRoomById : roomId ] ;
2019-03-22 09:21:16 +00:00
completionHandler ( ) ;
2019-03-14 00:05:30 +00:00
}
2019-01-30 21:01:55 +00:00
else
{
2019-04-05 12:51:56 +00:00
NSLog ( @ "[AppDelegate][Push] didReceiveNotificationResponse: unhandled identifier %@" , actionIdentifier ) ;
2019-03-22 09:21:16 +00:00
completionHandler ( ) ;
2019-01-30 21:01:55 +00:00
}
}
2019-03-13 23:53:41 +00:00
// iOS 10 + , this is called when a notification is about to display in foreground .
2019-01-30 21:01:55 +00:00
- ( void ) userNotificationCenter : ( UNUserNotificationCenter * ) center willPresentNotification : ( UNNotification * ) notification withCompletionHandler : ( void ( ^ ) ( UNNotificationPresentationOptions options ) ) completionHandler
{
NSLog ( @ "[AppDelegate][Push] willPresentNotification: applicationState: %@" , @ ( [ UIApplication sharedApplication ] . applicationState ) ) ;
2019-03-13 23:53:41 +00:00
completionHandler ( UNNotificationPresentationOptionNone ) ;
2019-01-30 21:01:55 +00:00
}
2019-03-14 00:05:30 +00:00
- ( void ) navigateToRoomById : ( NSString * ) roomId
{
2015-10-20 08:19:21 +00:00
if ( roomId . length )
2015-06-18 09:19:42 +00:00
{
2015-10-20 08:19:21 +00:00
// TODO retrieve the right matrix session
2017-12-29 12:04:57 +00:00
// We can use the "user_id" value in notification . userInfo
2019-03-14 00:05:30 +00:00
2015-10-20 08:19:21 +00:00
// * * * * * * * * * * * * * *
// Patch consider the first session which knows the room id
MXKAccount * dedicatedAccount = nil ;
2019-03-14 00:05:30 +00:00
2015-10-20 08:19:21 +00:00
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
2019-03-14 00:05:30 +00:00
2015-10-20 08:19:21 +00:00
if ( mxAccounts . count = = 1 )
2015-06-18 09:19:42 +00:00
{
2015-10-20 08:19:21 +00:00
dedicatedAccount = mxAccounts . firstObject ;
}
else
{
for ( MXKAccount * account in mxAccounts )
{
if ( [ account . mxSession roomWithRoomId : roomId ] )
{
dedicatedAccount = account ;
break ;
}
}
}
2019-03-14 00:05:30 +00:00
2015-10-20 08:19:21 +00:00
// sanity checks
if ( dedicatedAccount && dedicatedAccount . mxSession )
{
2019-03-14 00:05:30 +00:00
NSLog ( @ "[AppDelegate][Push] navigateToRoomById: open the roomViewController %@" , roomId ) ;
2017-08-21 10:28:05 +00:00
[ self showRoom : roomId andEventId : nil withMatrixSession : dedicatedAccount . mxSession ] ;
2015-10-20 08:19:21 +00:00
}
else
{
2019-03-14 00:05:30 +00:00
NSLog ( @ "[AppDelegate][Push] navigateToRoomById : no linked session / account has been found." ) ;
2015-02-19 17:23:24 +00:00
}
2015-02-13 15:16:27 +00:00
}
2014-12-12 09:49:15 +00:00
}
2017-08-21 10:28:05 +00:00
- ( void ) pushRegistry : ( PKPushRegistry * ) registry didUpdatePushCredentials : ( PKPushCredentials * ) credentials forType : ( PKPushType ) type
2016-09-12 08:33:10 +00:00
{
2017-08-21 10:28:05 +00:00
NSData * token = credentials . token ;
2019-03-27 10:53:31 +00:00
NSLog ( @ "[AppDelegate][Push] didUpdatePushCredentials: Got Push token: %@. Type: %@" , [ MXKTools logForPushToken : token ] , type ) ;
2017-08-21 10:28:05 +00:00
MXKAccountManager * accountManager = [ MXKAccountManager sharedManager ] ;
2017-09-19 14:06:44 +00:00
[ accountManager setPushDeviceToken : token withPushOptions : @ { @ "format" : @ "event_id_only" } ] ;
2017-08-21 10:28:05 +00:00
2017-09-19 14:06:44 +00:00
isPushRegistered = YES ;
2017-09-10 13:41:50 +00:00
if ( self . registrationForRemoteNotificationsCompletion )
{
self . registrationForRemoteNotificationsCompletion ( nil ) ;
self . registrationForRemoteNotificationsCompletion = nil ;
}
2017-08-21 10:28:05 +00:00
}
- ( void ) pushRegistry : ( PKPushRegistry * ) registry didInvalidatePushTokenForType : ( PKPushType ) type
{
2019-03-27 10:53:31 +00:00
NSLog ( @ "[AppDelegate][Push] didInvalidatePushTokenForType: Type: %@" , type ) ;
2019-03-22 09:21:16 +00:00
[ self clearPushNotificationToken ] ;
2017-08-21 10:28:05 +00:00
}
- ( void ) pushRegistry : ( PKPushRegistry * ) registry didReceiveIncomingPushWithPayload : ( PKPushPayload * ) payload forType : ( PKPushType ) type
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] didReceiveIncomingPushWithPayload: applicationState: %tu - type: %@ - payload: %@" , [ UIApplication sharedApplication ] . applicationState , payload . type , payload . dictionaryPayload ) ;
2017-10-18 09:58:24 +00:00
// Display local notifications only when the app is running in background .
if ( [ UIApplication sharedApplication ] . applicationState = = UIApplicationStateBackground )
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] didReceiveIncomingPushWithPayload while app is in background" ) ;
2017-10-18 09:58:24 +00:00
2017-10-18 17:18:14 +00:00
// Check whether an event id is provided .
NSString * eventId = payload . dictionaryPayload [ @ "event_id" ] ;
if ( eventId )
2017-10-18 12:10:08 +00:00
{
2017-10-23 14:58:55 +00:00
// Add this event identifier in the pending push array for each session .
2017-10-23 15:34:42 +00:00
for ( NSMutableArray * array in self . incomingPushEventIds . allValues )
2017-10-23 14:58:55 +00:00
{
[ array addObject : eventId ] ;
}
2017-12-29 12:04:57 +00:00
// Cache payload for further usage
incomingPushPayloads [ eventId ] = payload . dictionaryPayload ;
2017-10-18 12:10:08 +00:00
}
else
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] didReceiveIncomingPushWithPayload - Unexpected payload %@" , payload . dictionaryPayload ) ;
2017-10-18 12:10:08 +00:00
}
2017-10-18 17:18:14 +00:00
2017-10-19 14:09:50 +00:00
// Trigger a background sync to handle notifications .
[ self launchBackgroundSync ] ;
2017-10-18 09:58:24 +00:00
}
2017-09-06 14:59:12 +00:00
}
2017-10-19 14:09:50 +00:00
- ( void ) launchBackgroundSync
2017-09-06 14:59:12 +00:00
{
// Launch a background sync for all existing matrix sessions
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
for ( MXKAccount * account in mxAccounts )
{
// Check the current session state
2017-10-18 09:58:24 +00:00
if ( account . mxSession . state = = MXSessionStatePaused )
2017-08-21 10:28:05 +00:00
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] launchBackgroundSync" ) ;
2017-10-18 09:58:24 +00:00
__weak typeof ( self ) weakSelf = self ;
2017-12-29 12:33:55 +00:00
NSMutableArray < NSString * > * incomingPushEventIds = self . incomingPushEventIds [ @ ( account . mxSession . hash ) ] ;
NSMutableArray < NSString * > * incomingPushEventIdsCopy = [ incomingPushEventIds copy ] ;
2017-09-06 14:59:12 +00:00
2017-10-23 14:58:55 +00:00
// Flush all the pending push notifications for this session .
2017-12-29 12:33:55 +00:00
[ incomingPushEventIds removeAllObjects ] ;
2017-10-23 14:58:55 +00:00
2017-10-18 09:58:24 +00:00
[ account backgroundSync : 20000 success : ^ {
// Sanity check
if ( ! weakSelf )
{
return ;
}
typeof ( self ) self = weakSelf ;
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] launchBackgroundSync: the background sync succeeds" ) ;
2017-10-18 09:58:24 +00:00
2017-10-19 14:09:50 +00:00
// Trigger local notifcations
[ self handleLocalNotificationsForAccount : account ] ;
2017-10-18 09:58:24 +00:00
2017-10-19 14:09:50 +00:00
// Update app icon badge number
[ self refreshApplicationIconBadgeNumber ] ;
2017-10-18 17:18:14 +00:00
2017-10-19 14:09:50 +00:00
} failure : ^ ( NSError * error ) {
2017-12-29 12:33:55 +00:00
NSLog ( @ "[AppDelegate][Push] launchBackgroundSync: the background sync failed. Error: %@ (%@). incomingPushEventIdsCopy: %@ - self.incomingPushEventIds: %@" , error . domain , @ ( error . code ) , incomingPushEventIdsCopy , incomingPushEventIds ) ;
2017-12-29 12:40:55 +00:00
// Trigger limited local notifications when the sync with HS fails
2017-12-29 12:33:55 +00:00
[ self handleLimitedLocalNotifications : account . mxSession events : incomingPushEventIdsCopy ] ;
// Update app icon badge number
[ self refreshApplicationIconBadgeNumber ] ;
2017-12-29 09:31:16 +00:00
2017-10-19 14:09:50 +00:00
} ] ;
}
}
}
- ( void ) handleLocalNotificationsForAccount : ( MXKAccount * ) account
{
2019-04-05 08:59:18 +00:00
NSString * userId = account . mxCredentials . userId ;
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: %@" , userId ) ;
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: eventsToNotify: %@" , eventsToNotify [ @ ( account . mxSession . hash ) ] ) ;
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: incomingPushEventIds: %@" , self . incomingPushEventIds [ @ ( account . mxSession . hash ) ] ) ;
2019-04-05 08:59:18 +00:00
2018-07-19 12:18:30 +00:00
__block NSUInteger scheduledNotifications = 0 ;
2019-04-05 08:59:18 +00:00
2017-10-19 14:09:50 +00:00
// The call invite are handled here only when the callkit is not active .
BOOL isCallKitActive = [ MXCallKitAdapter callKitAvailable ] && [ MXKAppSettings standardAppSettings ] . isCallKitEnabled ;
NSMutableArray * eventsArray = eventsToNotify [ @ ( account . mxSession . hash ) ] ;
2019-03-22 10:06:15 +00:00
NSMutableArray < NSString * > * redactedEventIds = [ NSMutableArray array ] ;
2019-04-05 08:59:18 +00:00
2017-10-19 14:09:50 +00:00
// Display a local notification for each event retrieved by the bg sync .
for ( NSUInteger index = 0 ; index < eventsArray . count ; index + + )
{
NSDictionary * eventDict = eventsArray [ index ] ;
NSString * eventId = eventDict [ @ "event_id" ] ;
NSString * roomId = eventDict [ @ "room_id" ] ;
BOOL checkReadEvent = YES ;
MXEvent * event ;
2019-04-05 08:59:18 +00:00
2017-10-19 14:09:50 +00:00
if ( eventId && roomId )
{
event = [ account . mxSession . store eventWithEventId : eventId inRoom : roomId ] ;
}
if ( event )
{
if ( event . isRedactedEvent )
{
2019-04-25 09:52:33 +00:00
// Collect redacted event ids to remove possible delivered redacted notifications
[ redactedEventIds addObject : eventId ] ;
2017-10-19 14:09:50 +00:00
continue ;
}
// Consider here the call invites
if ( event . eventType = = MXEventTypeCallInvite )
{
// Ignore call invite when callkit is active .
if ( isCallKitActive )
{
2019-04-05 08:59:18 +00:00
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: Skip call event. Event id: %@" , eventId ) ;
2017-10-19 14:09:50 +00:00
continue ;
}
else
2017-10-18 09:58:24 +00:00
{
2017-10-19 14:09:50 +00:00
// Retrieve the current call state from the call manager
MXCallInviteEventContent * callInviteEventContent = [ MXCallInviteEventContent modelFromJSON : event . content ] ;
MXCall * call = [ account . mxSession . callManager callWithCallId : callInviteEventContent . callId ] ;
2017-10-18 09:58:24 +00:00
2017-10-19 14:09:50 +00:00
if ( call . state <= MXCallStateRinging )
2017-10-18 09:58:24 +00:00
{
2017-10-19 14:09:50 +00:00
// Keep display a local notification even if the event has been read on another device .
checkReadEvent = NO ;
2017-10-18 09:58:24 +00:00
}
2017-10-19 14:09:50 +00:00
}
}
if ( checkReadEvent )
{
// Ignore event which has been read on another device .
2019-04-05 08:59:18 +00:00
MXReceiptData * readReceipt = [ account . mxSession . store getReceiptInRoom : roomId forUserId : userId ] ;
2017-10-19 14:09:50 +00:00
if ( readReceipt )
{
MXEvent * readReceiptEvent = [ account . mxSession . store eventWithEventId : readReceipt . eventId inRoom : roomId ] ;
if ( event . originServerTs <= readReceiptEvent . originServerTs )
2017-10-18 09:58:24 +00:00
{
2019-04-05 08:59:18 +00:00
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: Skip already read event. Event id: %@" , eventId ) ;
2017-10-19 14:09:50 +00:00
continue ;
}
}
}
// Prepare the local notification
MXPushRule * rule = eventDict [ @ "push_rule" ] ;
2019-04-05 08:59:18 +00:00
2019-04-25 09:52:33 +00:00
[ self notificationContentForEvent : event pushRule : rule inAccount : account onComplete : ^ ( UNNotificationContent * _Nullable notificationContent ) {
if ( notificationContent )
{
UNNotificationRequest * request = [ UNNotificationRequest requestWithIdentifier : eventId
content : notificationContent
trigger : nil ] ;
2019-04-05 08:59:18 +00:00
2019-04-25 09:52:33 +00:00
[ [ UNUserNotificationCenter currentNotificationCenter ] addNotificationRequest : request withCompletionHandler : ^ ( NSError * _Nullable error ) {
2019-04-05 08:59:18 +00:00
2019-04-25 09:52:33 +00:00
if ( error )
{
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: Fail to display notification for event %@ with error: %@" , eventId , error ) ;
}
else
{
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@" , eventId ) ;
}
} ] ;
scheduledNotifications + + ;
}
else
{
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated content. Event id: %@" , eventId ) ;
}
} ] ;
2017-10-18 09:58:24 +00:00
}
2017-09-06 14:59:12 +00:00
}
2019-04-05 08:59:18 +00:00
2019-04-25 09:52:33 +00:00
// Remove possible pending and delivered notifications having a redacted event id
if ( redactedEventIds . count )
2019-03-22 10:06:15 +00:00
{
2019-04-25 09:52:33 +00:00
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: Remove possible notification with redacted event ids: %@" , redactedEventIds ) ;
[ [ UNUserNotificationCenter currentNotificationCenter ] removePendingNotificationRequestsWithIdentifiers : redactedEventIds ] ;
[ [ UNUserNotificationCenter currentNotificationCenter ] removeDeliveredNotificationsWithIdentifiers : redactedEventIds ] ;
2017-09-06 14:59:12 +00:00
}
2019-04-05 08:59:18 +00:00
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: Sent %tu local notifications for %tu events" , scheduledNotifications , eventsArray . count ) ;
2019-04-05 08:59:18 +00:00
2017-10-19 14:09:50 +00:00
[ eventsArray removeAllObjects ] ;
2017-08-21 10:28:05 +00:00
}
2019-04-05 08:59:18 +00:00
- ( NSString * ) notificationSoundNameFromPushRule : ( MXPushRule * ) pushRule
{
NSString * soundName ;
// Set sound name based on the value provided in action of MXPushRule
for ( MXPushRuleAction * action in pushRule . actions )
{
if ( action . actionType = = MXPushRuleActionTypeSetTweak )
{
if ( [ action . parameters [ @ "set_tweak" ] isEqualToString : @ "sound" ] )
{
soundName = action . parameters [ @ "value" ] ;
if ( [ soundName isEqualToString : @ "default" ] )
{
soundName = @ "message.mp3" ;
}
}
}
}
return soundName ;
}
- ( NSString * ) notificationCategoryIdentifierForEvent : ( MXEvent * ) event
{
BOOL isNotificationContentShown = ! event . isEncrypted || RiotSettings . shared . showDecryptedContentInNotifications ;
NSString * categoryIdentifier ;
if ( ( event . eventType = = MXEventTypeRoomMessage || event . eventType = = MXEventTypeRoomEncrypted ) && isNotificationContentShown )
{
categoryIdentifier = @ "QUICK_REPLY" ;
}
return categoryIdentifier ;
}
- ( NSDictionary * ) notificationUserInfoForEvent : ( MXEvent * ) event andUserId : ( NSString * ) userId
{
NSDictionary * notificationUserInfo = @ {
@ "type" : @ "full" ,
@ "room_id" : event . roomId ,
@ "event_id" : event . eventId ,
@ "user_id" : userId
} ;
return notificationUserInfo ;
}
2018-07-19 12:18:30 +00:00
- ( void ) notificationBodyForEvent : ( MXEvent * ) event pushRule : ( MXPushRule * ) rule inAccount : ( MXKAccount * ) account onComplete : ( void ( ^ ) ( NSString * _Nullable notificationBody ) ) onComplete ;
2017-08-21 10:28:05 +00:00
{
if ( ! event . content || ! event . content . count )
2017-12-15 08:26:44 +00:00
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] notificationBodyForEvent: empty event content" ) ;
2018-07-19 12:18:30 +00:00
onComplete ( nil ) ;
return ;
2017-12-15 08:26:44 +00:00
}
2017-08-21 10:28:05 +00:00
2017-10-18 09:58:24 +00:00
MXRoom * room = [ account . mxSession roomWithRoomId : event . roomId ] ;
2018-07-19 12:18:30 +00:00
if ( ! room )
2017-08-21 10:28:05 +00:00
{
2018-07-19 12:18:30 +00:00
NSLog ( @ "[AppDelegate][Push] notificationBodyForEvent: Unknown room" ) ;
onComplete ( nil ) ;
return ;
}
2018-08-01 15:12:46 +00:00
[ room state : ^ ( MXRoomState * roomState ) {
2018-07-19 12:18:30 +00:00
NSString * notificationBody ;
2018-08-01 15:12:46 +00:00
NSString * eventSenderName = [ roomState . members memberName : event . sender ] ;
2018-07-19 12:18:30 +00:00
if ( event . eventType = = MXEventTypeRoomMessage || event . eventType = = MXEventTypeRoomEncrypted )
2017-10-12 09:42:10 +00:00
{
2018-07-19 12:18:30 +00:00
if ( room . isMentionsOnly )
2017-10-12 12:46:20 +00:00
{
2018-07-19 12:18:30 +00:00
// A local notification will be displayed only for highlighted notification .
BOOL isHighlighted = NO ;
// Check whether is there an highlight tweak on it
for ( MXPushRuleAction * ruleAction in rule . actions )
2017-10-12 12:46:20 +00:00
{
2018-07-19 12:18:30 +00:00
if ( ruleAction . actionType = = MXPushRuleActionTypeSetTweak )
2017-10-12 12:46:20 +00:00
{
2018-07-19 12:18:30 +00:00
if ( [ ruleAction . parameters [ @ "set_tweak" ] isEqualToString : @ "highlight" ] )
2017-10-12 12:46:20 +00:00
{
2018-07-19 12:18:30 +00:00
// Check the highlight tweak "value"
// If not present , highlight . Else check its value before highlighting
if ( nil = = ruleAction . parameters [ @ "value" ] || YES = = [ ruleAction . parameters [ @ "value" ] boolValue ] )
{
isHighlighted = YES ;
break ;
}
2017-10-12 12:46:20 +00:00
}
}
}
2018-07-19 12:18:30 +00:00
if ( ! isHighlighted )
{
// Ignore this notif .
NSLog ( @ "[AppDelegate][Push] notificationBodyForEvent: Ignore non highlighted notif in mentions only room" ) ;
onComplete ( nil ) ;
return ;
}
2017-10-12 12:46:20 +00:00
}
2018-07-19 12:18:30 +00:00
NSString * msgType = event . content [ @ "msgtype" ] ;
NSString * content = event . content [ @ "body" ] ;
if ( event . isEncrypted && ! RiotSettings . shared . showDecryptedContentInNotifications )
2017-10-12 12:46:20 +00:00
{
2018-07-19 12:18:30 +00:00
// Hide the content
msgType = nil ;
}
NSString * roomDisplayName = room . summary . displayname ;
// Display the room name only if it is different than the sender name
if ( roomDisplayName . length && ! [ roomDisplayName isEqualToString : eventSenderName ] )
{
if ( [ msgType isEqualToString : @ "m.text" ] )
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "MSG_FROM_USER_IN_ROOM_WITH_CONTENT" , nil ) , eventSenderName , roomDisplayName , content ] ;
else if ( [ msgType isEqualToString : @ "m.emote" ] )
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "ACTION_FROM_USER_IN_ROOM" , nil ) , roomDisplayName , eventSenderName , content ] ;
else if ( [ msgType isEqualToString : @ "m.image" ] )
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "IMAGE_FROM_USER_IN_ROOM" , nil ) , eventSenderName , content , roomDisplayName ] ;
else
// Encrypted messages falls here
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "MSG_FROM_USER_IN_ROOM" , nil ) , eventSenderName , roomDisplayName ] ;
}
else
{
if ( [ msgType isEqualToString : @ "m.text" ] )
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "MSG_FROM_USER_WITH_CONTENT" , nil ) , eventSenderName , content ] ;
else if ( [ msgType isEqualToString : @ "m.emote" ] )
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "ACTION_FROM_USER" , nil ) , eventSenderName , content ] ;
else if ( [ msgType isEqualToString : @ "m.image" ] )
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "IMAGE_FROM_USER" , nil ) , eventSenderName , content ] ;
else
// Encrypted messages falls here
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "MSG_FROM_USER" , nil ) , eventSenderName ] ;
2017-10-12 12:46:20 +00:00
}
2017-10-12 09:42:10 +00:00
}
2018-07-19 12:18:30 +00:00
else if ( event . eventType = = MXEventTypeCallInvite )
2017-09-08 13:32:55 +00:00
{
2018-07-19 12:18:30 +00:00
NSString * sdp = event . content [ @ "offer" ] [ @ "sdp" ] ;
BOOL isVideoCall = [ sdp rangeOfString : @ "m=video" ] . location ! = NSNotFound ;
if ( ! isVideoCall )
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "VOICE_CALL_FROM_USER" , nil ) , eventSenderName ] ;
else
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "VIDEO_CALL_FROM_USER" , nil ) , eventSenderName ] ;
2017-09-08 13:32:55 +00:00
}
2018-07-19 12:18:30 +00:00
else if ( event . eventType = = MXEventTypeRoomMember )
2017-08-21 10:28:05 +00:00
{
2018-07-19 12:18:30 +00:00
NSString * roomDisplayName = room . summary . displayname ;
if ( roomDisplayName . length && ! [ roomDisplayName isEqualToString : eventSenderName ] )
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "USER_INVITE_TO_NAMED_ROOM" , nil ) , eventSenderName , roomDisplayName ] ;
2017-08-21 10:28:05 +00:00
else
2018-07-19 12:18:30 +00:00
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "USER_INVITE_TO_CHAT" , nil ) , eventSenderName ] ;
2017-08-21 10:28:05 +00:00
}
2018-07-19 12:18:30 +00:00
else if ( event . eventType = = MXEventTypeSticker )
2015-10-20 08:19:21 +00:00
{
2018-07-19 12:18:30 +00:00
NSString * roomDisplayName = room . summary . displayname ;
if ( roomDisplayName . length && ! [ roomDisplayName isEqualToString : eventSenderName ] )
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "MSG_FROM_USER_IN_ROOM" , nil ) , eventSenderName , roomDisplayName ] ;
2017-08-21 10:28:05 +00:00
else
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "MSG_FROM_USER" , nil ) , eventSenderName ] ;
2015-02-19 17:23:24 +00:00
}
2018-07-19 12:18:30 +00:00
onComplete ( notificationBody ) ;
} ] ;
2016-09-12 08:33:10 +00:00
}
2019-03-21 23:40:27 +00:00
// iOS 10 + , does the same thing as notificationBodyForEvent : pushRule : inAccount : onComplete : , except with more features
2019-04-05 08:59:18 +00:00
- ( void ) notificationContentForEvent : ( MXEvent * ) event pushRule : ( MXPushRule * ) rule inAccount : ( MXKAccount * ) account onComplete : ( void ( ^ ) ( UNNotificationContent * _Nullable notificationContent ) ) onComplete ;
2019-02-08 11:42:55 +00:00
{
if ( ! event . content || ! event . content . count )
{
NSLog ( @ "[AppDelegate][Push] notificationContentForEvent: empty event content" ) ;
onComplete ( nil ) ;
return ;
}
MXRoom * room = [ account . mxSession roomWithRoomId : event . roomId ] ;
if ( ! room )
{
NSLog ( @ "[AppDelegate][Push] notificationBodyForEvent: Unknown room" ) ;
onComplete ( nil ) ;
return ;
}
[ room state : ^ ( MXRoomState * roomState ) {
2019-03-21 23:40:27 +00:00
NSString * notificationTitle ;
2019-02-08 11:42:55 +00:00
NSString * notificationBody ;
2019-03-21 23:40:27 +00:00
NSString * threadIdentifier = room . roomId ;
2019-02-08 11:42:55 +00:00
NSString * eventSenderName = [ roomState . members memberName : event . sender ] ;
if ( event . eventType = = MXEventTypeRoomMessage || event . eventType = = MXEventTypeRoomEncrypted )
{
if ( room . isMentionsOnly )
{
// A local notification will be displayed only for highlighted notification .
BOOL isHighlighted = NO ;
// Check whether is there an highlight tweak on it
for ( MXPushRuleAction * ruleAction in rule . actions )
{
if ( ruleAction . actionType = = MXPushRuleActionTypeSetTweak )
{
if ( [ ruleAction . parameters [ @ "set_tweak" ] isEqualToString : @ "highlight" ] )
{
// Check the highlight tweak "value"
// If not present , highlight . Else check its value before highlighting
if ( nil = = ruleAction . parameters [ @ "value" ] || YES = = [ ruleAction . parameters [ @ "value" ] boolValue ] )
{
isHighlighted = YES ;
break ;
}
}
}
}
if ( ! isHighlighted )
{
// Ignore this notif .
NSLog ( @ "[AppDelegate][Push] notificationBodyForEvent: Ignore non highlighted notif in mentions only room" ) ;
onComplete ( nil ) ;
return ;
}
}
2019-04-05 08:59:18 +00:00
2019-02-08 11:42:55 +00:00
NSString * msgType = event . content [ @ "msgtype" ] ;
2019-03-21 23:40:27 +00:00
NSString * messageContent = event . content [ @ "body" ] ;
2019-04-05 08:59:18 +00:00
2019-02-08 11:42:55 +00:00
if ( event . isEncrypted && ! RiotSettings . shared . showDecryptedContentInNotifications )
{
// Hide the content
msgType = nil ;
}
2019-04-05 08:59:18 +00:00
2019-02-08 11:42:55 +00:00
NSString * roomDisplayName = room . summary . displayname ;
2019-04-05 08:59:18 +00:00
2019-02-08 11:42:55 +00:00
// Display the room name only if it is different than the sender name
if ( roomDisplayName . length && ! [ roomDisplayName isEqualToString : eventSenderName ] )
{
2019-04-05 13:55:05 +00:00
notificationTitle = [ NSString localizedUserNotificationStringForKey : @ "MSG_FROM_USER_IN_ROOM_TITLE" arguments : @ [ eventSenderName , roomDisplayName ] ] ;
2019-04-05 08:59:18 +00:00
2019-02-08 11:42:55 +00:00
if ( [ msgType isEqualToString : @ "m.text" ] )
2019-04-05 08:59:18 +00:00
{
2019-03-21 23:40:27 +00:00
notificationBody = messageContent ;
2019-04-05 08:59:18 +00:00
}
2019-02-08 11:42:55 +00:00
else if ( [ msgType isEqualToString : @ "m.emote" ] )
2019-03-21 23:40:27 +00:00
{
2019-04-05 08:59:18 +00:00
notificationBody = [ NSString localizedUserNotificationStringForKey : @ "ACTION_FROM_USER" arguments : @ [ eventSenderName , messageContent ] ] ;
2019-03-21 23:40:27 +00:00
}
2019-02-08 11:42:55 +00:00
else if ( [ msgType isEqualToString : @ "m.image" ] )
2019-04-05 08:59:18 +00:00
{
notificationBody = [ NSString localizedUserNotificationStringForKey : @ "IMAGE_FROM_USER" arguments : @ [ eventSenderName , messageContent ] ] ;
}
2019-02-08 11:42:55 +00:00
else
2019-04-05 08:59:18 +00:00
{
2019-02-08 11:42:55 +00:00
// Encrypted messages falls here
2019-04-05 08:59:18 +00:00
notificationBody = [ NSString localizedUserNotificationStringForKey : @ "MSG_FROM_USER" arguments : @ [ eventSenderName ] ] ;
}
2019-02-08 11:42:55 +00:00
}
else
{
2019-03-21 23:40:27 +00:00
notificationTitle = eventSenderName ;
2019-04-05 08:59:18 +00:00
2019-02-08 11:42:55 +00:00
if ( [ msgType isEqualToString : @ "m.text" ] )
2019-04-05 08:59:18 +00:00
{
2019-03-21 23:40:27 +00:00
notificationBody = messageContent ;
2019-04-05 08:59:18 +00:00
}
2019-02-08 11:42:55 +00:00
else if ( [ msgType isEqualToString : @ "m.emote" ] )
2019-03-21 23:40:27 +00:00
{
2019-04-05 08:59:18 +00:00
notificationBody = [ NSString localizedUserNotificationStringForKey : @ "ACTION_FROM_USER" arguments : @ [ eventSenderName , messageContent ] ] ;
2019-03-21 23:40:27 +00:00
}
2019-02-08 11:42:55 +00:00
else if ( [ msgType isEqualToString : @ "m.image" ] )
2019-04-05 08:59:18 +00:00
{
notificationBody = [ NSString localizedUserNotificationStringForKey : @ "IMAGE_FROM_USER" arguments : @ [ eventSenderName , messageContent ] ] ;
}
2019-02-08 11:42:55 +00:00
else
2019-04-05 08:59:18 +00:00
{
2019-02-08 11:42:55 +00:00
// Encrypted messages falls here
2019-04-05 08:59:18 +00:00
notificationBody = [ NSString localizedUserNotificationStringForKey : @ "MSG_FROM_USER" arguments : @ [ eventSenderName ] ] ;
}
2019-02-08 11:42:55 +00:00
}
}
else if ( event . eventType = = MXEventTypeCallInvite )
{
NSString * sdp = event . content [ @ "offer" ] [ @ "sdp" ] ;
BOOL isVideoCall = [ sdp rangeOfString : @ "m=video" ] . location ! = NSNotFound ;
2019-04-05 08:59:18 +00:00
2019-02-08 11:42:55 +00:00
if ( ! isVideoCall )
2019-04-05 08:59:18 +00:00
{
notificationBody = [ NSString localizedUserNotificationStringForKey : @ "VOICE_CALL_FROM_USER" arguments : @ [ eventSenderName ] ] ;
}
2019-02-08 11:42:55 +00:00
else
2019-04-05 08:59:18 +00:00
{
notificationBody = [ NSString localizedUserNotificationStringForKey : @ "VIDEO_CALL_FROM_USER" arguments : @ [ eventSenderName ] ] ;
}
2019-03-22 00:10:57 +00:00
// call notifications should stand out from normal messages , so we don ' t stack them
threadIdentifier = nil ;
2019-02-08 11:42:55 +00:00
}
else if ( event . eventType = = MXEventTypeRoomMember )
{
NSString * roomDisplayName = room . summary . displayname ;
2019-04-05 08:59:18 +00:00
2019-02-08 11:42:55 +00:00
if ( roomDisplayName . length && ! [ roomDisplayName isEqualToString : eventSenderName ] )
2019-04-05 08:59:18 +00:00
{
notificationBody = [ NSString localizedUserNotificationStringForKey : @ "USER_INVITE_TO_NAMED_ROOM" arguments : @ [ eventSenderName , roomDisplayName ] ] ;
}
2019-02-08 11:42:55 +00:00
else
2019-04-05 08:59:18 +00:00
{
notificationBody = [ NSString localizedUserNotificationStringForKey : @ "USER_INVITE_TO_CHAT" arguments : @ [ eventSenderName ] ] ;
}
2019-02-08 11:42:55 +00:00
}
else if ( event . eventType = = MXEventTypeSticker )
{
NSString * roomDisplayName = room . summary . displayname ;
2019-04-05 08:59:18 +00:00
2019-02-08 11:42:55 +00:00
if ( roomDisplayName . length && ! [ roomDisplayName isEqualToString : eventSenderName ] )
2019-04-05 08:59:18 +00:00
{
notificationTitle = [ NSString localizedUserNotificationStringForKey : @ "MSG_FROM_USER_IN_ROOM_TITLE" arguments : @ [ eventSenderName , roomDisplayName ] ] ;
}
2019-02-08 11:42:55 +00:00
else
2019-04-05 08:59:18 +00:00
{
2019-03-21 23:40:27 +00:00
notificationTitle = eventSenderName ;
2019-04-05 08:59:18 +00:00
}
notificationBody = [ NSString localizedUserNotificationStringForKey : @ "STICKER_FROM_USER" arguments : @ [ eventSenderName ] ] ;
2019-02-08 11:42:55 +00:00
}
2019-04-05 08:59:18 +00:00
UNMutableNotificationContent * notificationContent = [ [ UNMutableNotificationContent alloc ] init ] ;
NSDictionary * notificationUserInfo = [ self notificationUserInfoForEvent : event andUserId : account . mxCredentials . userId ] ;
NSString * notificationSoundName = [ self notificationSoundNameFromPushRule : rule ] ;
NSString * categoryIdentifier = [ self notificationCategoryIdentifierForEvent : event ] ;
2019-04-05 14:48:24 +00:00
notificationContent . title = notificationTitle ;
2019-04-05 08:59:18 +00:00
notificationContent . body = notificationBody ;
notificationContent . threadIdentifier = threadIdentifier ;
notificationContent . userInfo = notificationUserInfo ;
notificationContent . categoryIdentifier = categoryIdentifier ;
if ( notificationSoundName )
{
notificationContent . sound = [ UNNotificationSound soundNamed : notificationSoundName ] ;
}
onComplete ( [ notificationContent copy ] ) ;
2019-02-08 11:42:55 +00:00
} ] ;
}
2017-12-29 12:04:57 +00:00
/ * *
2017-12-29 12:22:29 +00:00
Display "limited" notifications for events the app was not able to get data
2017-12-29 12:04:57 +00:00
( because of / sync failure ) .
2017-12-29 12:22:29 +00:00
In this situation , we are only able to display "You received a message in %@" .
2017-12-29 12:04:57 +00:00
@ param mxSession the matrix session where the / sync failed .
@ param events the list of events id we did not get data .
* /
2017-12-29 12:22:29 +00:00
- ( void ) handleLimitedLocalNotifications : ( MXSession * ) mxSession events : ( NSArray < NSString * > * ) events
2017-12-29 09:31:16 +00:00
{
NSString * userId = mxSession . matrixRestClient . credentials . userId ;
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForFailedSync: %@" , userId ) ;
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForFailedSync: eventsToNotify: %@" , eventsToNotify [ @ ( mxSession . hash ) ] ) ;
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForFailedSync: incomingPushEventIds: %@" , self . incomingPushEventIds [ @ ( mxSession . hash ) ] ) ;
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForFailedSync: events: %@" , events ) ;
2017-12-29 14:20:12 +00:00
if ( ! events . count )
2017-12-29 09:31:16 +00:00
{
return ;
}
for ( NSString * eventId in events )
{
2017-12-29 12:04:57 +00:00
// Build notification user info
NSMutableDictionary * userInfo = [ NSMutableDictionary dictionaryWithDictionary : @ {
2017-12-29 15:43:20 +00:00
@ "type" : @ "limited" ,
2017-12-29 12:04:57 +00:00
@ "event_id" : eventId ,
@ "user_id" : userId
} ] ;
// Add the room_id so that user will open the room when tapping on the notif
NSDictionary * payload = incomingPushPayloads [ eventId ] ;
NSString * roomId = payload [ @ "room_id" ] ;
if ( roomId )
{
userInfo [ @ "room_id" ] = roomId ;
}
else
{
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForFailedSync: room_id is missing for event %@ in payload %@" , eventId , payload ) ;
}
2017-12-29 09:31:16 +00:00
2019-04-25 09:52:33 +00:00
UNMutableNotificationContent * localNotificationContentForFailedSync = [ [ UNMutableNotificationContent alloc ] init ] ;
localNotificationContentForFailedSync . userInfo = userInfo ;
localNotificationContentForFailedSync . body = [ self limitedNotificationBodyForEvent : eventId inMatrixSession : mxSession ] ;
localNotificationContentForFailedSync . threadIdentifier = roomId ;
UNNotificationRequest * request = [ UNNotificationRequest requestWithIdentifier : eventId content : localNotificationContentForFailedSync trigger : nil ] ;
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForFailedSync: Display notification for event %@" , eventId ) ;
[ [ UNUserNotificationCenter currentNotificationCenter ] addNotificationRequest : request withCompletionHandler : nil ] ;
2017-12-29 09:31:16 +00:00
}
}
2017-12-29 12:04:57 +00:00
/ * *
2017-12-29 12:40:55 +00:00
Build the body for the "limited" notification to display to the user .
2017-12-29 12:04:57 +00:00
@ param eventId the id of the event the app failed to get data .
@ param mxSession the matrix session where the / sync failed .
@ return the string to display in the local notification .
* /
2017-12-29 12:22:29 +00:00
- ( nullable NSString * ) limitedNotificationBodyForEvent : ( NSString * ) eventId inMatrixSession : ( MXSession * ) mxSession
2017-12-29 12:04:57 +00:00
{
NSString * notificationBody ;
NSString * roomDisplayName ;
NSDictionary * payload = incomingPushPayloads [ eventId ] ;
NSString * roomId = payload [ @ "room_id" ] ;
if ( roomId )
{
MXRoomSummary * roomSummary = [ mxSession roomSummaryWithRoomId : roomId ] ;
if ( roomSummary )
{
roomDisplayName = roomSummary . displayname ;
}
}
if ( roomDisplayName . length )
{
2018-01-03 10:06:39 +00:00
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "SINGLE_UNREAD_IN_ROOM" , nil ) , roomDisplayName ] ;
2017-12-29 12:04:57 +00:00
}
else
{
2018-01-03 10:06:39 +00:00
notificationBody = NSLocalizedString ( @ "SINGLE_UNREAD" , nil ) ;
2017-12-29 12:04:57 +00:00
}
return notificationBody ;
}
2016-03-03 08:15:44 +00:00
- ( void ) refreshApplicationIconBadgeNumber
{
2017-04-18 13:55:51 +00:00
// Consider the total number of missed discussions including the invites .
NSUInteger count = [ self . masterTabBarController missedDiscussionsCount ] ;
2017-04-18 14:20:55 +00:00
2016-04-26 12:45:19 +00:00
NSLog ( @ "[AppDelegate] refreshApplicationIconBadgeNumber: %tu" , count ) ;
[ UIApplication sharedApplication ] . applicationIconBadgeNumber = count ;
2016-03-03 08:15:44 +00:00
}
2015-11-17 23:15:52 +00:00
2019-03-22 09:21:16 +00:00
- ( void ) handleNotificationInlineReplyForRoomId : ( NSString * ) roomId
withResponseText : ( NSString * ) responseText
success : ( void ( ^ ) ( NSString * eventId ) ) success
failure : ( void ( ^ ) ( NSError * error ) ) failure
{
if ( ! roomId . length )
{
failure ( nil ) ;
return ;
}
2019-04-02 22:29:54 +00:00
2019-03-22 09:21:16 +00:00
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
2019-04-02 22:29:54 +00:00
2019-03-22 09:21:16 +00:00
MXKRoomDataSourceManager * manager ;
2019-04-02 22:29:54 +00:00
2019-03-22 09:21:16 +00:00
for ( MXKAccount * account in mxAccounts )
{
MXRoom * room = [ account . mxSession roomWithRoomId : roomId ] ;
if ( room )
{
manager = [ MXKRoomDataSourceManager sharedManagerForMatrixSession : account . mxSession ] ;
if ( manager )
{
break ;
}
}
}
2019-04-02 22:29:54 +00:00
2019-03-22 09:21:16 +00:00
if ( manager = = nil )
{
NSLog ( @ "[AppDelegate][Push] didReceiveNotificationResponse: room with id %@ not found" , roomId ) ;
failure ( nil ) ;
}
else
{
[ manager roomDataSourceForRoom : roomId create : YES onComplete : ^ ( MXKRoomDataSource * roomDataSource ) {
if ( responseText ! = nil && responseText . length ! = 0 )
{
NSLog ( @ "[AppDelegate][Push] didReceiveNotificationResponse: sending message to room: %@" , roomId ) ;
[ roomDataSource sendTextMessage : responseText success : ^ ( NSString * eventId ) {
success ( eventId ) ;
} failure : ^ ( NSError * error ) {
failure ( error ) ;
} ] ;
}
else
{
failure ( nil ) ;
}
} ] ;
}
}
- ( void ) clearPushNotificationToken
{
2019-04-05 14:03:27 +00:00
NSLog ( @ "[AppDelegate][Push] clearPushNotificationToken: Clear existing token" ) ;
2019-03-22 09:21:16 +00:00
// Clear existing token
MXKAccountManager * accountManager = [ MXKAccountManager sharedManager ] ;
[ accountManager setPushDeviceToken : nil withPushOptions : nil ] ;
}
2019-04-05 12:51:56 +00:00
// Remove delivred notifications for a given room id except call notifications
- ( void ) removeDeliveredNotificationsWithRoomId : ( NSString * ) roomId completion : ( dispatch_block _t ) completion
{
2019-04-05 14:03:27 +00:00
NSLog ( @ "[AppDelegate][Push] removeDeliveredNotificationsWithRoomId: Remove potential delivered notifications for room id: %@" , roomId ) ;
2019-04-05 12:51:56 +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 ( ) ;
}
} ] ;
}
2016-04-12 08:23:48 +00:00
# pragma mark - Universal link
- ( BOOL ) handleUniversalLink : ( NSUserActivity * ) userActivity
{
NSURL * webURL = userActivity . webpageURL ;
NSLog ( @ "[AppDelegate] handleUniversalLink: %@" , webURL . absoluteString ) ;
2017-07-14 14:41:25 +00:00
2016-04-20 07:21:46 +00:00
// iOS Patch : fix vector . im urls before using it
2016-04-29 14:19:13 +00:00
webURL = [ Tools fixURLWithSeveralHashKeys : webURL ] ;
2019-09-10 09:51:49 +00:00
if ( [ webURL . path hasPrefix : @ "/config" ] )
{
return [ self handleServerProvionningLink : webURL ] ;
}
2017-07-14 14:41:25 +00:00
2019-08-30 09:15:06 +00:00
NSString * validateEmailSubmitTokenPath = @ "validate/email/submitToken" ;
NSString * validateEmailSubmitTokenAPIPathV1 = [ NSString stringWithFormat : @ "/%@/%@" , kMXIdentityAPIPrefixPathV1 , validateEmailSubmitTokenPath ] ;
NSString * validateEmailSubmitTokenAPIPathV2 = [ NSString stringWithFormat : @ "/%@/%@" , kMXIdentityAPIPrefixPathV2 , validateEmailSubmitTokenPath ] ;
2016-04-22 15:54:57 +00:00
// Manage email validation link
2019-08-30 09:15:06 +00:00
if ( [ webURL . path isEqualToString : validateEmailSubmitTokenAPIPathV1 ] || [ webURL . path isEqualToString : validateEmailSubmitTokenAPIPathV2 ] )
2016-04-22 15:54:57 +00:00
{
// Validate the email on the passed identity server
NSString * identityServer = [ NSString stringWithFormat : @ "%@://%@" , webURL . scheme , webURL . host ] ;
2019-08-30 09:15:06 +00:00
MXSession * mainSession = self . mxSessions . firstObject ;
MXRestClient * homeserverRestClient ;
if ( mainSession . matrixRestClient )
{
homeserverRestClient = mainSession . matrixRestClient ;
}
else
{
homeserverRestClient = [ [ MXRestClient alloc ] initWithHomeServer : identityServer andOnUnrecognizedCertificateBlock : nil ] ;
}
2019-09-05 09:16:10 +00:00
MXIdentityService * identityService = [ [ MXIdentityService alloc ] initWithIdentityServer : identityServer accessToken : nil andHomeserverRestClient : homeserverRestClient ] ;
2017-07-14 14:41:25 +00:00
2016-04-22 15:54:57 +00:00
// Extract required parameters from the link
NSArray < NSString * > * pathParams ;
NSMutableDictionary * queryParams ;
[ self parseUniversalLinkFragment : webURL . absoluteString outPathParams : & pathParams outQueryParams : & queryParams ] ;
2017-07-14 14:41:25 +00:00
2019-09-10 17:34:40 +00:00
NSString * clientSecret = queryParams [ @ "client_secret" ] ;
NSString * sid = queryParams [ @ "sid" ] ;
[ identityService submit3PIDValidationToken : queryParams [ @ "token" ] medium : kMX3PIDMediumEmail clientSecret : clientSecret sid : sid success : ^ {
2017-07-14 14:41:25 +00:00
2016-04-22 15:54:57 +00:00
NSLog ( @ "[AppDelegate] handleUniversalLink. Email successfully validated." ) ;
2017-07-14 14:41:25 +00:00
2016-04-22 15:54:57 +00:00
if ( queryParams [ @ "nextLink" ] )
{
// Continue the registration with the passed nextLink
NSLog ( @ "[AppDelegate] handleUniversalLink. Complete registration with nextLink" ) ;
NSURL * nextLink = [ NSURL URLWithString : queryParams [ @ "nextLink" ] ] ;
[ self handleUniversalLinkFragment : nextLink . fragment ] ;
}
else
{
// No nextLink in Vector world means validation for binding a new email
2019-09-10 17:34:40 +00:00
// Post a notification about email validation to make a chance to SettingsDiscoveryThreePidDetailsViewModel to make it discoverable or not by the identity server .
if ( clientSecret && sid )
{
NSDictionary * userInfo = @ { AppDelegateDidValidateEmailNotificationClientSecretKey : clientSecret ,
AppDelegateDidValidateEmailNotificationSIDKey : sid } ;
[ [ NSNotificationCenter defaultCenter ] postNotificationName : AppDelegateDidValidateEmailNotification object : nil userInfo : userInfo ] ;
}
2016-04-22 15:54:57 +00:00
}
2017-07-14 14:41:25 +00:00
2016-04-22 15:54:57 +00:00
} failure : ^ ( NSError * error ) {
2017-07-14 14:41:25 +00:00
2016-10-21 15:10:02 +00:00
NSLog ( @ "[AppDelegate] handleUniversalLink. Error: submitToken failed" ) ;
2016-04-22 15:54:57 +00:00
[ self showErrorAsAlert : error ] ;
2017-07-14 14:41:25 +00:00
2016-04-22 15:54:57 +00:00
} ] ;
2017-07-14 14:41:25 +00:00
2016-04-22 15:54:57 +00:00
return YES ;
}
2017-07-14 14:41:25 +00:00
2016-04-12 12:19:38 +00:00
return [ self handleUniversalLinkFragment : webURL . fragment ] ;
}
- ( BOOL ) handleUniversalLinkFragment : ( NSString * ) fragment
{
BOOL continueUserActivity = NO ;
2016-04-12 13:30:02 +00:00
MXKAccountManager * accountManager = [ MXKAccountManager sharedManager ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 15:44:06 +00:00
NSLog ( @ "[AppDelegate] Universal link: handleUniversalLinkFragment: %@" , fragment ) ;
2017-07-14 14:41:25 +00:00
2016-04-12 15:44:06 +00:00
// The app manages only one universal link at a time
// Discard any pending one
[ self resetPendingUniversalLink ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
// Extract params
2016-04-12 08:23:48 +00:00
NSArray < NSString * > * pathParams ;
NSMutableDictionary * queryParams ;
2016-04-12 12:19:38 +00:00
[ self parseUniversalLinkFragment : fragment outPathParams : & pathParams outQueryParams : & queryParams ] ;
2017-07-14 14:41:25 +00:00
2016-04-20 07:21:46 +00:00
// Sanity check
if ( ! pathParams . count )
{
NSLog ( @ "[AppDelegate] Universal link: Error: No path parameters" ) ;
return NO ;
}
2017-07-14 14:41:25 +00:00
2016-08-29 09:57:01 +00:00
NSString * roomIdOrAlias ;
NSString * eventId ;
2017-07-25 14:08:24 +00:00
NSString * userId ;
2017-12-31 15:24:47 +00:00
NSString * groupId ;
2017-07-14 14:41:25 +00:00
2016-08-29 09:57:01 +00:00
// Check permalink to room or event
2016-04-12 08:23:48 +00:00
if ( [ pathParams [ 0 ] isEqualToString : @ "room" ] && pathParams . count >= 2 )
{
2016-08-29 09:57:01 +00:00
// The link is the form of "/room/[roomIdOrAlias]" or "/room/[roomIdOrAlias]/[eventId]"
roomIdOrAlias = pathParams [ 1 ] ;
2017-07-14 14:41:25 +00:00
2016-08-29 09:57:01 +00:00
// Is it a link to an event of a room ?
eventId = ( pathParams . count >= 3 ) ? pathParams [ 2 ] : nil ;
}
2017-12-31 15:24:47 +00:00
else if ( [ pathParams [ 0 ] isEqualToString : @ "group" ] && pathParams . count >= 2 )
{
// The link is the form of "/group/[groupId]"
groupId = pathParams [ 1 ] ;
}
2016-08-29 09:57:01 +00:00
else if ( ( [ pathParams [ 0 ] hasPrefix : @ "#" ] || [ pathParams [ 0 ] hasPrefix : @ "!" ] ) && pathParams . count >= 1 )
{
// The link is the form of "/#/[roomIdOrAlias]" or "/#/[roomIdOrAlias]/[eventId]"
// Such links come from matrix . to permalinks
roomIdOrAlias = pathParams [ 0 ] ;
eventId = ( pathParams . count >= 2 ) ? pathParams [ 1 ] : nil ;
}
2017-07-25 14:08:24 +00:00
// Check permalink to a user
else if ( [ pathParams [ 0 ] isEqualToString : @ "user" ] && pathParams . count = = 2 )
{
// The link is the form of "/user/userId"
userId = pathParams [ 1 ] ;
}
else if ( [ pathParams [ 0 ] hasPrefix : @ "@" ] && pathParams . count = = 1 )
{
// The link is the form of "/#/[userId]"
// Such links come from matrix . to permalinks
userId = pathParams [ 0 ] ;
}
2017-02-13 16:58:29 +00:00
// Check the conditions to keep the room alias information of a pending fragment .
if ( universalLinkFragmentPendingRoomAlias )
{
if ( ! roomIdOrAlias || ! universalLinkFragmentPendingRoomAlias [ roomIdOrAlias ] )
{
universalLinkFragmentPendingRoomAlias = nil ;
}
}
2016-08-29 09:57:01 +00:00
if ( roomIdOrAlias )
2017-02-13 16:58:29 +00:00
{
2016-04-12 13:30:02 +00:00
if ( accountManager . activeAccounts . count )
2016-04-12 08:23:48 +00:00
{
2016-04-12 13:30:02 +00:00
// Check there is an account that knows this room
MXKAccount * account = [ accountManager accountKnowingRoomWithRoomIdOrAlias : roomIdOrAlias ] ;
if ( account )
2016-04-12 08:23:48 +00:00
{
2016-04-12 13:30:02 +00:00
NSString * roomId = roomIdOrAlias ;
2017-07-14 14:41:25 +00:00
2016-04-12 13:30:02 +00:00
// Translate the alias into the room id
if ( [ roomIdOrAlias hasPrefix : @ "#" ] )
2016-04-12 08:23:48 +00:00
{
2016-04-12 13:30:02 +00:00
MXRoom * room = [ account . mxSession roomWithAlias : roomIdOrAlias ] ;
if ( room )
{
roomId = room . roomId ;
}
2016-04-12 08:23:48 +00:00
}
2017-07-14 14:41:25 +00:00
2016-04-12 13:30:02 +00:00
// Open the room page
[ self showRoom : roomId andEventId : eventId withMatrixSession : account . mxSession ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 13:30:02 +00:00
continueUserActivity = YES ;
2016-04-12 08:23:48 +00:00
}
2016-04-19 13:18:24 +00:00
else
2016-04-12 13:30:02 +00:00
{
2016-04-19 13:18:24 +00:00
// We will display something but we need to do some requests before .
// So , come back to the home VC and show its loading wheel while processing
[ self restoreInitialDisplay : ^ {
2017-03-23 16:48:05 +00:00
if ( [ _masterTabBarController . selectedViewController isKindOfClass : MXKViewController . class ] )
2016-04-19 13:18:24 +00:00
{
2017-03-23 16:48:05 +00:00
MXKViewController * homeViewController = ( MXKViewController * ) _masterTabBarController . selectedViewController ;
[ homeViewController startActivityIndicator ] ;
if ( [ roomIdOrAlias hasPrefix : @ "#" ] )
{
// The alias may be not part of user ' s rooms states
// Ask the HS to resolve the room alias into a room id and then retry
universalLinkFragmentPending = fragment ;
MXKAccount * account = accountManager . activeAccounts . firstObject ;
[ account . mxSession . matrixRestClient roomIDForRoomAlias : roomIdOrAlias success : ^ ( NSString * roomId ) {
2017-02-13 16:58:29 +00:00
2017-03-23 16:48:05 +00:00
// Note : the activity indicator will not disappear if the session is not ready
[ homeViewController stopActivityIndicator ] ;
2017-02-13 16:58:29 +00:00
2017-03-23 16:48:05 +00:00
// Check that ' fragment ' has not been cancelled
if ( [ universalLinkFragmentPending isEqualToString : fragment ] )
2016-04-19 13:18:24 +00:00
{
2017-03-23 16:48:05 +00:00
// Retry opening the link but with the returned room id
NSString * newUniversalLinkFragment =
2019-02-18 14:31:55 +00:00
[ fragment stringByReplacingOccurrencesOfString : [ MXTools encodeURIComponent : roomIdOrAlias ]
withString : [ MXTools encodeURIComponent : roomId ]
2019-02-04 20:15:47 +00:00
] ;
2017-03-23 16:48:05 +00:00
universalLinkFragmentPendingRoomAlias = @ { roomId : roomIdOrAlias } ;
[ self handleUniversalLinkFragment : newUniversalLinkFragment ] ;
2016-04-19 13:18:24 +00:00
}
2017-03-23 16:48:05 +00:00
} failure : ^ ( NSError * error ) {
2019-04-20 02:17:34 +00:00
NSLog ( @ "[AppDelegate] Universal link: Error: The homeserver failed to resolve the room alias (%@)" , roomIdOrAlias ) ;
2019-01-17 16:46:49 +00:00
[ homeViewController stopActivityIndicator ] ;
NSString * errorMessage = [ NSString stringWithFormat : NSLocalizedStringFromTable ( @ "room_does_not_exist" , @ "Vector" , nil ) , roomIdOrAlias ] ;
[ self showAlertWithTitle : nil message : errorMessage ] ;
2017-03-23 16:48:05 +00:00
} ] ;
}
else if ( [ roomIdOrAlias hasPrefix : @ "!" ] && ( ( MXKAccount * ) accountManager . activeAccounts . firstObject ) . mxSession . state ! = MXSessionStateRunning )
2016-04-19 13:18:24 +00:00
{
2017-03-23 16:48:05 +00:00
// The user does not know the room id but this may be because their session is not yet sync ' ed
// So , wait for the completion of the sync and then retry
// FIXME : Manange all user ' s accounts not only the first one
MXKAccount * account = accountManager . activeAccounts . firstObject ;
NSLog ( @ "[AppDelegate] Universal link: Need to wait for the session to be sync'ed and running" ) ;
universalLinkFragmentPending = fragment ;
universalLinkWaitingObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXSessionStateDidChangeNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * _Nonnull notif ) {
// Check that ' fragment ' has not been cancelled
if ( [ universalLinkFragmentPending isEqualToString : fragment ] )
{
// Check whether the concerned session is the associated one
if ( notif . object = = account . mxSession && account . mxSession . state = = MXSessionStateRunning )
{
NSLog ( @ "[AppDelegate] Universal link: The session is running. Retry the link" ) ;
[ self handleUniversalLinkFragment : fragment ] ;
}
}
} ] ;
2016-04-19 13:18:24 +00:00
}
else
{
2017-03-23 16:48:05 +00:00
NSLog ( @ "[AppDelegate] Universal link: The room (%@) is not known by any account (email invitation: %@). Display its preview to try to join it" , roomIdOrAlias , queryParams ? @ "YES" : @ "NO" ) ;
// FIXME : In case of multi - account , ask the user which one to use
MXKAccount * account = accountManager . activeAccounts . firstObject ;
RoomPreviewData * roomPreviewData ;
if ( queryParams )
{
2016-04-19 13:18:24 +00:00
// Note : the activity indicator will not disappear if the session is not ready
2017-03-23 16:48:05 +00:00
[ homeViewController stopActivityIndicator ] ;
2017-02-13 16:58:29 +00:00
2017-03-23 16:48:05 +00:00
roomPreviewData = [ [ RoomPreviewData alloc ] initWithRoomId : roomIdOrAlias emailInvitationParams : queryParams andSession : account . mxSession ] ;
2019-07-08 09:15:44 +00:00
roomPreviewData . viaServers = queryParams [ @ "via" ] ;
2016-04-19 13:18:24 +00:00
[ self showRoomPreview : roomPreviewData ] ;
2017-03-23 16:48:05 +00:00
}
else
{
roomPreviewData = [ [ RoomPreviewData alloc ] initWithRoomId : roomIdOrAlias andSession : account . mxSession ] ;
// Is it a link to an event of a room ?
// If yes , the event will be displayed once the room is joined
roomPreviewData . eventId = ( pathParams . count >= 3 ) ? pathParams [ 2 ] : nil ;
// Try to get more information about the room before opening its preview
[ roomPreviewData peekInRoom : ^ ( BOOL succeeded ) {
// Note : the activity indicator will not disappear if the session is not ready
[ homeViewController stopActivityIndicator ] ;
// If no data is available for this room , we name it with the known room alias ( if any ) .
if ( ! succeeded && universalLinkFragmentPendingRoomAlias [ roomIdOrAlias ] )
{
roomPreviewData . roomName = universalLinkFragmentPendingRoomAlias [ roomIdOrAlias ] ;
}
universalLinkFragmentPendingRoomAlias = nil ;
[ self showRoomPreview : roomPreviewData ] ;
} ] ;
}
2016-04-12 15:44:06 +00:00
}
2017-07-14 14:41:25 +00:00
2016-04-12 13:30:02 +00:00
}
} ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 13:56:17 +00:00
// Let ' s say we are handling the case
continueUserActivity = YES ;
2016-04-12 13:30:02 +00:00
}
2016-04-12 12:19:38 +00:00
}
2016-04-12 08:23:48 +00:00
else
{
2016-04-12 13:56:17 +00:00
// There is no account . The app will display the AuthenticationVC .
// Wait for a successful login
NSLog ( @ "[AppDelegate] Universal link: The user is not logged in. Wait for a successful login" ) ;
2016-04-12 15:44:06 +00:00
universalLinkFragmentPending = fragment ;
2017-07-14 14:41:25 +00:00
2016-04-12 13:56:17 +00:00
// Register an observer in order to handle new account
2016-04-12 15:44:06 +00:00
universalLinkWaitingObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXKAccountManagerDidAddAccountNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
2017-07-14 14:41:25 +00:00
2016-04-12 15:44:06 +00:00
// Check that ' fragment ' has not been cancelled
if ( [ universalLinkFragmentPending isEqualToString : fragment ] )
{
NSLog ( @ "[AppDelegate] Universal link: The user is now logged in. Retry the link" ) ;
[ self handleUniversalLinkFragment : fragment ] ;
}
2016-04-12 13:56:17 +00:00
} ] ;
2016-04-12 08:23:48 +00:00
}
}
2017-07-25 14:08:24 +00:00
else if ( userId )
{
// Check there is an account that knows this user
MXUser * mxUser ;
MXKAccount * account = [ accountManager accountKnowingUserWithUserId : userId ] ;
if ( account )
{
mxUser = [ account . mxSession userWithUserId : userId ] ;
}
// Prepare the display name of this user
NSString * displayName ;
if ( mxUser )
{
displayName = ( mxUser . displayname . length > 0 ) ? mxUser . displayname : userId ;
}
else
{
displayName = userId ;
}
// Create the contact related to this member
MXKContact * contact = [ [ MXKContact alloc ] initMatrixContactWithDisplayName : displayName andMatrixID : userId ] ;
[ self showContact : contact ] ;
continueUserActivity = YES ;
}
2017-12-31 15:24:47 +00:00
else if ( groupId )
{
// @ FIXME : In case of multi - account , ask the user which one to use
MXKAccount * account = accountManager . activeAccounts . firstObject ;
if ( account )
{
MXGroup * group = [ account . mxSession groupWithGroupId : groupId ] ;
if ( ! group )
{
// Create a group instance to display its preview
group = [ [ MXGroup alloc ] initWithGroupId : groupId ] ;
}
// Display the group details
[ self showGroup : group withMatrixSession : account . mxSession ] ;
continueUserActivity = YES ;
}
else
{
// There is no account . The app will display the AuthenticationVC .
// Wait for a successful login
NSLog ( @ "[AppDelegate] Universal link: The user is not logged in. Wait for a successful login" ) ;
universalLinkFragmentPending = fragment ;
// Register an observer in order to handle new account
universalLinkWaitingObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXKAccountManagerDidAddAccountNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
// Check that ' fragment ' has not been cancelled
if ( [ universalLinkFragmentPending isEqualToString : fragment ] )
{
NSLog ( @ "[AppDelegate] Universal link: The user is now logged in. Retry the link" ) ;
[ self handleUniversalLinkFragment : fragment ] ;
}
} ] ;
}
}
2018-04-17 19:39:31 +00:00
// Check whether this is a registration links .
else if ( [ pathParams [ 0 ] isEqualToString : @ "register" ] )
2016-04-13 15:56:46 +00:00
{
2016-04-22 16:42:14 +00:00
NSLog ( @ "[AppDelegate] Universal link with registration parameters" ) ;
2016-04-21 14:19:16 +00:00
continueUserActivity = YES ;
2016-04-22 16:42:14 +00:00
2017-03-23 16:48:05 +00:00
[ _masterTabBarController showAuthenticationScreenWithRegistrationParameters : queryParams ] ;
2016-04-13 15:56:46 +00:00
}
2016-04-12 08:23:48 +00:00
else
{
2016-04-13 15:56:46 +00:00
// Unknown command : Do nothing except coming back to the main screen
2016-04-12 13:30:02 +00:00
NSLog ( @ "[AppDelegate] Universal link: TODO: Do not know what to do with the link arguments: %@" , pathParams ) ;
2017-07-14 14:41:25 +00:00
2016-04-13 15:56:46 +00:00
[ self popToHomeViewControllerAnimated : NO completion : nil ] ;
2016-04-12 08:23:48 +00:00
}
2017-07-14 14:41:25 +00:00
2016-04-12 08:23:48 +00:00
return continueUserActivity ;
}
2016-04-12 15:44:06 +00:00
- ( void ) resetPendingUniversalLink
{
universalLinkFragmentPending = nil ;
if ( universalLinkWaitingObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : universalLinkWaitingObserver ] ;
universalLinkWaitingObserver = nil ;
}
}
2016-04-12 09:19:50 +00:00
/ * *
2016-04-12 13:30:02 +00:00
Extract params from the URL fragment part ( after ' # ' ) of a vector . im Universal link :
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
The fragment can contain a ' ? ' . So there are two kinds of parameters : path params and query params .
It is in the form of / [ pathParam1 ] / [ pathParam2 ] ? [ queryParam1Key ] = [ queryParam1Value ] & [ queryParam2Key ] = [ queryParam2Value ]
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
@ param fragment the fragment to parse .
@ param outPathParams the decoded path params .
@ param outQueryParams the decoded query params . If there is no query params , it will be nil .
* /
- ( void ) parseUniversalLinkFragment : ( NSString * ) fragment outPathParams : ( NSArray < NSString * > * * ) outPathParams outQueryParams : ( NSMutableDictionary * * ) outQueryParams
{
NSParameterAssert ( outPathParams && outQueryParams ) ;
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
NSArray < NSString * > * pathParams ;
NSMutableDictionary * queryParams ;
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
NSArray < NSString * > * fragments = [ fragment componentsSeparatedByString : @ "?" ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
// Extract path params
pathParams = [ fragments [ 0 ] componentsSeparatedByString : @ "/" ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
// Remove the first empty path param string
pathParams = [ pathParams filteredArrayUsingPredicate : [ NSPredicate predicateWithFormat : @ "length > 0" ] ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
// URL decode each path param
NSMutableArray < NSString * > * pathParams2 = [ NSMutableArray arrayWithArray : pathParams ] ;
for ( NSInteger i = 0 ; i < pathParams . count ; i + + )
{
2019-02-04 20:15:47 +00:00
pathParams2 [ i ] = [ pathParams2 [ i ] stringByRemovingPercentEncoding ] ;
2016-04-12 09:19:50 +00:00
}
pathParams = pathParams2 ;
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
// Extract query params if any
2016-05-16 14:03:56 +00:00
// Query params are in the form [ queryParam1Key ] = [ queryParam1Value ] , so the
// presence of at least one ' = ' character is mandatory
2016-05-16 14:01:28 +00:00
if ( fragments . count = = 2 && ( NSNotFound ! = [ fragments [ 1 ] rangeOfString : @ "=" ] . location ) )
2016-04-12 09:19:50 +00:00
{
queryParams = [ [ NSMutableDictionary alloc ] init ] ;
for ( NSString * keyValue in [ fragments [ 1 ] componentsSeparatedByString : @ "&" ] )
{
// Get the parameter name
2019-01-07 23:24:11 +00:00
NSString * key = [ keyValue componentsSeparatedByString : @ "=" ] [ 0 ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
// Get the parameter value
2019-01-07 23:24:11 +00:00
NSString * value = [ keyValue componentsSeparatedByString : @ "=" ] [ 1 ] ;
2016-04-13 15:10:35 +00:00
if ( value . length )
{
value = [ value stringByReplacingOccurrencesOfString : @ "+" withString : @ " " ] ;
2019-02-04 20:15:47 +00:00
value = [ value stringByRemovingPercentEncoding ] ;
2019-07-08 09:15:44 +00:00
if ( [ key isEqualToString : @ "via" ] )
{
// Special case the via parameter
// As we can have several of them , store each value into an array
if ( ! queryParams [ key ] )
{
queryParams [ key ] = [ NSMutableArray array ] ;
}
[ queryParams [ key ] addObject : value ] ;
}
else
{
queryParams [ key ] = value ;
}
2016-04-13 15:10:35 +00:00
}
2016-04-12 09:19:50 +00:00
}
}
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
* outPathParams = pathParams ;
* outQueryParams = queryParams ;
}
2019-09-10 09:51:49 +00:00
- ( BOOL ) handleServerProvionningLink : ( NSURL * ) link
{
NSLog ( @ "[AppDelegate] handleServerProvionningLink: %@" , link ) ;
NSString * homeserver , * identityServer ;
[ self parseServerProvionningLink : link homeserver : & homeserver identityServer : & identityServer ] ;
if ( homeserver )
{
if ( [ MXKAccountManager sharedManager ] . activeAccounts . count )
{
[ self displayServerProvionningLinkBuyAlreadyLoggedInAlertWithCompletion : ^ ( BOOL logout ) {
NSLog ( @ "[AppDelegate] handleServerProvionningLink: logoutWithConfirmation: logout: %@" , @ ( logout ) ) ;
if ( logout )
{
[ self logoutWithConfirmation : NO completion : ^ ( BOOL isLoggedOut ) {
[ self handleServerProvionningLink : link ] ;
} ] ;
}
} ] ;
}
else
{
[ _masterTabBarController showAuthenticationScreen ] ;
[ _masterTabBarController . authViewController showCustomHomeserver : homeserver andIdentityServer : identityServer ] ;
}
return YES ;
}
return NO ;
}
- ( void ) parseServerProvionningLink : ( NSURL * ) link homeserver : ( NSString * * ) homeserver identityServer : ( NSString * * ) identityServer
{
if ( [ link . path isEqualToString : @ "/config/config" ] )
{
NSURLComponents * linkURLComponents = [ NSURLComponents componentsWithURL : link resolvingAgainstBaseURL : NO ] ;
for ( NSURLQueryItem * item in linkURLComponents . queryItems )
{
if ( [ item . name isEqualToString : @ "hs_url" ] )
{
* homeserver = item . value ;
}
else if ( [ item . name isEqualToString : @ "is_url" ] )
{
* identityServer = item . value ;
break ;
}
}
}
else
{
NSLog ( @ "[AppDelegate] parseServerProvionningLink: Error: Unknown path: %@" , link . path ) ;
}
NSLog ( @ "[AppDelegate] parseServerProvionningLink: homeserver: %@ - identityServer: %@" , * homeserver , * identityServer ) ;
}
- ( void ) displayServerProvionningLinkBuyAlreadyLoggedInAlertWithCompletion : ( void ( ^ ) ( BOOL logout ) ) completion
{
// Ask confirmation
self . logoutConfirmation = [ UIAlertController alertControllerWithTitle : NSLocalizedStringFromTable ( @ "error_user_already_logged_in" , @ "Vector" , nil ) message : nil preferredStyle : UIAlertControllerStyleAlert ] ;
[ self . logoutConfirmation addAction : [ UIAlertAction actionWithTitle : NSLocalizedStringFromTable ( @ "settings_sign_out" , @ "Vector" , nil )
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action )
{
self . logoutConfirmation = nil ;
completion ( YES ) ;
} ] ] ;
[ self . logoutConfirmation addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "cancel" ]
style : UIAlertActionStyleCancel
handler : ^ ( UIAlertAction * action )
{
self . logoutConfirmation = nil ;
completion ( NO ) ;
} ] ] ;
[ self . logoutConfirmation mxk_setAccessibilityIdentifier : @ "AppDelegateLogoutConfirmationAlert" ] ;
[ self showNotificationAlert : self . logoutConfirmation ] ;
}
2015-04-30 14:19:12 +00:00
# pragma mark - Matrix sessions handling
2015-06-18 09:19:42 +00:00
- ( void ) initMatrixSessions
{
2017-02-20 17:34:41 +00:00
NSLog ( @ "[AppDelegate] initMatrixSessions" ) ;
2017-07-14 14:41:25 +00:00
2017-06-14 15:37:22 +00:00
MXSDKOptions * sdkOptions = [ MXSDKOptions sharedInstance ] ;
2017-02-20 17:34:41 +00:00
2017-03-23 10:49:58 +00:00
// Define the media cache version
2017-06-14 15:37:22 +00:00
sdkOptions . mediaCacheAppVersion = 0 ;
2017-03-23 10:49:58 +00:00
2016-11-16 12:40:04 +00:00
// Enable e2e encryption for newly created MXSession
2017-06-14 15:37:22 +00:00
sdkOptions . enableCryptoWhenStartingMXSession = YES ;
2017-07-14 14:41:25 +00:00
2015-12-11 12:51:48 +00:00
// Disable identicon use
2017-06-14 15:37:22 +00:00
sdkOptions . disableIdenticonUseForUserAvatar = YES ;
2017-07-14 14:41:25 +00:00
2017-06-14 15:37:22 +00:00
// Use UIKit BackgroundTask for handling background tasks in the SDK
2017-06-16 12:34:52 +00:00
sdkOptions . backgroundModeHandler = [ [ MXUIKitBackgroundModeHandler alloc ] init ] ;
2017-08-30 15:37:14 +00:00
// Get modular widget events in rooms histories
2018-05-07 15:58:18 +00:00
[ [ MXKAppSettings standardAppSettings ] addSupportedEventTypes : @ [ kWidgetMatrixEventTypeString , kWidgetModularEventTypeString ] ] ;
2015-12-11 12:51:48 +00:00
2019-05-07 16:24:05 +00:00
// Enable long press on event in bubble cells
[ MXKRoomBubbleTableViewCell disableLongPressGestureOnEvent : NO ] ;
2016-02-11 14:19:49 +00:00
2015-08-21 18:00:39 +00:00
// Set first RoomDataSource class used in Vector
[ MXKRoomDataSourceManager registerRoomDataSourceClass : RoomDataSource . class ] ;
2015-05-21 16:15:45 +00:00
// Register matrix session state observer in order to handle multi - sessions .
2017-07-14 14:41:25 +00:00
matrixSessionStateObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXSessionStateDidChangeNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
2015-04-30 14:19:12 +00:00
MXSession * mxSession = ( MXSession * ) notif . object ;
// Check whether the concerned session is a new one
2015-06-18 09:19:42 +00:00
if ( mxSession . state = = MXSessionStateInitialised )
{
2015-11-20 13:47:38 +00:00
// Store this new session
2015-11-17 23:15:52 +00:00
[ self addMatrixSession : mxSession ] ;
2015-12-18 10:55:35 +00:00
2016-03-23 13:45:16 +00:00
// Set the VoIP call stack ( if supported ) .
id < MXCallStack > callStack ;
# ifdef MX_CALL _STACK _OPENWEBRTC
callStack = [ [ MXOpenWebRTCCallStack alloc ] init ] ;
# endif
# ifdef MX_CALL _STACK _ENDPOINT
callStack = [ [ MXEndpointCallStack alloc ] initWithMatrixId : mxSession . myUser . userId ] ;
2016-07-22 16:26:55 +00:00
# endif
2017-11-24 15:28:48 +00:00
# ifdef CALL_STACK _JINGLE
2016-07-22 16:26:55 +00:00
callStack = [ [ MXJingleCallStack alloc ] init ] ;
2016-03-23 13:45:16 +00:00
# endif
if ( callStack )
{
[ mxSession enableVoIPWithCallStack : callStack ] ;
2017-06-20 20:16:54 +00:00
2017-12-20 10:27:11 +00:00
// Let ' s call invite be valid for 1 minute
mxSession . callManager . inviteLifetime = 60000 ;
2019-08-28 15:04:49 +00:00
if ( RiotSettings . shared . allowStunServerFallback )
{
mxSession . callManager . fallbackSTUNServer = RiotSettings . shared . stunServerFallback ;
}
2017-06-20 20:16:54 +00:00
// Setup CallKit
if ( [ MXCallKitAdapter callKitAvailable ] )
{
BOOL isCallKitEnabled = [ MXKAppSettings standardAppSettings ] . isCallKitEnabled ;
[ self enableCallKit : isCallKitEnabled forCallManager : mxSession . callManager ] ;
// Register for changes performed by the user
[ [ MXKAppSettings standardAppSettings ] addObserver : self
forKeyPath : @ "enableCallKit"
options : NSKeyValueObservingOptionNew
context : NULL ] ;
}
2019-04-19 16:33:21 +00:00
else
{
[ self enableCallKit : NO forCallManager : mxSession . callManager ] ;
}
2016-03-23 13:45:16 +00:00
}
2016-05-20 13:30:10 +00:00
else
{
// When there is no call stack , display alerts on call invites
[ self enableNoVoIPOnMatrixSession : mxSession ] ;
}
2017-07-14 14:41:25 +00:00
2015-12-22 07:43:55 +00:00
// Each room member will be considered as a potential contact .
[ MXKContactManager sharedManager ] . contactManagerMXRoomSource = MXKContactManagerMXRoomSourceAll ;
2017-08-30 15:37:14 +00:00
2018-05-07 15:58:18 +00:00
// Send read receipts for widgets events too
2017-08-30 15:37:14 +00:00
NSMutableArray < MXEventTypeString > * acknowledgableEventTypes = [ NSMutableArray arrayWithArray : mxSession . acknowledgableEventTypes ] ;
2018-05-07 15:58:18 +00:00
[ acknowledgableEventTypes addObject : kWidgetMatrixEventTypeString ] ;
[ acknowledgableEventTypes addObject : kWidgetModularEventTypeString ] ;
2017-08-30 15:37:14 +00:00
mxSession . acknowledgableEventTypes = acknowledgableEventTypes ;
2015-06-18 13:20:33 +00:00
}
else if ( mxSession . state = = MXSessionStateStoreDataReady )
2015-06-18 09:19:42 +00:00
{
2017-09-07 17:28:38 +00:00
// A new call observer may be added here
[ self addMatrixCallObserver ] ;
2017-10-18 17:18:14 +00:00
// Enable local notifications
[ self enableLocalNotificationsFromMatrixSession : mxSession ] ;
2017-09-08 13:32:55 +00:00
// Look for the account related to this session .
2015-06-19 08:31:27 +00:00
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
2015-06-18 13:20:33 +00:00
for ( MXKAccount * account in mxAccounts )
2015-06-18 09:19:42 +00:00
{
2015-06-18 13:20:33 +00:00
if ( account . mxSession = = mxSession )
{
2017-09-08 13:32:55 +00:00
// Enable inApp notifications ( if they are allowed for this account ) .
2015-06-18 13:20:33 +00:00
[ self enableInAppNotificationsForAccount : account ] ;
break ;
}
2015-04-30 14:19:12 +00:00
}
2015-06-18 13:20:33 +00:00
}
else if ( mxSession . state = = MXSessionStateClosed )
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
[ self removeMatrixSession : mxSession ] ;
2015-05-21 16:15:45 +00:00
}
2017-09-06 14:59:12 +00:00
// Consider here the case where the app is running in background .
else if ( [ [ UIApplication sharedApplication ] applicationState ] = = UIApplicationStateBackground )
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] MXSession state changed while in background. mxSession.state: %tu - incomingPushEventIds: %@" , mxSession . state , self . incomingPushEventIds [ @ ( mxSession . hash ) ] ) ;
2017-09-06 14:59:12 +00:00
if ( mxSession . state = = MXSessionStateRunning )
{
// Pause the session in background task
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
for ( MXKAccount * account in mxAccounts )
{
if ( account . mxSession = = mxSession )
{
[ account pauseInBackgroundTask ] ;
2017-10-19 14:09:50 +00:00
// Trigger local notifcations ( Indeed the app finishs here an initial sync in background , the user has missed some notifcations )
[ self handleLocalNotificationsForAccount : account ] ;
// Update app icon badge number
[ self refreshApplicationIconBadgeNumber ] ;
2017-09-06 14:59:12 +00:00
break ;
}
}
}
else if ( mxSession . state = = MXSessionStatePaused )
{
2017-10-23 14:58:55 +00:00
// Check whether some push notifications are pending for this session .
if ( self . incomingPushEventIds [ @ ( mxSession . hash ) ] . count )
2017-09-06 14:59:12 +00:00
{
2017-12-29 09:31:16 +00:00
NSLog ( @ "[AppDelegate][Push] relaunch a background sync for %tu kMXSessionStateDidChangeNotification pending incoming pushes" , self . incomingPushEventIds [ @ ( mxSession . hash ) ] . count ) ;
2017-10-19 14:09:50 +00:00
[ self launchBackgroundSync ] ;
2017-09-06 14:59:12 +00:00
}
}
2017-12-29 09:31:16 +00:00
else if ( mxSession . state = = MXSessionStateInitialSyncFailed )
{
// Display failure sync notifications for pending events if any
if ( self . incomingPushEventIds [ @ ( mxSession . hash ) ] . count )
{
NSLog ( @ "[AppDelegate][Push] initial sync failed with %tu pending incoming pushes" , self . incomingPushEventIds [ @ ( mxSession . hash ) ] . count ) ;
2017-12-29 12:40:55 +00:00
// Trigger limited local notifications when the sync with HS fails
2017-12-29 12:22:29 +00:00
[ self handleLimitedLocalNotifications : mxSession events : self . incomingPushEventIds [ @ ( mxSession . hash ) ] ] ;
2017-12-29 09:31:16 +00:00
// Update app icon badge number
[ self refreshApplicationIconBadgeNumber ] ;
}
}
2017-09-06 14:59:12 +00:00
}
2017-11-16 13:46:47 +00:00
else if ( [ [ UIApplication sharedApplication ] applicationState ] = = UIApplicationStateActive )
{
if ( mxSession . state = = MXSessionStateRunning )
{
// Check if we need to display a key share dialog
[ self checkPendingRoomKeyRequests ] ;
2019-04-18 21:15:01 +00:00
[ self checkPendingIncomingDeviceVerificationsInSession : mxSession ] ;
2017-11-16 13:46:47 +00:00
}
}
2015-05-21 16:15:45 +00:00
2017-01-31 23:18:01 +00:00
[ self handleLaunchAnimation ] ;
2015-04-30 14:19:12 +00:00
} ] ;
// Register an observer in order to handle new account
2015-06-18 13:20:33 +00:00
addedAccountObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXKAccountManagerDidAddAccountNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
2015-04-30 14:19:12 +00:00
2016-03-23 13:45:16 +00:00
// Finalize the initialization of this new account
2015-06-18 13:20:33 +00:00
MXKAccount * account = notif . object ;
2015-06-18 09:19:42 +00:00
if ( account )
{
2017-03-17 15:46:44 +00:00
// Replace default room summary updater
EventFormatter * eventFormatter = [ [ EventFormatter alloc ] initWithMatrixSession : account . mxSession ] ;
eventFormatter . isForSubtitle = YES ;
account . mxSession . roomSummaryUpdateDelegate = eventFormatter ;
2017-07-14 14:41:25 +00:00
2016-03-21 13:49:15 +00:00
// Set the push gateway URL .
account . pushGatewayURL = [ [ NSUserDefaults standardUserDefaults ] objectForKey : @ "pushGatewayURL" ] ;
2019-04-02 07:35:02 +00:00
NSLog ( @ "[AppDelegate][Push] didAddAccountNotification: isPushRegistered: %@" , @ ( isPushRegistered ) ) ;
2017-09-19 14:06:44 +00:00
if ( isPushRegistered )
2015-06-19 08:31:27 +00:00
{
// Enable push notifications by default on new added account
2019-07-16 12:42:25 +00:00
[ account enablePushKitNotifications : YES success : nil failure : nil ] ;
2015-06-19 08:31:27 +00:00
}
else
{
// Set up push notifications
[ self registerUserNotificationSettings ] ;
}
2015-04-30 14:19:12 +00:00
2015-06-18 13:20:33 +00:00
// Observe inApp notifications toggle change
[ account addObserver : self forKeyPath : @ "enableInAppNotifications" options : 0 context : nil ] ;
2015-04-30 14:19:12 +00:00
}
2017-01-17 22:13:30 +00:00
// Load the local contacts on first account creation .
if ( [ MXKAccountManager sharedManager ] . accounts . count = = 1 )
{
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self refreshLocalContacts ] ;
} ) ;
}
2015-05-11 21:11:23 +00:00
} ] ;
2015-06-18 13:20:33 +00:00
// Add observer to handle removed accounts
removedAccountObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXKAccountManagerDidRemoveAccountNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
// Remove inApp notifications toggle change
MXKAccount * account = notif . object ;
2019-07-23 14:20:07 +00:00
if ( ! account . isSoftLogout )
{
[ account removeObserver : self forKeyPath : @ "enableInAppNotifications" ] ;
}
2017-09-15 13:35:27 +00:00
// Clear Modular data
[ [ WidgetManager sharedManager ] deleteDataForUser : account . mxCredentials . userId ] ;
2015-06-18 13:20:33 +00:00
// Logout the app when there is no available account
if ( ! [ MXKAccountManager sharedManager ] . accounts . count )
{
2018-04-17 19:39:31 +00:00
[ self logoutWithConfirmation : NO completion : nil ] ;
2015-06-18 13:20:33 +00:00
}
} ] ;
2019-07-19 12:25:45 +00:00
// Add observer to handle soft logout
[ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXKAccountManagerDidSoftlogoutAccountNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
MXKAccount * account = notif . object ;
[ self removeMatrixSession : account . mxSession ] ;
// Return to authentication screen
[ self . masterTabBarController showAuthenticationScreenAfterSoftLogout : account . mxCredentials ] ;
} ] ;
2017-07-14 14:41:25 +00:00
2016-05-04 16:35:59 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXSessionIgnoredUsersDidChangeNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * _Nonnull notif ) {
2017-07-14 14:41:25 +00:00
2016-05-04 16:35:59 +00:00
NSLog ( @ "[AppDelegate] kMXSessionIgnoredUsersDidChangeNotification received. Reload the app" ) ;
2017-07-14 14:41:25 +00:00
2016-05-04 16:35:59 +00:00
// Reload entirely the app when a user has been ignored or unignored
[ [ AppDelegate theDelegate ] reloadMatrixSessions : YES ] ;
2017-07-14 14:41:25 +00:00
2016-05-04 16:35:59 +00:00
} ] ;
2015-06-18 13:20:33 +00:00
2016-08-12 14:50:27 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXSessionDidCorruptDataNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * _Nonnull notif ) {
NSLog ( @ "[AppDelegate] kMXSessionDidCorruptDataNotification received. Reload the app" ) ;
// Reload entirely the app when a session has corrupted its data
[ [ AppDelegate theDelegate ] reloadMatrixSessions : YES ] ;
} ] ;
2017-08-24 13:18:23 +00:00
// Add observer on settings changes .
2017-08-16 16:35:32 +00:00
[ [ MXKAppSettings standardAppSettings ] addObserver : self forKeyPath : @ "showAllEventsInRoomHistory" options : 0 context : nil ] ;
2015-04-30 14:19:12 +00:00
2015-07-21 09:30:37 +00:00
// Prepare account manager
MXKAccountManager * accountManager = [ MXKAccountManager sharedManager ] ;
// Use MXFileStore as MXStore to permanently store events .
accountManager . storeClass = [ MXFileStore class ] ;
2017-07-14 14:41:25 +00:00
2017-09-19 14:06:44 +00:00
// Disable APNS use .
if ( accountManager . apnsDeviceToken )
{
// We use now Pushkit , unregister for all remote notifications received via Apple Push Notification service .
[ [ UIApplication sharedApplication ] unregisterForRemoteNotifications ] ;
[ accountManager setApnsDeviceToken : nil ] ;
}
2016-03-23 13:45:16 +00:00
// Observers have been defined , we can start a matrix session for each enabled accounts .
2017-09-06 14:59:12 +00:00
NSLog ( @ "[AppDelegate] initMatrixSessions: prepareSessionForActiveAccounts (app state: %tu)" , [ [ UIApplication sharedApplication ] applicationState ] ) ;
[ accountManager prepareSessionForActiveAccounts ] ;
2015-06-19 08:31:27 +00:00
// Check whether we ' re already logged in
2017-10-04 11:04:21 +00:00
NSArray * mxAccounts = accountManager . activeAccounts ;
2015-06-18 09:19:42 +00:00
if ( mxAccounts . count )
{
2016-03-21 13:49:15 +00:00
for ( MXKAccount * account in mxAccounts )
{
2017-03-17 15:46:44 +00:00
// Replace default room summary updater
EventFormatter * eventFormatter = [ [ EventFormatter alloc ] initWithMatrixSession : account . mxSession ] ;
eventFormatter . isForSubtitle = YES ;
account . mxSession . roomSummaryUpdateDelegate = eventFormatter ;
2017-07-14 14:41:25 +00:00
2017-03-17 15:46:44 +00:00
// The push gateway url is now configurable .
// Set this url in the existing accounts when it is undefined .
2016-03-21 13:49:15 +00:00
if ( ! account . pushGatewayURL )
{
account . pushGatewayURL = [ [ NSUserDefaults standardUserDefaults ] objectForKey : @ "pushGatewayURL" ] ;
}
}
2015-06-19 08:31:27 +00:00
// Set up push notifications
2015-04-30 14:19:12 +00:00
[ self registerUserNotificationSettings ] ;
2015-06-19 08:31:27 +00:00
// Observe inApp notifications toggle change for each account
2015-06-18 09:19:42 +00:00
for ( MXKAccount * account in mxAccounts )
{
2015-06-18 13:20:33 +00:00
[ account addObserver : self forKeyPath : @ "enableInAppNotifications" options : 0 context : nil ] ;
2015-05-21 16:15:45 +00:00
}
2015-04-30 14:19:12 +00:00
}
}
2015-11-17 23:15:52 +00:00
- ( NSArray * ) mxSessions
{
return [ NSArray arrayWithArray : mxSessionArray ] ;
}
- ( void ) addMatrixSession : ( MXSession * ) mxSession
{
if ( mxSession )
{
2015-11-20 13:47:38 +00:00
// Report this session to contact manager
2018-03-08 17:23:52 +00:00
// But wait a bit that our launch animation screen is ready to show and
// displayed if needed . As the processing in MXKContactManager can lock
// the UI thread for several seconds , it is better to show the animation
// during this blocking task .
dispatch_after ( dispatch_walltime ( DISPATCH_TIME _NOW , 0.3 * NSEC_PER _SEC ) , dispatch_get _main _queue ( ) , ^ {
[ [ MXKContactManager sharedManager ] addMatrixSession : mxSession ] ;
2019-10-10 13:05:39 +00:00
// Load the local contacts on first account
if ( [ MXKAccountManager sharedManager ] . accounts . count = = 1 )
{
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self refreshLocalContacts ] ;
} ) ;
}
2018-03-08 17:23:52 +00:00
} ) ;
2015-11-20 13:47:38 +00:00
2016-06-14 09:10:37 +00:00
// Update home data sources
2017-03-23 16:48:05 +00:00
[ _masterTabBarController addMatrixSession : mxSession ] ;
2017-08-04 11:05:07 +00:00
// Register the session to the widgets manager
[ [ WidgetManager sharedManager ] addMatrixSession : mxSession ] ;
2015-11-17 23:15:52 +00:00
[ mxSessionArray addObject : mxSession ] ;
2017-03-23 16:48:05 +00:00
// Do the one time check on device id
[ self checkDeviceId : mxSession ] ;
2017-10-23 14:58:55 +00:00
// Add an array to handle incoming push
self . incomingPushEventIds [ @ ( mxSession . hash ) ] = [ NSMutableArray array ] ;
2017-11-14 17:21:01 +00:00
// Enable listening of incoming key share requests
[ self enableRoomKeyRequestObserver : mxSession ] ;
2019-04-12 21:19:07 +00:00
// Enable listening of incoming device verification requests
[ self enableIncomingDeviceVerificationObserver : mxSession ] ;
2015-11-17 23:15:52 +00:00
}
}
- ( void ) removeMatrixSession : ( MXSession * ) mxSession
{
2015-11-20 13:47:38 +00:00
[ [ MXKContactManager sharedManager ] removeMatrixSession : mxSession ] ;
2015-12-09 17:12:42 +00:00
// Update home data sources
2017-03-23 16:48:05 +00:00
[ _masterTabBarController removeMatrixSession : mxSession ] ;
2016-05-20 12:31:23 +00:00
2017-08-04 11:05:07 +00:00
// Update the widgets manager
[ [ WidgetManager sharedManager ] removeMatrixSession : mxSession ] ;
2017-07-14 14:41:25 +00:00
2016-05-20 13:30:10 +00:00
// If any , disable the no VoIP support workaround
2016-05-20 12:31:23 +00:00
[ self disableNoVoIPOnMatrixSession : mxSession ] ;
2015-11-17 23:15:52 +00:00
2017-10-18 17:18:14 +00:00
// Disable local notifications from this session
[ self disableLocalNotificationsFromMatrixSession : mxSession ] ;
2017-11-14 17:21:01 +00:00
// Disable listening of incoming key share requests
[ self disableRoomKeyRequestObserver : mxSession ] ;
2019-04-12 21:19:07 +00:00
// Disable listening of incoming device verification requests
[ self disableIncomingDeviceVerificationObserver : mxSession ] ;
2017-10-18 17:18:14 +00:00
2015-11-17 23:15:52 +00:00
[ mxSessionArray removeObject : mxSession ] ;
2017-09-07 17:28:38 +00:00
if ( ! mxSessionArray . count && matrixCallObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : matrixCallObserver ] ;
matrixCallObserver = nil ;
}
2017-10-23 14:58:55 +00:00
[ self . incomingPushEventIds removeObjectForKey : @ ( mxSession . hash ) ] ;
2015-11-17 23:15:52 +00:00
}
2016-08-26 14:12:46 +00:00
- ( void ) markAllMessagesAsRead
{
2017-04-18 14:20:55 +00:00
for ( MXSession * session in mxSessionArray )
{
[ session markAllMessagesAsRead ] ;
}
2016-08-26 14:12:46 +00:00
}
2015-06-18 09:19:42 +00:00
- ( void ) reloadMatrixSessions : ( BOOL ) clearCache
{
2015-04-30 14:19:12 +00:00
// Reload all running matrix sessions
2015-06-19 08:31:27 +00:00
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
2015-06-18 09:19:42 +00:00
for ( MXKAccount * account in mxAccounts )
{
2015-06-19 08:31:27 +00:00
[ account reload : clearCache ] ;
2017-07-14 14:41:25 +00:00
2017-03-27 08:02:26 +00:00
// Replace default room summary updater
EventFormatter * eventFormatter = [ [ EventFormatter alloc ] initWithMatrixSession : account . mxSession ] ;
eventFormatter . isForSubtitle = YES ;
account . mxSession . roomSummaryUpdateDelegate = eventFormatter ;
2015-04-30 14:19:12 +00:00
}
// Force back to Recents list if room details is displayed ( Room details are not available until the end of initial sync )
2016-04-13 15:08:02 +00:00
[ self popToHomeViewControllerAnimated : NO completion : nil ] ;
2015-04-30 14:19:12 +00:00
2015-06-18 09:19:42 +00:00
if ( clearCache )
{
2015-04-30 14:19:12 +00:00
// clear the media cache
2016-12-01 13:55:29 +00:00
[ MXMediaManager clearCache ] ;
2015-04-30 14:19:12 +00:00
}
}
2014-10-08 16:55:26 +00:00
2018-04-17 19:39:31 +00:00
- ( void ) logoutWithConfirmation : ( BOOL ) askConfirmation completion : ( void ( ^ ) ( BOOL isLoggedOut ) ) completion
2015-06-18 09:19:42 +00:00
{
2018-04-17 19:39:31 +00:00
// Check whether we have to ask confirmation before logging out .
if ( askConfirmation )
{
if ( self . logoutConfirmation )
{
[ self . logoutConfirmation dismissViewControllerAnimated : NO completion : nil ] ;
self . logoutConfirmation = nil ;
}
__weak typeof ( self ) weakSelf = self ;
NSString * message = NSLocalizedStringFromTable ( @ "settings_sign_out_confirmation" , @ "Vector" , nil ) ;
// If the user has encrypted rooms , warn he will lose his e2e keys
MXSession * session = self . mxSessions . firstObject ;
for ( MXRoom * room in session . rooms )
{
2018-07-16 20:30:55 +00:00
if ( room . summary . isEncrypted )
2018-04-17 19:39:31 +00:00
{
message = [ message stringByAppendingString : [ NSString stringWithFormat : @ "\n\n%@" , NSLocalizedStringFromTable ( @ "settings_sign_out_e2e_warn" , @ "Vector" , nil ) ] ] ;
break ;
}
}
// Ask confirmation
self . logoutConfirmation = [ UIAlertController alertControllerWithTitle : NSLocalizedStringFromTable ( @ "settings_sign_out" , @ "Vector" , nil ) message : message preferredStyle : UIAlertControllerStyleAlert ] ;
[ self . logoutConfirmation addAction : [ UIAlertAction actionWithTitle : NSLocalizedStringFromTable ( @ "settings_sign_out" , @ "Vector" , nil )
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self . logoutConfirmation = nil ;
dispatch_after ( dispatch_time ( DISPATCH_TIME _NOW , 0.3 * NSEC_PER _SEC ) , dispatch_get _main _queue ( ) , ^ {
[ self logoutWithConfirmation : NO completion : completion ] ;
} ) ;
}
} ] ] ;
[ self . logoutConfirmation addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "cancel" ]
style : UIAlertActionStyleCancel
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self . logoutConfirmation = nil ;
if ( completion )
{
completion ( NO ) ;
}
}
} ] ] ;
[ self . logoutConfirmation mxk_setAccessibilityIdentifier : @ "AppDelegateLogoutConfirmationAlert" ] ;
[ self showNotificationAlert : self . logoutConfirmation ] ;
return ;
}
2018-04-17 21:26:26 +00:00
// Display a loading wheel during the logout process
id topVC ;
if ( _masterTabBarController && _masterTabBarController = = _masterNavigationController . visibleViewController )
{
topVC = _masterTabBarController . selectedViewController ;
}
else
{
topVC = _masterNavigationController . visibleViewController ;
}
if ( topVC && [ topVC respondsToSelector : @ selector ( startActivityIndicator ) ] )
{
[ topVC startActivityIndicator ] ;
}
2018-04-17 19:39:31 +00:00
2018-06-01 14:07:42 +00:00
[ self logoutSendingRequestServer : YES completion : ^ ( BOOL isLoggedOut ) {
2018-05-30 15:04:58 +00:00
if ( completion )
{
completion ( YES ) ;
}
} ] ;
}
2018-06-01 14:07:42 +00:00
- ( void ) logoutSendingRequestServer : ( BOOL ) sendLogoutServerRequest
completion : ( void ( ^ ) ( BOOL isLoggedOut ) ) completion
2018-05-30 15:04:58 +00:00
{
2017-08-21 10:28:05 +00:00
self . pushRegistry = nil ;
2017-09-19 14:06:44 +00:00
isPushRegistered = NO ;
2015-04-17 13:45:32 +00:00
2014-10-17 23:04:05 +00:00
// Clear cache
2016-12-01 13:55:29 +00:00
[ MXMediaManager clearCache ] ;
2017-07-14 14:41:25 +00:00
2019-01-31 18:53:12 +00:00
// Reset key backup banner preferences
[ KeyBackupBannerPreferences . shared reset ] ;
2015-08-07 16:30:33 +00:00
# ifdef MX_CALL _STACK _ENDPOINT
// Erase all created certificates and private keys by MXEndpointCallStack
for ( MXKAccount * account in MXKAccountManager . sharedManager . accounts )
{
if ( [ account . mxSession . callManager . callStack isKindOfClass : MXEndpointCallStack . class ] )
{
[ ( MXEndpointCallStack * ) account . mxSession . callManager . callStack deleteData : account . mxSession . myUser . userId ] ;
}
}
# endif
2015-04-17 13:45:32 +00:00
2015-04-30 14:19:12 +00:00
// Logout all matrix account
2018-04-13 18:14:44 +00:00
[ [ MXKAccountManager sharedManager ] logoutWithCompletion : ^ {
2018-04-17 19:39:31 +00:00
if ( completion )
{
completion ( YES ) ;
}
2018-04-13 18:14:44 +00:00
// Return to authentication screen
[ _masterTabBarController showAuthenticationScreen ] ;
// Note : Keep App settings
2018-09-25 13:46:07 +00:00
// But enforce usage of member lazy loading
[ MXKAppSettings standardAppSettings ] . syncWithLazyLoadOfRoomMembers = YES ;
2018-04-13 18:14:44 +00:00
// Reset the contact manager
[ [ MXKContactManager sharedManager ] reset ] ;
} ] ;
2014-10-08 16:55:26 +00:00
}
2014-10-03 17:26:39 +00:00
2015-11-17 23:15:52 +00:00
- ( void ) observeValueForKeyPath : ( NSString * ) keyPath ofObject : ( id ) object change : ( NSDictionary * ) change context : ( void * ) context
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
if ( [ @ "showAllEventsInRoomHistory" isEqualToString : keyPath ] )
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
// Flush and restore Matrix data
[ self reloadMatrixSessions : NO ] ;
2015-02-18 16:40:55 +00:00
}
2015-11-17 23:15:52 +00:00
else if ( [ @ "enableInAppNotifications" isEqualToString : keyPath ] && [ object isKindOfClass : [ MXKAccount class ] ] )
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
[ self enableInAppNotificationsForAccount : ( MXKAccount * ) object ] ;
2014-10-31 17:54:32 +00:00
}
2017-06-20 20:16:54 +00:00
else if ( object = = [ MXKAppSettings standardAppSettings ] && [ keyPath isEqualToString : @ "enableCallKit" ] )
{
BOOL isCallKitEnabled = [ MXKAppSettings standardAppSettings ] . isCallKitEnabled ;
MXCallManager * callManager = [ [ [ [ [ MXKAccountManager sharedManager ] activeAccounts ] firstObject ] mxSession ] callManager ] ;
[ self enableCallKit : isCallKitEnabled forCallManager : callManager ] ;
}
2015-11-17 23:15:52 +00:00
}
- ( void ) addMatrixCallObserver
{
if ( matrixCallObserver )
2014-10-08 12:14:12 +00:00
{
2017-09-07 17:28:38 +00:00
return ;
2014-10-03 17:26:39 +00:00
}
2014-10-08 12:14:12 +00:00
2017-09-07 17:28:38 +00:00
// Register call observer in order to handle incoming calls
2017-06-21 10:02:49 +00:00
matrixCallObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXCallManagerNewCall
object : nil
queue : [ NSOperationQueue mainQueue ]
usingBlock : ^ ( NSNotification * notif )
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
// Ignore the call if a call is already in progress
2017-08-11 12:18:10 +00:00
if ( ! currentCallViewController && ! _jitsiViewController )
2015-11-17 23:15:52 +00:00
{
MXCall * mxCall = ( MXCall * ) notif . object ;
2017-10-18 08:19:05 +00:00
BOOL isCallKitEnabled = [ MXCallKitAdapter callKitAvailable ] && [ MXKAppSettings standardAppSettings ] . isCallKitEnabled ;
2017-06-20 20:16:54 +00:00
2016-08-19 15:43:29 +00:00
// Prepare the call view controller
2017-06-20 20:16:54 +00:00
currentCallViewController = [ CallViewController callViewController : nil ] ;
2017-10-18 08:19:05 +00:00
currentCallViewController . playRingtone = ! isCallKitEnabled ;
2017-06-20 20:16:54 +00:00
currentCallViewController . mxCall = mxCall ;
2015-11-17 23:15:52 +00:00
currentCallViewController . delegate = self ;
2017-08-31 13:58:28 +00:00
2017-08-24 11:03:05 +00:00
UIApplicationState applicationState = UIApplication . sharedApplication . applicationState ;
2015-11-17 23:15:52 +00:00
2017-08-24 11:03:05 +00:00
// App has been woken by PushKit notification in the background
if ( applicationState = = UIApplicationStateBackground && mxCall . isIncoming )
{
// Create backgound task .
// Without CallKit this will allow us to play vibro until the call was ended
// With CallKit we ' ll inform the system when the call is ended to let the system terminate our app to save resources
id < MXBackgroundModeHandler > handler = [ MXSDKOptions sharedInstance ] . backgroundModeHandler ;
2019-11-05 16:23:17 +00:00
id < MXBackgroundTask > callBackgroundTask = [ handler startBackgroundTaskWithName : @ "[AppDelegate] addMatrixCallObserver" expirationHandler : nil ] ;
2017-08-24 11:03:05 +00:00
// Start listening for call state change notifications
__weak NSNotificationCenter * notificationCenter = [ NSNotificationCenter defaultCenter ] ;
__block id token = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXCallStateDidChange
object : mxCall
queue : nil
usingBlock : ^ ( NSNotification * _Nonnull note ) {
MXCall * call = ( MXCall * ) note . object ;
if ( call . state = = MXCallStateEnded )
{
// Set call vc to nil to let our app handle new incoming calls even it wasn ' t killed by the system
currentCallViewController = nil ;
[ notificationCenter removeObserver : token ] ;
2019-11-05 16:23:17 +00:00
[ callBackgroundTask stop ] ;
2017-08-24 11:03:05 +00:00
}
} ] ;
}
2017-10-18 08:19:05 +00:00
if ( mxCall . isIncoming && isCallKitEnabled )
2016-08-19 15:43:29 +00:00
{
2017-10-18 08:19:05 +00:00
// Let ' s CallKit display the system incoming call screen
// Show the callVC only after the user answered the call
__weak NSNotificationCenter * notificationCenter = [ NSNotificationCenter defaultCenter ] ;
__block id token = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXCallStateDidChange
object : mxCall
queue : nil
usingBlock : ^ ( NSNotification * _Nonnull note ) {
MXCall * call = ( MXCall * ) note . object ;
2017-08-31 13:51:05 +00:00
2017-10-27 13:35:43 +00:00
NSLog ( @ "[AppDelegate] call.state: %@" , call ) ;
2017-10-18 08:19:05 +00:00
if ( call . state = = MXCallStateCreateAnswer )
{
[ notificationCenter removeObserver : token ] ;
2017-10-27 13:35:43 +00:00
NSLog ( @ "[AppDelegate] presentCallViewController" ) ;
2017-10-18 08:19:05 +00:00
[ self presentCallViewController : NO completion : nil ] ;
}
} ] ;
2016-08-19 15:43:29 +00:00
}
else
{
2017-10-18 08:19:05 +00:00
[ self presentCallViewController : YES completion : nil ] ;
2016-08-19 15:43:29 +00:00
}
2015-11-17 23:15:52 +00:00
}
2014-11-04 16:26:33 +00:00
} ] ;
2014-10-03 17:26:39 +00:00
}
2017-01-31 23:18:01 +00:00
- ( void ) handleLaunchAnimation
{
MXSession * mainSession = self . mxSessions . firstObject ;
if ( mainSession )
{
BOOL isLaunching = NO ;
switch ( mainSession . state )
{
case MXSessionStateClosed :
case MXSessionStateInitialised :
isLaunching = YES ;
break ;
case MXSessionStateStoreDataReady :
case MXSessionStateSyncInProgress :
2017-05-12 13:20:56 +00:00
// Stay in launching during the first server sync if the store is empty .
isLaunching = ( mainSession . rooms . count = = 0 && launchAnimationContainerView ) ;
2017-01-31 23:18:01 +00:00
default :
break ;
}
if ( isLaunching )
{
UIWindow * window = [ [ UIApplication sharedApplication ] keyWindow ] ;
if ( ! launchAnimationContainerView && window )
{
launchAnimationContainerView = [ [ UIView alloc ] initWithFrame : window . bounds ] ;
2019-01-11 10:45:27 +00:00
launchAnimationContainerView . backgroundColor = ThemeService . shared . theme . backgroundColor ;
2017-01-31 23:18:01 +00:00
launchAnimationContainerView . autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight ;
[ window addSubview : launchAnimationContainerView ] ;
// Add animation view
UIImageView * animationView = [ [ UIImageView alloc ] initWithFrame : CGRectMake ( 0 , 0 , 170 , 170 ) ] ;
animationView . image = [ UIImage animatedImageNamed : @ "animatedLogo-" duration : 2 ] ;
animationView . center = CGPointMake ( launchAnimationContainerView . center . x , 3 * launchAnimationContainerView . center . y / 4 ) ;
animationView . translatesAutoresizingMaskIntoConstraints = NO ;
[ launchAnimationContainerView addSubview : animationView ] ;
NSLayoutConstraint * widthConstraint = [ NSLayoutConstraint constraintWithItem : animationView
attribute : NSLayoutAttributeWidth
relatedBy : NSLayoutRelationEqual
toItem : nil
attribute : NSLayoutAttributeNotAnAttribute
multiplier : 1
constant : 170 ] ;
NSLayoutConstraint * heightConstraint = [ NSLayoutConstraint constraintWithItem : animationView
attribute : NSLayoutAttributeHeight
relatedBy : NSLayoutRelationEqual
toItem : nil
attribute : NSLayoutAttributeNotAnAttribute
multiplier : 1
constant : 170 ] ;
NSLayoutConstraint * centerXConstraint = [ NSLayoutConstraint constraintWithItem : animationView
attribute : NSLayoutAttributeCenterX
relatedBy : NSLayoutRelationEqual
toItem : launchAnimationContainerView
attribute : NSLayoutAttributeCenterX
multiplier : 1
constant : 0 ] ;
NSLayoutConstraint * centerYConstraint = [ NSLayoutConstraint constraintWithItem : animationView
attribute : NSLayoutAttributeCenterY
relatedBy : NSLayoutRelationEqual
toItem : launchAnimationContainerView
attribute : NSLayoutAttributeCenterY
multiplier : 3.0 / 4.0
constant : 0 ] ;
[ NSLayoutConstraint activateConstraints : @ [ widthConstraint , heightConstraint , centerXConstraint , centerYConstraint ] ] ;
launchAnimationStart = [ NSDate date ] ;
}
2017-02-21 15:08:10 +00:00
return ;
2017-01-31 23:18:01 +00:00
}
2017-02-21 15:08:10 +00:00
}
if ( launchAnimationContainerView )
{
2018-06-27 15:57:40 +00:00
NSTimeInterval duration = [ [ NSDate date ] timeIntervalSinceDate : launchAnimationStart ] ;
NSLog ( @ "[AppDelegate] LaunchAnimation was shown for %.3fms" , duration * 1000 ) ;
// Track it on our analytics
[ [ Analytics sharedInstance ] trackLaunchScreenDisplayDuration : duration ] ;
2018-01-29 15:30:20 +00:00
// TODO : Send durationMs to Piwik
// Such information should be the same on all platforms
2017-02-21 15:08:10 +00:00
[ launchAnimationContainerView removeFromSuperview ] ;
launchAnimationContainerView = nil ;
2017-01-31 23:18:01 +00:00
}
}
2017-06-20 20:16:54 +00:00
- ( void ) enableCallKit : ( BOOL ) enable forCallManager : ( MXCallManager * ) callManager
{
2019-04-19 16:33:21 +00:00
JitsiService . shared . enableCallKit = enable ;
2017-06-20 20:16:54 +00:00
if ( enable )
{
2017-10-19 08:03:23 +00:00
// Create adapter for Riot
MXCallKitConfiguration * callKitConfiguration = [ [ MXCallKitConfiguration alloc ] init ] ;
callKitConfiguration . iconName = @ "riot_icon_callkit" ;
2019-04-19 16:33:21 +00:00
NSData * riotCallKitIconData = UIImagePNGRepresentation ( [ UIImage imageNamed : callKitConfiguration . iconName ] ) ;
[ JitsiService . shared configureCallKitProviderWithLocalizedName : callKitConfiguration . name
ringtoneName : callKitConfiguration . ringtoneName
iconTemplateImageData : riotCallKitIconData ] ;
2017-10-19 08:03:23 +00:00
MXCallKitAdapter * callKitAdapter = [ [ MXCallKitAdapter alloc ] initWithConfiguration : callKitConfiguration ] ;
2017-06-20 20:16:54 +00:00
id < MXCallAudioSessionConfigurator > audioSessionConfigurator ;
2017-11-24 15:28:48 +00:00
# ifdef CALL_STACK _JINGLE
2017-06-20 20:16:54 +00:00
audioSessionConfigurator = [ [ MXJingleCallAudioSessionConfigurator alloc ] init ] ;
# endif
callKitAdapter . audioSessionConfigurator = audioSessionConfigurator ;
callManager . callKitAdapter = callKitAdapter ;
}
else
{
callManager . callKitAdapter = nil ;
}
}
2017-10-18 17:18:14 +00:00
- ( void ) enableLocalNotificationsFromMatrixSession : ( MXSession * ) mxSession
{
// Prepare listener block .
2018-07-19 12:18:30 +00:00
MXWeakify ( self ) ;
2017-10-18 17:18:14 +00:00
MXOnNotification notificationListenerBlock = ^ ( MXEvent * event , MXRoomState * roomState , MXPushRule * rule ) {
2018-07-19 12:18:30 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2017-10-18 17:18:14 +00:00
// Ignore this event if the app is not running in background .
if ( [ [ UIApplication sharedApplication ] applicationState ] ! = UIApplicationStateBackground )
{
return ;
}
2018-10-10 16:05:17 +00:00
// If the app is doing an initial sync , ignore all events from which we
// did not receive a notification from APNS / PushKit
if ( ! mxSession . isEventStreamInitialised && ! self -> incomingPushPayloads [ event . eventId ] )
{
NSLog ( @ "[AppDelegate][Push] enableLocalNotificationsFromMatrixSession: Initial sync in progress. Ignore event %@" , event . eventId ) ;
return ;
}
2017-10-20 12:23:08 +00:00
// Sanity check
if ( event . eventId && event . roomId && rule )
2017-10-18 17:18:14 +00:00
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] enableLocalNotificationsFromMatrixSession: got event %@ to notify" , event . eventId ) ;
2017-10-23 14:58:55 +00:00
// Check whether this event corresponds to a pending push for this session .
NSUInteger index = [ self . incomingPushEventIds [ @ ( mxSession . hash ) ] indexOfObject : event . eventId ] ;
2017-10-20 12:23:08 +00:00
if ( index ! = NSNotFound )
{
// Remove it from the pending list .
2017-10-23 14:58:55 +00:00
[ self . incomingPushEventIds [ @ ( mxSession . hash ) ] removeObjectAtIndex : index ] ;
2017-10-20 12:23:08 +00:00
}
// Add it to the list of the events to notify .
2018-07-19 12:18:30 +00:00
[ self -> eventsToNotify [ @ ( mxSession . hash ) ] addObject : @ {
@ "event_id" : event . eventId ,
@ "room_id" : event . roomId ,
@ "push_rule" : rule
} ] ;
2017-10-20 12:23:08 +00:00
}
else
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] enableLocalNotificationsFromMatrixSession: WARNING: wrong event to notify %@ %@ %@" , event , event . roomId , rule ) ;
2017-10-18 17:18:14 +00:00
}
} ;
eventsToNotify [ @ ( mxSession . hash ) ] = [ NSMutableArray array ] ;
[ mxSession . notificationCenter listenToNotifications : notificationListenerBlock ] ;
notificationListenerBlocks [ @ ( mxSession . hash ) ] = notificationListenerBlock ;
}
- ( void ) disableLocalNotificationsFromMatrixSession : ( MXSession * ) mxSession
{
// Stop listening to notification of this session
[ mxSession . notificationCenter removeListener : notificationListenerBlocks [ @ ( mxSession . hash ) ] ] ;
[ notificationListenerBlocks removeObjectForKey : @ ( mxSession . hash ) ] ;
[ eventsToNotify removeObjectForKey : @ ( mxSession . hash ) ] ;
}
2017-03-23 16:48:05 +00:00
# pragma mark -
/ * *
Check the existence of device id .
* /
- ( void ) checkDeviceId : ( MXSession * ) mxSession
{
// In case of the app update for the e2e encryption , the app starts with
// no device id provided by the homeserver .
// Ask the user to login again in order to enable e2e . Ask it once
if ( ! isErrorNotificationSuspended && ! [ [ NSUserDefaults standardUserDefaults ] boolForKey : @ "deviceIdAtStartupChecked" ] )
{
[ [ NSUserDefaults standardUserDefaults ] setBool : YES forKey : @ "deviceIdAtStartupChecked" ] ;
// Check if there is a device id
if ( ! mxSession . matrixRestClient . credentials . deviceId )
{
NSLog ( @ "WARNING: The user has no device. Prompt for login again" ) ;
NSString * msg = NSLocalizedStringFromTable ( @ "e2e_enabling_on_app_update" , @ "Vector" , nil ) ;
__weak typeof ( self ) weakSelf = self ;
2017-07-14 14:41:25 +00:00
[ _errorNotification dismissViewControllerAnimated : NO completion : nil ] ;
_errorNotification = [ UIAlertController alertControllerWithTitle : nil message : msg preferredStyle : UIAlertControllerStyleAlert ] ;
2017-03-23 16:48:05 +00:00
2017-07-14 14:41:25 +00:00
[ _errorNotification addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "later" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> _errorNotification = nil ;
}
} ] ] ;
2017-03-23 16:48:05 +00:00
2017-07-14 14:41:25 +00:00
[ _errorNotification addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "ok" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> _errorNotification = nil ;
2018-04-17 19:39:31 +00:00
[ self logoutWithConfirmation : NO completion : nil ] ;
2017-07-14 14:41:25 +00:00
}
} ] ] ;
2017-03-23 16:48:05 +00:00
// Prompt the user
2017-07-14 14:41:25 +00:00
[ _errorNotification mxk_setAccessibilityIdentifier : @ "AppDelegateErrorAlert" ] ;
2017-03-23 16:48:05 +00:00
[ self showNotificationAlert : _errorNotification ] ;
}
}
}
2015-11-17 23:15:52 +00:00
# pragma mark - Matrix Accounts handling
2015-01-30 08:05:15 +00:00
2015-06-18 13:20:33 +00:00
- ( void ) enableInAppNotificationsForAccount : ( MXKAccount * ) account
2015-06-18 09:19:42 +00:00
{
2015-06-18 13:20:33 +00:00
if ( account . mxSession )
2015-06-18 09:19:42 +00:00
{
2015-06-18 13:20:33 +00:00
if ( account . enableInAppNotifications )
2015-06-18 09:19:42 +00:00
{
2015-06-18 13:20:33 +00:00
// Build MXEvent -> NSString formatter
2015-08-21 18:00:39 +00:00
EventFormatter * eventFormatter = [ [ EventFormatter alloc ] initWithMatrixSession : account . mxSession ] ;
2015-06-18 13:20:33 +00:00
eventFormatter . isForSubtitle = YES ;
2015-06-19 08:31:27 +00:00
[ account listenToNotifications : ^ ( MXEvent * event , MXRoomState * roomState , MXPushRule * rule ) {
// Check conditions to display this notification
2015-11-17 23:15:52 +00:00
if ( ! [ self . visibleRoomId isEqualToString : event . roomId ]
&& ! self . window . rootViewController . presentedViewController )
2015-06-19 08:31:27 +00:00
{
MXKEventFormatterError error ;
NSString * messageText = [ eventFormatter stringFromEvent : event withRoomState : roomState error : & error ] ;
if ( messageText . length && ( error = = MXKEventFormatterErrorNone ) )
{
// Removing existing notification ( if any )
if ( self . mxInAppNotification )
{
2017-07-14 14:41:25 +00:00
[ self . mxInAppNotification dismissViewControllerAnimated : NO completion : nil ] ;
2015-06-19 08:31:27 +00:00
}
// Check whether tweak is required
for ( MXPushRuleAction * ruleAction in rule . actions )
{
if ( ruleAction . actionType = = MXPushRuleActionTypeSetTweak )
{
if ( [ [ ruleAction . parameters valueForKey : @ "set_tweak" ] isEqualToString : @ "sound" ] )
{
2018-04-08 14:00:33 +00:00
// Play message sound
AudioServicesPlaySystemSound ( _messageSound ) ;
2015-06-19 08:31:27 +00:00
}
}
}
2017-10-16 12:58:38 +00:00
MXRoomSummary * roomSummary = [ account . mxSession roomSummaryWithRoomId : event . roomId ] ;
2017-10-16 12:43:07 +00:00
2015-06-19 08:31:27 +00:00
__weak typeof ( self ) weakSelf = self ;
2017-10-16 12:58:38 +00:00
self . mxInAppNotification = [ UIAlertController alertControllerWithTitle : roomSummary . displayname
2017-07-14 14:41:25 +00:00
message : messageText
preferredStyle : UIAlertControllerStyleAlert ] ;
2015-06-19 08:31:27 +00:00
2017-07-14 14:41:25 +00:00
[ self . mxInAppNotification addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "cancel" ]
2018-01-19 02:43:28 +00:00
style : UIAlertActionStyleCancel
2017-07-14 14:41:25 +00:00
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self . mxInAppNotification = nil ;
[ account updateNotificationListenerForRoomId : event . roomId ignore : YES ] ;
}
} ] ] ;
[ self . mxInAppNotification addAction : [ UIAlertAction actionWithTitle : NSLocalizedStringFromTable ( @ "view" , @ "Vector" , nil )
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self . mxInAppNotification = nil ;
// Show the room
[ self showRoom : event . roomId andEventId : nil withMatrixSession : account . mxSession ] ;
}
} ] ] ;
[ self . window . rootViewController presentViewController : self . mxInAppNotification animated : YES completion : nil ] ;
2015-06-19 08:31:27 +00:00
}
}
} ] ;
2015-06-18 13:20:33 +00:00
}
else
{
[ account removeNotificationListener ] ;
2015-04-30 14:19:12 +00:00
}
}
2015-06-18 09:19:42 +00:00
if ( self . mxInAppNotification )
{
2017-07-14 14:41:25 +00:00
[ self . mxInAppNotification dismissViewControllerAnimated : NO completion : nil ] ;
2015-04-30 14:19:12 +00:00
self . mxInAppNotification = nil ;
}
}
2015-06-18 09:19:42 +00:00
- ( void ) selectMatrixAccount : ( void ( ^ ) ( MXKAccount * selectedAccount ) ) onSelection
{
2015-06-19 08:31:27 +00:00
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
2015-05-28 16:31:08 +00:00
2015-06-18 09:19:42 +00:00
if ( mxAccounts . count = = 1 )
{
if ( onSelection )
{
2015-05-28 16:31:08 +00:00
onSelection ( mxAccounts . firstObject ) ;
}
2015-06-18 09:19:42 +00:00
}
else if ( mxAccounts . count > 1 )
{
2017-07-14 14:41:25 +00:00
[ accountPicker dismissViewControllerAnimated : NO completion : nil ] ;
2015-05-28 16:31:08 +00:00
2017-07-14 14:41:25 +00:00
accountPicker = [ UIAlertController alertControllerWithTitle : [ NSBundle mxk_localizedStringForKey : @ "select_account" ] message : nil preferredStyle : UIAlertControllerStyleActionSheet ] ;
2015-05-28 16:31:08 +00:00
__weak typeof ( self ) weakSelf = self ;
2015-06-18 09:19:42 +00:00
for ( MXKAccount * account in mxAccounts )
{
2017-07-14 14:41:25 +00:00
[ accountPicker addAction : [ UIAlertAction actionWithTitle : account . mxCredentials . userId
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> accountPicker = nil ;
if ( onSelection )
{
onSelection ( account ) ;
}
}
} ] ] ;
2015-05-28 16:31:08 +00:00
}
2017-07-14 14:41:25 +00:00
[ accountPicker addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "cancel" ]
2018-01-19 02:43:28 +00:00
style : UIAlertActionStyleCancel
2017-07-14 14:41:25 +00:00
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> accountPicker = nil ;
if ( onSelection )
{
onSelection ( nil ) ;
}
}
} ] ] ;
2015-05-28 16:31:08 +00:00
2016-09-06 15:20:16 +00:00
[ self showNotificationAlert : accountPicker ] ;
2015-05-28 16:31:08 +00:00
}
}
2015-05-04 12:49:30 +00:00
# pragma mark - Matrix Rooms handling
2019-04-29 12:22:01 +00:00
- ( void ) showRoom : ( NSString * ) roomId andEventId : ( NSString * ) eventId withMatrixSession : ( MXSession * ) mxSession restoreInitialDisplay : ( BOOL ) restoreInitialDisplay completion : ( void ( ^ ) ( void ) ) completion
2015-08-26 17:04:30 +00:00
{
2019-04-29 12:22:01 +00:00
void ( ^ selectRoom ) ( void ) = ^ ( ) {
2015-12-09 17:12:42 +00:00
// Select room to display its details ( dispatch this action in order to let TabBarController end its refresh )
2019-04-08 11:32:46 +00:00
[ self . masterTabBarController selectRoomWithId : roomId andEventId : eventId inMatrixSession : mxSession completion : ^ {
2019-04-05 12:51:56 +00:00
// Remove delivered notifications for this room
[ self removeDeliveredNotificationsWithRoomId : roomId completion : nil ] ;
2019-04-08 11:32:46 +00:00
if ( completion )
{
completion ( ) ;
}
2019-04-05 12:51:56 +00:00
} ] ;
2019-04-29 12:22:01 +00:00
} ;
if ( restoreInitialDisplay )
{
[ self restoreInitialDisplay : ^ {
selectRoom ( ) ;
} ] ;
}
else
{
selectRoom ( ) ;
}
}
- ( void ) showRoom : ( NSString * ) roomId andEventId : ( NSString * ) eventId withMatrixSession : ( MXSession * ) mxSession restoreInitialDisplay : ( BOOL ) restoreInitialDisplay
{
[ self showRoom : roomId andEventId : eventId withMatrixSession : mxSession restoreInitialDisplay : restoreInitialDisplay completion : nil ] ;
2015-08-26 17:04:30 +00:00
}
2019-04-08 11:32:46 +00:00
- ( void ) showRoom : ( NSString * ) roomId andEventId : ( NSString * ) eventId withMatrixSession : ( MXSession * ) mxSession
{
2019-04-29 12:22:01 +00:00
[ self showRoom : roomId andEventId : eventId withMatrixSession : mxSession restoreInitialDisplay : YES completion : nil ] ;
2019-04-08 11:32:46 +00:00
}
2016-04-15 07:43:56 +00:00
- ( void ) showRoomPreview : ( RoomPreviewData * ) roomPreviewData
{
[ self restoreInitialDisplay : ^ {
2017-03-23 16:48:05 +00:00
[ _masterTabBarController showRoomPreview : roomPreviewData ] ;
2016-04-15 07:43:56 +00:00
} ] ;
}
2015-11-17 23:15:52 +00:00
- ( void ) setVisibleRoomId : ( NSString * ) roomId
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
if ( roomId )
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
// Enable inApp notification for this room in all existing accounts .
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . accounts ;
for ( MXKAccount * account in mxAccounts )
{
[ account updateNotificationListenerForRoomId : roomId ignore : NO ] ;
}
2014-10-02 15:02:47 +00:00
}
2015-11-17 23:15:52 +00:00
_visibleRoomId = roomId ;
2014-10-02 15:02:47 +00:00
}
2016-11-02 13:32:02 +00:00
- ( void ) createDirectChatWithUserId : ( NSString * ) userId completion : ( void ( ^ ) ( void ) ) completion
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
// Handle here potential multiple accounts
2016-03-21 17:38:44 +00:00
[ self selectMatrixAccount : ^ ( MXKAccount * selectedAccount ) {
MXSession * mxSession = selectedAccount . mxSession ;
if ( mxSession )
{
2016-11-02 13:32:02 +00:00
// Create a new room by inviting the other user only if it is defined and not oneself
NSArray * invite = ( ( userId && ! [ mxSession . myUser . userId isEqualToString : userId ] ) ? @ [ userId ] : nil ) ;
2016-03-21 17:38:44 +00:00
2016-11-02 13:32:02 +00:00
[ mxSession createRoom : nil
visibility : kMXRoomDirectoryVisibilityPrivate
roomAlias : nil
topic : nil
invite : invite
invite3PID : nil
isDirect : ( invite . count ! = 0 )
2016-11-02 15:35:45 +00:00
preset : kMXRoomPresetTrustedPrivateChat
2016-11-02 13:32:02 +00:00
success : ^ ( MXRoom * room ) {
// Open created room
2018-07-16 20:30:55 +00:00
[ self showRoom : room . roomId andEventId : nil withMatrixSession : mxSession ] ;
2016-11-02 13:32:02 +00:00
if ( completion )
{
completion ( ) ;
2016-03-21 17:38:44 +00:00
}
2016-11-02 13:32:02 +00:00
}
failure : ^ ( NSError * error ) {
NSLog ( @ "[AppDelegate] Create direct chat failed" ) ;
// Alert user
[ self showErrorAsAlert : error ] ;
if ( completion )
{
completion ( ) ;
}
} ] ;
2016-03-21 17:38:44 +00:00
}
else if ( completion )
{
completion ( ) ;
}
} ] ;
2015-01-05 15:53:41 +00:00
}
2016-11-02 16:54:28 +00:00
- ( void ) startDirectChatWithUserId : ( NSString * ) userId completion : ( void ( ^ ) ( void ) ) completion
{
// Handle here potential multiple accounts
[ self selectMatrixAccount : ^ ( MXKAccount * selectedAccount ) {
MXSession * mxSession = selectedAccount . mxSession ;
if ( mxSession )
{
2016-11-03 17:12:08 +00:00
MXRoom * directRoom = [ mxSession directJoinedRoomWithUserId : userId ] ;
2016-11-02 16:54:28 +00:00
// if the room exists
2016-11-03 17:12:08 +00:00
if ( directRoom )
2016-11-02 16:54:28 +00:00
{
// open it
2016-11-03 17:12:08 +00:00
[ self showRoom : directRoom . roomId andEventId : nil withMatrixSession : mxSession ] ;
2016-11-02 16:54:28 +00:00
if ( completion )
{
completion ( ) ;
}
}
else
{
[ self createDirectChatWithUserId : userId completion : completion ] ;
}
}
else if ( completion )
{
completion ( ) ;
}
} ] ;
}
2017-01-17 22:13:30 +00:00
# pragma mark - Contacts handling
2017-07-25 14:08:24 +00:00
- ( void ) showContact : ( MXKContact * ) contact
{
[ self restoreInitialDisplay : ^ {
[ self . masterTabBarController selectContact : contact ] ;
} ] ;
}
2017-01-17 22:13:30 +00:00
- ( void ) refreshLocalContacts
{
2019-10-10 13:05:39 +00:00
// Do not scan local contacts in background if the user has not decided yet about using
// an identity server
BOOL doRefreshLocalContacts = NO ;
for ( MXSession * session in mxSessionArray )
{
if ( session . hasAccountDataIdentityServerValue )
{
doRefreshLocalContacts = YES ;
break ;
}
}
2017-01-17 22:13:30 +00:00
// Check whether the application is allowed to access the local contacts .
2019-10-10 13:05:39 +00:00
if ( doRefreshLocalContacts
&& [ CNContactStore authorizationStatusForEntityType : CNEntityTypeContacts ] = = CNAuthorizationStatusAuthorized )
2017-01-17 22:13:30 +00:00
{
// Check the user permission for syncing local contacts . This permission was handled independently on previous application version .
if ( ! [ MXKAppSettings standardAppSettings ] . syncLocalContacts )
{
// Check whether it was not requested yet .
if ( ! [ MXKAppSettings standardAppSettings ] . syncLocalContactsPermissionRequested )
{
[ MXKAppSettings standardAppSettings ] . syncLocalContactsPermissionRequested = YES ;
UIViewController * viewController = self . window . rootViewController . presentedViewController ;
if ( ! viewController )
{
viewController = self . window . rootViewController ;
}
[ MXKContactManager requestUserConfirmationForLocalContactsSyncInViewController : viewController completionHandler : ^ ( BOOL granted ) {
if ( granted )
{
// Allow local contacts sync in order to discover matrix users .
[ MXKAppSettings standardAppSettings ] . syncLocalContacts = YES ;
}
} ] ;
}
}
2017-01-23 17:40:30 +00:00
// Refresh the local contacts list .
[ [ MXKContactManager sharedManager ] refreshLocalContacts ] ;
2017-01-17 22:13:30 +00:00
}
}
2017-12-31 15:24:47 +00:00
# pragma mark - Matrix Groups handling
- ( void ) showGroup : ( MXGroup * ) group withMatrixSession : ( MXSession * ) mxSession
{
[ self restoreInitialDisplay : ^ {
// Select group to display its details ( dispatch this action in order to let TabBarController end its refresh )
[ _masterTabBarController selectGroup : group inMatrixSession : mxSession ] ;
} ] ;
}
2015-05-11 21:11:23 +00:00
# pragma mark - MXKCallViewControllerDelegate
2019-01-05 02:28:03 +00:00
- ( void ) dismissCallViewController : ( MXKCallViewController * ) callViewController completion : ( void ( ^ ) ( void ) ) completion
2015-06-18 09:19:42 +00:00
{
2016-11-24 14:09:04 +00:00
if ( currentCallViewController && callViewController = = currentCallViewController )
2015-06-18 09:19:42 +00:00
{
2017-09-04 14:26:41 +00:00
if ( callViewController . isBeingPresented )
2016-11-24 14:09:04 +00:00
{
// Here the presentation of the call view controller is in progress
// Postpone the dismiss
dispatch_after ( dispatch_time ( DISPATCH_TIME _NOW , ( int64_t ) ( 0.3 * NSEC_PER _SEC ) ) , dispatch_get _main _queue ( ) , ^ {
[ self dismissCallViewController : callViewController completion : completion ] ;
} ) ;
}
// Check whether the call view controller is actually presented
else if ( callViewController . presentingViewController )
2015-06-18 09:19:42 +00:00
{
2015-05-12 08:53:47 +00:00
BOOL callIsEnded = ( callViewController . mxCall . state = = MXCallStateEnded ) ;
NSLog ( @ "Call view controller is dismissed (%d)" , callIsEnded ) ;
[ callViewController dismissViewControllerAnimated : YES completion : ^ {
2015-06-18 09:19:42 +00:00
if ( ! callIsEnded )
{
2016-09-02 20:21:02 +00:00
NSString * btnTitle = [ NSString stringWithFormat : NSLocalizedStringFromTable ( @ "active_call_details" , @ "Vector" , nil ) , callViewController . callerNameLabel . text ] ;
[ self addCallStatusBar : btnTitle ] ;
2015-05-12 08:53:47 +00:00
}
2019-08-28 15:04:49 +00:00
if ( [ callViewController isKindOfClass : [ CallViewController class ] ]
&& ( ( CallViewController * ) callViewController ) . shouldPromptForStunServerFallback )
{
[ self promptForStunServerFallback ] ;
}
2016-08-19 15:43:29 +00:00
if ( completion )
{
completion ( ) ;
}
2016-11-24 14:09:04 +00:00
2015-05-12 08:53:47 +00:00
} ] ;
2015-06-18 09:19:42 +00:00
if ( callIsEnded )
{
2015-05-12 08:53:47 +00:00
[ self removeCallStatusBar ] ;
// Release properly
2017-06-03 23:39:00 +00:00
[ currentCallViewController destroy ] ;
2015-05-12 08:53:47 +00:00
currentCallViewController = nil ;
}
2016-08-19 15:43:29 +00:00
}
2017-10-27 13:35:43 +00:00
else if ( _callStatusBarWindow )
2015-06-18 09:19:42 +00:00
{
2016-11-24 14:09:04 +00:00
// Here the call view controller was not presented .
NSLog ( @ "Call view controller was not presented" ) ;
// Workaround to manage the "back to call" banner : present temporarily the call screen .
// This will correctly manage the navigation bar layout .
2017-10-18 08:19:05 +00:00
[ self presentCallViewController : YES completion : ^ {
2016-11-24 14:09:04 +00:00
[ self dismissCallViewController : currentCallViewController completion : completion ] ;
} ] ;
2015-05-12 08:53:47 +00:00
}
2015-05-11 21:11:23 +00:00
}
}
2019-08-28 15:04:49 +00:00
- ( void ) promptForStunServerFallback
{
[ _errorNotification dismissViewControllerAnimated : NO completion : nil ] ;
NSString * stunFallbackHost = RiotSettings . shared . stunServerFallback ;
// Remove "stun:"
stunFallbackHost = [ stunFallbackHost componentsSeparatedByString : @ ":" ] . lastObject ;
MXSession * mainSession = self . mxSessions . firstObject ;
NSString * homeServerName = mainSession . matrixRestClient . credentials . homeServerName ;
NSString * message = [ NSString stringWithFormat : @ "%@\n\n%@" ,
[ NSString stringWithFormat : NSLocalizedStringFromTable ( @ "call_no_stun_server_error_message_1" , @ "Vector" , nil ) , homeServerName ] ,
[ NSString stringWithFormat : NSLocalizedStringFromTable ( @ "call_no_stun_server_error_message_2" , @ "Vector" , nil ) , stunFallbackHost ] ] ;
_errorNotification = [ UIAlertController alertControllerWithTitle : NSLocalizedStringFromTable ( @ "call_no_stun_server_error_title" , @ "Vector" , nil )
message : message
preferredStyle : UIAlertControllerStyleAlert ] ;
[ _errorNotification addAction : [ UIAlertAction actionWithTitle : [ NSString stringWithFormat : NSLocalizedStringFromTable ( @ "call_no_stun_server_error_use_fallback_button" , @ "Vector" , nil ) , stunFallbackHost ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
RiotSettings . shared . allowStunServerFallback = YES ;
mainSession . callManager . fallbackSTUNServer = RiotSettings . shared . stunServerFallback ;
[ AppDelegate theDelegate ] . errorNotification = nil ;
} ] ] ;
[ _errorNotification addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "cancel" ]
style : UIAlertActionStyleCancel
handler : ^ ( UIAlertAction * action ) {
RiotSettings . shared . allowStunServerFallback = NO ;
[ AppDelegate theDelegate ] . errorNotification = nil ;
} ] ] ;
// Display the error notification
if ( ! isErrorNotificationSuspended )
{
[ _errorNotification mxk_setAccessibilityIdentifier : @ "AppDelegateErrorAlert" ] ;
[ self showNotificationAlert : _errorNotification ] ;
}
}
2017-08-11 12:18:10 +00:00
# pragma mark - Jitsi call
- ( void ) displayJitsiViewControllerWithWidget : ( Widget * ) jitsiWidget andVideo : ( BOOL ) video
{
if ( ! _jitsiViewController && ! currentCallViewController )
{
2019-11-22 10:18:08 +00:00
MXWeakify ( self ) ;
[ self checkPermissionForNativeWidget : jitsiWidget fromUrl : JitsiService . shared . serverURL completion : ^ ( BOOL granted ) {
MXStrongifyAndReturnIfNil ( self ) ;
if ( ! granted )
{
return ;
}
2017-08-11 12:18:10 +00:00
2019-11-22 10:18:08 +00:00
self -> _jitsiViewController = [ JitsiViewController jitsiViewController ] ;
2018-01-04 10:49:39 +00:00
2019-11-22 10:18:08 +00:00
[ self -> _jitsiViewController openWidget : jitsiWidget withVideo : video success : ^ {
2018-01-04 10:49:39 +00:00
2019-11-22 10:18:08 +00:00
self -> _jitsiViewController . delegate = self ;
[ self presentJitsiViewController : nil ] ;
2017-08-18 09:40:56 +00:00
2019-11-22 10:18:08 +00:00
} failure : ^ ( NSError * error ) {
self -> _jitsiViewController = nil ;
[ self showAlertWithTitle : nil message : NSLocalizedStringFromTable ( @ "call_jitsi_error" , @ "Vector" , nil ) ] ;
} ] ;
2018-01-04 10:49:39 +00:00
} ] ;
2017-08-11 12:18:10 +00:00
}
else
{
2019-01-17 16:46:49 +00:00
[ self showAlertWithTitle : nil message : NSLocalizedStringFromTable ( @ "call_already_displayed" , @ "Vector" , nil ) ] ;
2017-08-11 12:18:10 +00:00
}
}
2019-01-05 02:28:03 +00:00
- ( void ) presentJitsiViewController : ( void ( ^ ) ( void ) ) completion
2017-08-11 12:18:10 +00:00
{
[ self removeCallStatusBar ] ;
if ( _jitsiViewController )
{
if ( self . window . rootViewController . presentedViewController )
{
[ self . window . rootViewController . presentedViewController presentViewController : _jitsiViewController animated : YES completion : completion ] ;
}
else
{
[ self . window . rootViewController presentViewController : _jitsiViewController animated : YES completion : completion ] ;
}
}
}
2019-01-05 02:28:03 +00:00
- ( void ) jitsiViewController : ( JitsiViewController * ) jitsiViewController dismissViewJitsiController : ( void ( ^ ) ( void ) ) completion
2017-08-11 12:18:10 +00:00
{
if ( jitsiViewController = = _jitsiViewController )
{
[ _jitsiViewController dismissViewControllerAnimated : YES completion : completion ] ;
_jitsiViewController = nil ;
2017-08-11 12:59:05 +00:00
[ self removeCallStatusBar ] ;
2017-08-11 12:18:10 +00:00
}
}
2019-01-05 02:28:03 +00:00
- ( void ) jitsiViewController : ( JitsiViewController * ) jitsiViewController goBackToApp : ( void ( ^ ) ( void ) ) completion
2017-08-11 12:18:10 +00:00
{
if ( jitsiViewController = = _jitsiViewController )
{
[ _jitsiViewController dismissViewControllerAnimated : YES completion : ^ {
MXRoom * room = [ _jitsiViewController . widget . mxSession roomWithRoomId : _jitsiViewController . widget . roomId ] ;
2017-09-14 14:00:50 +00:00
NSString * btnTitle = [ NSString stringWithFormat : NSLocalizedStringFromTable ( @ "active_call_details" , @ "Vector" , nil ) , room . summary . displayname ] ;
2017-08-11 12:18:10 +00:00
[ self addCallStatusBar : btnTitle ] ;
if ( completion )
{
completion ( ) ;
}
} ] ;
}
}
2019-11-22 10:18:08 +00:00
# pragma mark - Native Widget Permission
- ( void ) checkPermissionForNativeWidget : ( Widget * ) widget fromUrl : ( NSURL * ) url completion : ( void ( ^ ) ( BOOL granted ) ) completion
{
MXSession * session = widget . mxSession ;
if ( [ widget . widgetEvent . sender isEqualToString : session . myUser . userId ] )
{
// No need of more permission check if the user created the widget
completion ( YES ) ;
return ;
}
// Check permission in user Riot settings
__block RiotSharedSettings * sharedSettings = [ [ RiotSharedSettings alloc ] initWithSession : session ] ;
WidgetPermission permission = [ sharedSettings permissionForNative : widget fromUrl : url ] ;
if ( permission = = WidgetPermissionGranted )
{
completion ( YES ) ;
}
else
{
// Note : ask permission again if the user previously declined it
2019-11-28 16:38:19 +00:00
[ self askNativeWidgetPermissionWithWidget : widget completion : ^ ( BOOL granted ) {
2019-11-22 10:18:08 +00:00
// Update the settings in user account data in parallel
[ sharedSettings setPermission : granted ? WidgetPermissionGranted : WidgetPermissionDeclined
forNative : widget fromUrl : url
success : ^
{
sharedSettings = nil ;
}
failure : ^ ( NSError * _Nullable error )
{
NSLog ( @ "[WidgetVC] setPermissionForWidget failed. Error: %@" , error ) ;
sharedSettings = nil ;
} ] ;
2019-11-28 16:38:19 +00:00
2019-11-22 10:18:08 +00:00
completion ( granted ) ;
} ] ;
}
}
2019-11-28 16:38:19 +00:00
- ( void ) askNativeWidgetPermissionWithWidget : ( Widget * ) widget completion : ( void ( ^ ) ( BOOL granted ) ) completion
2019-11-22 10:18:08 +00:00
{
2019-11-28 16:38:19 +00:00
if ( ! self . slidingModalPresenter )
{
self . slidingModalPresenter = [ SlidingModalPresenter new ] ;
}
[ self . slidingModalPresenter dismissWithAnimated : NO completion : nil ] ;
NSString * widgetCreatorUserId = widget . widgetEvent . sender ? : NSLocalizedStringFromTable ( @ "room_participants_unknown" , @ "Vector" , nil ) ;
MXSession * session = widget . mxSession ;
MXRoom * room = [ session roomWithRoomId : widget . widgetEvent . roomId ] ;
MXRoomState * roomState = room . dangerousSyncState ;
MXRoomMember * widgetCreatorRoomMember = [ roomState . members memberWithUserId : widgetCreatorUserId ] ;
NSString * widgetDomain = @ "" ;
if ( widget . url )
{
NSString * host = [ [ NSURL alloc ] initWithString : widget . url ] . host ;
if ( host )
{
widgetDomain = host ;
}
}
MXMediaManager * mediaManager = widget . mxSession . mediaManager ;
NSString * widgetCreatorDisplayName = widgetCreatorRoomMember . displayname ;
NSString * widgetCreatorAvatarURL = widgetCreatorRoomMember . avatarUrl ;
NSArray < NSString * > * permissionStrings = @ [
NSLocalizedStringFromTable ( @ "room_widget_permission_display_name_permission" , @ "Vector" , nil ) ,
NSLocalizedStringFromTable ( @ "room_widget_permission_avatar_url_permission" , @ "Vector" , nil )
] ;
WidgetPermissionViewModel * widgetPermissionViewModel = [ [ WidgetPermissionViewModel alloc ] initWithCreatorUserId : widgetCreatorUserId
creatorDisplayName : widgetCreatorDisplayName creatorAvatarUrl : widgetCreatorAvatarURL widgetDomain : widgetDomain
2019-11-28 17:02:17 +00:00
isWebviewWidget : NO
2019-11-28 16:38:19 +00:00
widgetPermissions : permissionStrings
mediaManager : mediaManager ] ;
WidgetPermissionViewController * widgetPermissionViewController = [ WidgetPermissionViewController instantiateWith : widgetPermissionViewModel ] ;
MXWeakify ( self ) ;
widgetPermissionViewController . didTapContinueButton = ^ {
MXStrongifyAndReturnIfNil ( self ) ;
[ self . slidingModalPresenter dismissWithAnimated : YES completion : ^ {
completion ( YES ) ;
} ] ;
} ;
widgetPermissionViewController . didTapCloseButton = ^ {
MXStrongifyAndReturnIfNil ( self ) ;
[ self . slidingModalPresenter dismissWithAnimated : YES completion : ^ {
completion ( NO ) ;
} ] ;
} ;
2019-11-22 10:18:08 +00:00
UIViewController * presentingViewController = self . window . rootViewController . presentedViewController ? : self . window . rootViewController ;
2019-11-28 16:38:19 +00:00
[ self . slidingModalPresenter present : widgetPermissionViewController
from : presentingViewController
animated : YES
completion : nil ] ;
2019-11-22 10:18:08 +00:00
}
2015-05-11 21:11:23 +00:00
# pragma mark - Call status handling
2016-09-02 20:21:02 +00:00
- ( void ) addCallStatusBar : ( NSString * ) buttonTitle
2015-06-18 09:19:42 +00:00
{
2015-05-11 21:11:23 +00:00
// Add a call status bar
2016-09-02 20:21:02 +00:00
CGSize topBarSize = CGSizeMake ( [ [ UIScreen mainScreen ] bounds ] . size . width , CALL_STATUS _BAR _HEIGHT ) ;
2015-05-11 21:11:23 +00:00
2016-09-02 20:21:02 +00:00
_callStatusBarWindow = [ [ UIWindow alloc ] initWithFrame : CGRectMake ( 0 , 0 , topBarSize . width , topBarSize . height ) ] ;
_callStatusBarWindow . windowLevel = UIWindowLevelStatusBar ;
2015-05-11 21:11:23 +00:00
// Create statusBarButton
2016-09-02 20:21:02 +00:00
_callStatusBarButton = [ UIButton buttonWithType : UIButtonTypeCustom ] ;
_callStatusBarButton . frame = CGRectMake ( 0 , 0 , topBarSize . width , topBarSize . height ) ;
2015-05-11 21:11:23 +00:00
2016-09-02 20:21:02 +00:00
[ _callStatusBarButton setTitle : buttonTitle forState : UIControlStateNormal ] ;
[ _callStatusBarButton setTitle : buttonTitle forState : UIControlStateHighlighted ] ;
2019-01-11 10:45:27 +00:00
_callStatusBarButton . titleLabel . textColor = ThemeService . shared . theme . backgroundColor ;
2019-04-25 10:27:31 +00:00
_callStatusBarButton . titleLabel . font = [ UIFont systemFontOfSize : 17 weight : UIFontWeightMedium ] ;
2015-05-11 21:11:23 +00:00
2019-01-11 10:45:27 +00:00
[ _callStatusBarButton setBackgroundColor : ThemeService . shared . theme . tintColor ] ;
2017-08-11 12:18:10 +00:00
[ _callStatusBarButton addTarget : self action : @ selector ( onCallStatusBarButtonPressed ) forControlEvents : UIControlEventTouchUpInside ] ;
2015-05-11 21:11:23 +00:00
// Place button into the new window
2016-09-02 20:21:02 +00:00
[ _callStatusBarButton setTranslatesAutoresizingMaskIntoConstraints : NO ] ;
[ _callStatusBarWindow addSubview : _callStatusBarButton ] ;
// Force callStatusBarButton to fill the window ( to handle auto - layout in case of screen rotation )
NSLayoutConstraint * widthConstraint = [ NSLayoutConstraint constraintWithItem : _callStatusBarButton
2017-07-14 14:41:25 +00:00
attribute : NSLayoutAttributeWidth
relatedBy : NSLayoutRelationEqual
toItem : _callStatusBarWindow
attribute : NSLayoutAttributeWidth
multiplier : 1.0
constant : 0 ] ;
2016-09-02 20:21:02 +00:00
NSLayoutConstraint * heightConstraint = [ NSLayoutConstraint constraintWithItem : _callStatusBarButton
attribute : NSLayoutAttributeHeight
relatedBy : NSLayoutRelationEqual
toItem : _callStatusBarWindow
attribute : NSLayoutAttributeHeight
multiplier : 1.0
constant : 0 ] ;
[ NSLayoutConstraint activateConstraints : @ [ widthConstraint , heightConstraint ] ] ;
_callStatusBarWindow . hidden = NO ;
2015-05-11 21:11:23 +00:00
[ self statusBarDidChangeFrame ] ;
// We need to listen to the system status bar size change events to refresh the root controller frame .
// Else the navigation bar position will be wrong .
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( statusBarDidChangeFrame )
name : UIApplicationDidChangeStatusBarFrameNotification
object : nil ] ;
}
2015-06-18 09:19:42 +00:00
- ( void ) removeCallStatusBar
{
2016-09-02 20:21:02 +00:00
if ( _callStatusBarWindow )
2015-06-18 09:19:42 +00:00
{
2016-09-02 20:21:02 +00:00
// No more need to listen to system status bar changes
[ [ NSNotificationCenter defaultCenter ] removeObserver : self name : UIApplicationDidChangeStatusBarFrameNotification object : nil ] ;
2015-05-11 21:11:23 +00:00
// Hide & destroy it
2016-09-02 20:21:02 +00:00
_callStatusBarWindow . hidden = YES ;
[ _callStatusBarButton removeFromSuperview ] ;
_callStatusBarButton = nil ;
_callStatusBarWindow = nil ;
2015-05-11 21:11:23 +00:00
2016-09-02 20:21:02 +00:00
[ self statusBarDidChangeFrame ] ;
2015-05-11 21:11:23 +00:00
}
}
2017-08-11 12:18:10 +00:00
- ( void ) onCallStatusBarButtonPressed
2016-11-24 14:09:04 +00:00
{
2017-08-11 12:18:10 +00:00
if ( currentCallViewController )
{
2017-10-18 08:19:05 +00:00
[ self presentCallViewController : YES completion : nil ] ;
2017-08-11 12:18:10 +00:00
}
else if ( _jitsiViewController )
{
[ self presentJitsiViewController : nil ] ;
}
2016-11-24 14:09:04 +00:00
}
2019-01-05 02:28:03 +00:00
- ( void ) presentCallViewController : ( BOOL ) animated completion : ( void ( ^ ) ( void ) ) completion
2015-06-18 09:19:42 +00:00
{
2015-05-11 21:11:23 +00:00
[ self removeCallStatusBar ] ;
2017-07-14 14:41:25 +00:00
2016-09-06 12:21:11 +00:00
if ( currentCallViewController )
{
2016-09-06 15:20:16 +00:00
if ( self . window . rootViewController . presentedViewController )
{
2017-10-18 08:19:05 +00:00
[ self . window . rootViewController . presentedViewController presentViewController : currentCallViewController animated : animated completion : completion ] ;
2016-09-06 15:20:16 +00:00
}
else
{
2017-10-18 08:19:05 +00:00
[ self . window . rootViewController presentViewController : currentCallViewController animated : animated completion : completion ] ;
2016-09-06 15:20:16 +00:00
}
2016-09-06 12:21:11 +00:00
}
2015-05-11 21:11:23 +00:00
}
2015-06-18 09:19:42 +00:00
- ( void ) statusBarDidChangeFrame
{
2015-05-11 21:11:23 +00:00
UIApplication * app = [ UIApplication sharedApplication ] ;
UIViewController * rootController = app . keyWindow . rootViewController ;
// Refresh the root view controller frame
2016-09-02 20:21:02 +00:00
CGRect rootControllerFrame = [ [ UIScreen mainScreen ] bounds ] ;
if ( _callStatusBarWindow )
2015-06-18 09:19:42 +00:00
{
2016-09-02 20:21:02 +00:00
UIInterfaceOrientation statusBarOrientation = [ UIApplication sharedApplication ] . statusBarOrientation ;
2015-05-11 21:11:23 +00:00
2016-09-02 20:21:02 +00:00
switch ( statusBarOrientation )
{
case UIInterfaceOrientationLandscapeLeft :
{
_callStatusBarWindow . frame = CGRectMake ( - rootControllerFrame . size . width / 2 , - CALL_STATUS _BAR _HEIGHT / 2 , rootControllerFrame . size . width , CALL_STATUS _BAR _HEIGHT ) ;
_callStatusBarWindow . transform = CGAffineTransformMake ( 0 , -1 , 1 , 0 , CALL_STATUS _BAR _HEIGHT / 2 , rootControllerFrame . size . width / 2 ) ;
break ;
}
case UIInterfaceOrientationLandscapeRight :
{
_callStatusBarWindow . frame = CGRectMake ( - rootControllerFrame . size . width / 2 , - CALL_STATUS _BAR _HEIGHT / 2 , rootControllerFrame . size . width , CALL_STATUS _BAR _HEIGHT ) ;
_callStatusBarWindow . transform = CGAffineTransformMake ( 0 , 1 , -1 , 0 , rootControllerFrame . size . height - CALL_STATUS _BAR _HEIGHT / 2 , rootControllerFrame . size . width / 2 ) ;
break ;
}
default :
{
_callStatusBarWindow . transform = CGAffineTransformIdentity ;
_callStatusBarWindow . frame = CGRectMake ( 0 , 0 , rootControllerFrame . size . width , CALL_STATUS _BAR _HEIGHT ) ;
break ;
}
}
// Apply the vertical offset due to call status bar
rootControllerFrame . origin . y = CALL_STATUS _BAR _HEIGHT ;
rootControllerFrame . size . height - = CALL_STATUS _BAR _HEIGHT ;
2015-05-11 21:11:23 +00:00
}
2016-09-02 20:21:02 +00:00
rootController . view . frame = rootControllerFrame ;
2016-09-06 15:20:16 +00:00
if ( rootController . presentedViewController )
{
rootController . presentedViewController . view . frame = rootControllerFrame ;
}
2015-05-11 21:11:23 +00:00
[ rootController . view setNeedsLayout ] ;
}
2015-11-17 23:15:52 +00:00
# pragma mark - SplitViewController delegate
2016-06-21 19:47:20 +00:00
- ( nullable UIViewController * ) splitViewController : ( UISplitViewController * ) splitViewController separateSecondaryViewControllerFromPrimaryViewController : ( UIViewController * ) primaryViewController
{
2017-03-28 15:26:50 +00:00
// Return the top view controller of the master navigation controller , if it is a navigation controller itself .
2017-03-23 16:48:05 +00:00
UIViewController * topViewController = _masterNavigationController . topViewController ;
2017-03-28 15:26:50 +00:00
if ( [ topViewController isKindOfClass : UINavigationController . class ] )
2016-06-21 19:47:20 +00:00
{
2017-03-28 15:26:50 +00:00
return topViewController ;
2016-06-21 19:47:20 +00:00
}
2017-04-21 14:28:57 +00:00
// Else return the default empty details view controller from the storyboard .
// Be sure that the primary is then visible too .
if ( splitViewController . displayMode = = UISplitViewControllerDisplayModePrimaryHidden )
2016-06-21 19:47:20 +00:00
{
2017-04-21 14:28:57 +00:00
splitViewController . preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible ;
2016-06-21 19:47:20 +00:00
}
2017-03-28 15:26:50 +00:00
UIStoryboard * storyboard = [ UIStoryboard storyboardWithName : @ "Main" bundle : [ NSBundle mainBundle ] ] ;
2017-08-14 13:57:23 +00:00
UIViewController * emptyDetailsViewController = [ storyboard instantiateViewControllerWithIdentifier : @ "EmptyDetailsViewControllerStoryboardId" ] ;
2019-01-11 10:45:27 +00:00
emptyDetailsViewController . view . backgroundColor = ThemeService . shared . theme . backgroundColor ;
2017-08-14 13:57:23 +00:00
return emptyDetailsViewController ;
2016-06-21 19:47:20 +00:00
}
2015-11-17 23:15:52 +00:00
- ( BOOL ) splitViewController : ( UISplitViewController * ) splitViewController collapseSecondaryViewController : ( UIViewController * ) secondaryViewController ontoPrimaryViewController : ( UIViewController * ) primaryViewController
{
2017-12-12 15:03:26 +00:00
if ( ! self . masterTabBarController . currentRoomViewController && ! self . masterTabBarController . currentContactDetailViewController && ! self . masterTabBarController . currentGroupDetailViewController )
2015-11-17 23:15:52 +00:00
{
// Return YES to indicate that we have handled the collapse by doing nothing ; the secondary controller will be discarded .
return YES ;
}
else
{
return NO ;
}
}
- ( BOOL ) splitViewController : ( UISplitViewController * ) svc shouldHideViewController : ( UIViewController * ) vc inOrientation : ( UIInterfaceOrientation ) orientation
{
// oniPad devices , force to display the primary and the secondary viewcontroller
// to avoid empty room View Controller in portrait orientation
// else , the user cannot select a room
return NO ;
}
2016-05-19 15:22:29 +00:00
# pragma mark - Status Bar Tap handling
- ( void ) touchesBegan : ( NSSet * ) touches withEvent : ( UIEvent * ) event
{
[ super touchesBegan : touches withEvent : event ] ;
UITouch * touch = [ touches anyObject ] ;
CGPoint point = [ touch locationInView : self . window ] ;
CGRect statusBarFrame = [ UIApplication sharedApplication ] . statusBarFrame ;
if ( CGRectContainsPoint ( statusBarFrame , point ) )
{
[ [ NSNotificationCenter defaultCenter ] postNotificationName : kAppDelegateDidTapStatusBarNotification object : nil ] ;
}
}
2016-05-20 12:31:23 +00:00
# pragma mark - No call support
/ * *
Display a "Call not supported" alert when the session receives a call invitation .
2017-07-14 14:41:25 +00:00
2016-05-20 12:31:23 +00:00
@ param mxSession the session to spy
* /
- ( void ) enableNoVoIPOnMatrixSession : ( MXSession * ) mxSession
{
// Listen to call events
2016-05-20 13:45:29 +00:00
callEventsListeners [ @ ( mxSession . hash ) ] =
2016-05-20 12:31:23 +00:00
[ mxSession listenToEventsOfTypes : @ [
kMXEventTypeStringCallInvite ,
kMXEventTypeStringCallCandidates ,
kMXEventTypeStringCallAnswer ,
kMXEventTypeStringCallHangup
]
2017-07-14 14:41:25 +00:00
onEvent : ^ ( MXEvent * event , MXTimelineDirection direction , id customObject ) {
if ( MXTimelineDirectionForwards = = direction )
{
switch ( event . eventType )
{
case MXEventTypeCallInvite :
{
if ( noCallSupportAlert )
{
[ noCallSupportAlert dismissViewControllerAnimated : NO completion : nil ] ;
}
MXCallInviteEventContent * callInviteEventContent = [ MXCallInviteEventContent modelFromJSON : event . content ] ;
// Sanity and invite expiration checks
if ( ! callInviteEventContent || event . age >= callInviteEventContent . lifetime )
{
return ;
}
MXUser * caller = [ mxSession userWithUserId : event . sender ] ;
NSString * callerDisplayname = caller . displayname ;
if ( ! callerDisplayname . length )
{
callerDisplayname = event . sender ;
}
2019-01-07 23:24:11 +00:00
NSString * appDisplayName = [ [ NSBundle mainBundle ] infoDictionary ] [ @ "CFBundleDisplayName" ] ;
2017-07-14 14:41:25 +00:00
NSString * message = [ NSString stringWithFormat : NSLocalizedStringFromTable ( @ "no_voip" , @ "Vector" , nil ) , callerDisplayname , appDisplayName ] ;
noCallSupportAlert = [ UIAlertController alertControllerWithTitle : NSLocalizedStringFromTable ( @ "no_voip_title" , @ "Vector" , nil )
message : message
preferredStyle : UIAlertControllerStyleAlert ] ;
__weak typeof ( self ) weakSelf = self ;
[ noCallSupportAlert addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "ignore" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> noCallSupportAlert = nil ;
}
} ] ] ;
[ noCallSupportAlert addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "reject_call" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
// Reject the call by sending the hangup event
NSDictionary * content = @ {
@ "call_id" : callInviteEventContent . callId ,
@ "version" : @ ( 0 )
} ;
2018-02-27 12:35:20 +00:00
[ mxSession . matrixRestClient sendEventToRoom : event . roomId eventType : kMXEventTypeStringCallHangup content : content txnId : nil success : nil failure : ^ ( NSError * error ) {
2017-07-14 14:41:25 +00:00
NSLog ( @ "[AppDelegate] enableNoVoIPOnMatrixSession: ERROR: Cannot send m.call.hangup event." ) ;
} ] ;
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> noCallSupportAlert = nil ;
}
} ] ] ;
[ self showNotificationAlert : noCallSupportAlert ] ;
break ;
}
case MXEventTypeCallAnswer :
case MXEventTypeCallHangup :
// The call has ended . The alert is no more needed .
if ( noCallSupportAlert )
{
[ noCallSupportAlert dismissViewControllerAnimated : YES completion : nil ] ;
noCallSupportAlert = nil ;
}
break ;
default :
break ;
}
}
} ] ;
2016-05-20 12:31:23 +00:00
}
- ( void ) disableNoVoIPOnMatrixSession : ( MXSession * ) mxSession
{
// Stop listening to the call events of this session
2016-05-20 13:45:29 +00:00
[ mxSession removeListener : callEventsListeners [ @ ( mxSession . hash ) ] ] ;
[ callEventsListeners removeObjectForKey : @ ( mxSession . hash ) ] ;
2016-05-20 12:31:23 +00:00
}
2017-11-14 17:21:01 +00:00
# pragma mark - Incoming room key requests handling
- ( void ) enableRoomKeyRequestObserver : ( MXSession * ) mxSession
{
roomKeyRequestObserver =
[ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXCryptoRoomKeyRequestNotification
object : mxSession . crypto
queue : [ NSOperationQueue mainQueue ]
usingBlock : ^ ( NSNotification * notif )
{
[ self checkPendingRoomKeyRequestsInSession : mxSession ] ;
} ] ;
roomKeyRequestCancellationObserver =
[ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXCryptoRoomKeyRequestCancellationNotification
object : mxSession . crypto
queue : [ NSOperationQueue mainQueue ]
usingBlock : ^ ( NSNotification * notif )
{
[ self checkPendingRoomKeyRequestsInSession : mxSession ] ;
} ] ;
}
- ( void ) disableRoomKeyRequestObserver : ( MXSession * ) mxSession
{
if ( roomKeyRequestObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : roomKeyRequestObserver ] ;
roomKeyRequestObserver = nil ;
}
if ( roomKeyRequestCancellationObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : roomKeyRequestCancellationObserver ] ;
roomKeyRequestCancellationObserver = nil ;
}
}
// Check if a key share dialog must be displayed for the given session
- ( void ) checkPendingRoomKeyRequestsInSession : ( MXSession * ) mxSession
{
if ( [ UIApplication sharedApplication ] . applicationState ! = UIApplicationStateActive )
{
NSLog ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession called while the app is not active. Ignore it." ) ;
return ;
}
[ mxSession . crypto pendingKeyRequests : ^ ( MXUsersDevicesMap < NSArray < MXIncomingRoomKeyRequest * > * > * pendingKeyRequests ) {
NSLog ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession: pendingKeyRequests.count: %@. Already displayed: %@" ,
@ ( pendingKeyRequests . count ) ,
roomKeyRequestViewController ? @ "YES" : @ "NO" ) ;
if ( roomKeyRequestViewController )
{
// Check if the current RoomKeyRequestViewController is still valid
MXSession * currentMXSession = roomKeyRequestViewController . mxSession ;
NSString * currentUser = roomKeyRequestViewController . device . userId ;
NSString * currentDevice = roomKeyRequestViewController . device . deviceId ;
NSArray < MXIncomingRoomKeyRequest * > * currentPendingRequest = [ pendingKeyRequests objectForDevice : currentDevice forUser : currentUser ] ;
if ( currentMXSession = = mxSession && currentPendingRequest . count = = 0 )
{
NSLog ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession: Cancel current dialog" ) ;
// The key request has been probably cancelled , remove the popup
[ roomKeyRequestViewController hide ] ;
roomKeyRequestViewController = nil ;
}
}
if ( ! roomKeyRequestViewController && pendingKeyRequests . count )
{
// Pick the first coming user / device pair
2017-12-05 14:47:39 +00:00
NSString * userId = pendingKeyRequests . userIds . firstObject ;
NSString * deviceId = [ pendingKeyRequests deviceIdsForUser : userId ] . firstObject ;
2017-11-14 17:21:01 +00:00
2017-12-05 14:47:39 +00:00
// Give the client a chance to refresh the device list
2018-02-23 16:16:48 +00:00
[ mxSession . crypto downloadKeys : @ [ userId ] forceDownload : NO success : ^ ( MXUsersDevicesMap < MXDeviceInfo * > * usersDevicesInfoMap ) {
2017-11-14 17:21:01 +00:00
2017-12-05 14:47:39 +00:00
MXDeviceInfo * deviceInfo = [ usersDevicesInfoMap objectForDevice : deviceId forUser : userId ] ;
if ( deviceInfo )
{
BOOL wasNewDevice = ( deviceInfo . verified = = MXDeviceUnknown ) ;
2017-11-14 17:21:01 +00:00
2019-01-05 02:28:03 +00:00
void ( ^ openDialog ) ( void ) = ^ void ( )
2017-12-05 14:47:39 +00:00
{
NSLog ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession: Open dialog for %@" , deviceInfo ) ;
2017-11-14 17:21:01 +00:00
2017-12-05 14:47:39 +00:00
roomKeyRequestViewController = [ [ RoomKeyRequestViewController alloc ] initWithDeviceInfo : deviceInfo wasNewDevice : wasNewDevice andMatrixSession : mxSession onComplete : ^ {
2017-11-14 17:21:01 +00:00
2017-12-05 14:47:39 +00:00
roomKeyRequestViewController = nil ;
// Check next pending key request , if any
[ self checkPendingRoomKeyRequests ] ;
} ] ;
[ roomKeyRequestViewController show ] ;
} ;
2017-11-14 17:21:01 +00:00
2017-12-05 14:47:39 +00:00
// If the device was new before , it ' s not any more .
if ( wasNewDevice )
{
[ mxSession . crypto setDeviceVerification : MXDeviceUnverified forDevice : deviceId ofUser : userId success : openDialog failure : nil ] ;
}
else
{
openDialog ( ) ;
}
}
else
{
NSLog ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession: No details found for device %@:%@" , userId , deviceId ) ;
// Ignore this device to avoid to loop on it
[ mxSession . crypto ignoreAllPendingKeyRequestsFromUser : userId andDevice : deviceId onComplete : ^ {
// And check next requests
[ self checkPendingRoomKeyRequests ] ;
} ] ;
}
} failure : ^ ( NSError * error ) {
// Retry later
NSLog ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession: Failed to download device keys. Retry" ) ;
[ self checkPendingRoomKeyRequests ] ;
2017-11-14 17:21:01 +00:00
} ] ;
}
} ] ;
}
// Check all opened MXSessions for key share dialog
- ( void ) checkPendingRoomKeyRequests
{
for ( MXSession * mxSession in mxSessionArray )
{
[ self checkPendingRoomKeyRequestsInSession : mxSession ] ;
}
}
2019-04-12 21:19:07 +00:00
# pragma mark - Incoming device verification requests handling
- ( void ) enableIncomingDeviceVerificationObserver : ( MXSession * ) mxSession
{
incomingDeviceVerificationObserver =
2019-04-24 06:07:50 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserverForName : MXDeviceVerificationManagerNewTransactionNotification
2019-04-12 21:19:07 +00:00
object : mxSession . crypto . deviceVerificationManager
queue : [ NSOperationQueue mainQueue ]
usingBlock : ^ ( NSNotification * notif )
{
2019-04-24 06:07:50 +00:00
NSObject * object = notif . userInfo [ MXDeviceVerificationManagerNotificationTransactionKey ] ;
2019-04-12 21:19:07 +00:00
if ( [ object isKindOfClass : MXIncomingSASTransaction . class ] )
{
2019-04-18 21:15:01 +00:00
[ self checkPendingIncomingDeviceVerificationsInSession : mxSession ] ;
2019-04-12 21:19:07 +00:00
}
} ] ;
}
- ( void ) disableIncomingDeviceVerificationObserver : ( MXSession * ) mxSession
{
if ( incomingDeviceVerificationObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : incomingDeviceVerificationObserver ] ;
incomingDeviceVerificationObserver = nil ;
}
}
// Check if an incoming device verification dialog must be displayed for the given session
- ( void ) checkPendingIncomingDeviceVerificationsInSession : ( MXSession * ) mxSession
{
if ( [ UIApplication sharedApplication ] . applicationState ! = UIApplicationStateActive )
{
2019-04-18 21:15:01 +00:00
NSLog ( @ "[AppDelegate][MXKeyVerification] checkPendingIncomingDeviceVerificationsInSession: called while the app is not active. Ignore it." ) ;
2019-04-12 21:19:07 +00:00
return ;
}
2019-04-18 21:15:01 +00:00
[ mxSession . crypto . deviceVerificationManager transactions : ^ ( NSArray < MXDeviceVerificationTransaction * > * _Nonnull transactions ) {
NSLog ( @ "[AppDelegate][MXKeyVerification] checkPendingIncomingDeviceVerificationsInSession: transactions: %@" , transactions ) ;
for ( MXDeviceVerificationTransaction * transaction in transactions )
{
if ( transaction . isIncoming )
{
MXIncomingSASTransaction * incomingTransaction = ( MXIncomingSASTransaction * ) transaction ;
if ( incomingTransaction . state = = MXSASTransactionStateIncomingShowAccept )
{
[ self presentIncomingDeviceVerification : incomingTransaction inSession : mxSession ] ;
break ;
}
}
}
} ] ;
2019-04-12 21:19:07 +00:00
}
// Check all opened MXSessions for incoming device verification dialog
- ( void ) checkPendingIncomingDeviceVerifications
{
for ( MXSession * mxSession in mxSessionArray )
{
[ self checkPendingIncomingDeviceVerificationsInSession : mxSession ] ;
}
}
- ( BOOL ) presentIncomingDeviceVerification : ( MXIncomingSASTransaction * ) transaction inSession : ( MXSession * ) mxSession
{
2019-04-18 21:15:01 +00:00
NSLog ( @ "[AppDelegate][MXKeyVerification] presentIncomingDeviceVerification: %@" , transaction ) ;
2019-04-12 21:19:07 +00:00
BOOL presented = NO ;
if ( ! deviceVerificationCoordinatorBridgePresenter )
{
UIViewController * presentingViewController = self . window . rootViewController . presentedViewController ? : self . window . rootViewController ;
deviceVerificationCoordinatorBridgePresenter = [ [ DeviceVerificationCoordinatorBridgePresenter alloc ] initWithSession : mxSession ] ;
deviceVerificationCoordinatorBridgePresenter . delegate = self ;
[ deviceVerificationCoordinatorBridgePresenter presentFrom : presentingViewController incomingTransaction : transaction animated : YES ] ;
presented = YES ;
}
2019-04-18 21:15:01 +00:00
else
{
NSLog ( @ "[AppDelegate][MXKeyVerification] presentIncomingDeviceVerification: Controller already presented." ) ;
}
2019-04-12 21:19:07 +00:00
return presented ;
}
2019-04-17 06:09:43 +00:00
- ( void ) deviceVerificationCoordinatorBridgePresenterDelegateDidComplete : ( DeviceVerificationCoordinatorBridgePresenter * ) coordinatorBridgePresenter otherUserId : ( NSString * _Nonnull ) otherUserId otherDeviceId : ( NSString * _Nonnull ) otherDeviceId
2019-04-12 21:19:07 +00:00
{
2019-04-24 06:55:45 +00:00
[ deviceVerificationCoordinatorBridgePresenter dismissWithAnimated : YES completion : ^ {
2019-04-18 21:15:01 +00:00
[ self checkPendingIncomingDeviceVerifications ] ;
2019-04-24 06:55:45 +00:00
} ] ;
deviceVerificationCoordinatorBridgePresenter = nil ;
2019-04-12 21:19:07 +00:00
}
2018-05-23 15:01:18 +00:00
# pragma mark - GDPR consent
// Observe user GDPR consent not given
- ( void ) registerUserConsentNotGivenNotification
{
[ NSNotificationCenter . defaultCenter addObserverForName : kMXHTTPClientUserConsentNotGivenErrorNotification
object : nil
queue : [ NSOperationQueue mainQueue ]
usingBlock : ^ ( NSNotification * notification )
{
NSString * consentURI = notification . userInfo [ kMXHTTPClientUserConsentNotGivenErrorNotificationConsentURIKey ] ;
if ( consentURI
2018-06-01 07:46:11 +00:00
&& self . gdprConsentNotGivenAlertController . presentingViewController = = nil
2018-08-08 09:30:50 +00:00
&& self . gdprConsentController . presentingViewController = = nil )
2018-05-23 15:01:18 +00:00
{
2018-06-01 07:46:11 +00:00
self . gdprConsentNotGivenAlertController = nil ;
2018-08-08 09:30:50 +00:00
self . gdprConsentController = nil ;
2018-06-01 07:46:11 +00:00
2018-05-23 15:01:18 +00:00
UIViewController * presentingViewController = self . window . rootViewController . presentedViewController ? : self . window . rootViewController ;
__weak typeof ( self ) weakSelf = self ;
MXSession * mainSession = self . mxSessions . firstObject ;
NSString * homeServerName = mainSession . matrixRestClient . credentials . homeServerName ;
NSString * alertMessage = [ NSString stringWithFormat : NSLocalizedStringFromTable ( @ "gdpr_consent_not_given_alert_message" , @ "Vector" , nil ) , homeServerName ] ;
UIAlertController * alert = [ UIAlertController alertControllerWithTitle : NSLocalizedStringFromTable ( @ "settings_term_conditions" , @ "Vector" , nil )
message : alertMessage
preferredStyle : UIAlertControllerStyleAlert ] ;
[ alert addAction : [ UIAlertAction actionWithTitle : NSLocalizedStringFromTable ( @ "gdpr_consent_not_given_alert_review_now_action" , @ "Vector" , nil )
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
typeof ( weakSelf ) strongSelf = weakSelf ;
if ( strongSelf )
{
[ strongSelf presentGDPRConsentFromViewController : presentingViewController consentURI : consentURI ] ;
}
} ] ] ;
[ alert addAction : [ UIAlertAction actionWithTitle : NSLocalizedStringFromTable ( @ "later" , @ "Vector" , nil )
style : UIAlertActionStyleCancel
handler : nil ] ] ;
[ presentingViewController presentViewController : alert animated : YES completion : nil ] ;
self . gdprConsentNotGivenAlertController = alert ;
}
} ] ;
}
- ( void ) presentGDPRConsentFromViewController : ( UIViewController * ) viewController consentURI : ( NSString * ) consentURI
{
2018-08-08 09:30:50 +00:00
GDPRConsentViewController * gdprConsentViewController = [ [ GDPRConsentViewController alloc ] initWithURL : consentURI ] ;
2018-05-23 15:01:18 +00:00
UIBarButtonItem * closeBarButtonItem = [ [ UIBarButtonItem alloc ] initWithTitle : [ NSBundle mxk_localizedStringForKey : @ "close" ]
style : UIBarButtonItemStylePlain
target : self
action : @ selector ( dismissGDPRConsent ) ] ;
2018-08-08 09:30:50 +00:00
gdprConsentViewController . navigationItem . leftBarButtonItem = closeBarButtonItem ;
2018-05-23 15:01:18 +00:00
2018-08-08 09:30:50 +00:00
UINavigationController * navigationController = [ [ UINavigationController alloc ] initWithRootViewController : gdprConsentViewController ] ;
2018-05-23 15:01:18 +00:00
[ viewController presentViewController : navigationController animated : YES completion : nil ] ;
2018-08-08 09:30:50 +00:00
self . gdprConsentController = navigationController ;
gdprConsentViewController . delegate = self ;
2018-05-23 15:01:18 +00:00
}
- ( void ) dismissGDPRConsent
{
2018-08-08 09:30:50 +00:00
[ self . gdprConsentController dismissViewControllerAnimated : YES completion : nil ] ;
}
# pragma mark - GDPRConsentViewControllerDelegate
2018-08-09 16:56:40 +00:00
- ( void ) gdprConsentViewControllerDidConsentToGDPRWithSuccess : ( GDPRConsentViewController * ) gdprConsentViewController
2018-08-08 09:30:50 +00:00
{
2018-08-08 16:36:24 +00:00
MXSession * session = mxSessionArray . firstObject ;
2018-08-24 10:31:57 +00:00
// Leave the GDPR consent right now
[ self dismissGDPRConsent ] ;
// And create the room with riot bot in //
2018-08-08 16:36:24 +00:00
self . onBoardingManager = [ [ OnBoardingManager alloc ] initWithSession : session ] ;
MXWeakify ( self ) ;
void ( ^ createRiotBotDMcompletion ) ( void ) = ^ ( ) {
MXStrongifyAndReturnIfNil ( self ) ;
2018-08-24 10:31:57 +00:00
2018-08-08 16:36:24 +00:00
self . onBoardingManager = nil ;
} ;
2018-08-09 12:47:15 +00:00
[ self . onBoardingManager createRiotBotDirectMessageIfNeededWithSuccess : ^ {
2018-08-08 16:36:24 +00:00
createRiotBotDMcompletion ( ) ;
} failure : ^ ( NSError * _Nonnull error ) {
createRiotBotDMcompletion ( ) ;
} ] ;
2018-05-23 15:01:18 +00:00
}
2019-08-30 09:15:06 +00:00
# pragma mark - Identity server service terms
// Observe identity server terms not signed notification
- ( void ) registerIdentityServiceTermsNotSignedNotification
{
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( handleIdentityServiceTermsNotSignedNotification : ) name : MXIdentityServiceTermsNotSignedNotification object : nil ] ;
}
- ( void ) handleIdentityServiceTermsNotSignedNotification : ( NSNotification * ) notification
{
2019-10-10 09:59:34 +00:00
NSLog ( @ "[AppDelegate] IS Terms: handleIdentityServiceTermsNotSignedNotification." ) ;
2019-08-30 09:15:06 +00:00
NSString * baseURL ;
NSString * accessToken ;
MXJSONModelSetString ( baseURL , notification . userInfo [ MXIdentityServiceNotificationIdentityServerKey ] ) ;
MXJSONModelSetString ( accessToken , notification . userInfo [ MXIdentityServiceNotificationAccessTokenKey ] ) ;
[ self presentIdentityServerTermsWithBaseURL : baseURL andAccessToken : accessToken ] ;
}
- ( void ) presentIdentityServerTermsWithBaseURL : ( NSString * ) baseURL andAccessToken : ( NSString * ) accessToken
{
MXSession * mxSession = self . mxSessions . firstObject ;
if ( ! mxSession || ! baseURL || ! accessToken || self . serviceTermsModalCoordinatorBridgePresenter . isPresenting )
{
return ;
}
ServiceTermsModalCoordinatorBridgePresenter * serviceTermsModalCoordinatorBridgePresenter = [ [ ServiceTermsModalCoordinatorBridgePresenter alloc ] initWithSession : mxSession
baseUrl : baseURL
serviceType : MXServiceTypeIdentityService
2019-10-10 09:03:13 +00:00
outOfContext : YES
2019-08-30 09:15:06 +00:00
accessToken : accessToken ] ;
serviceTermsModalCoordinatorBridgePresenter . delegate = self ;
UIViewController * presentingViewController = self . window . rootViewController . presentedViewController ? : self . window . rootViewController ;
[ serviceTermsModalCoordinatorBridgePresenter presentFrom : presentingViewController animated : YES ] ;
self . serviceTermsModalCoordinatorBridgePresenter = serviceTermsModalCoordinatorBridgePresenter ;
}
- ( void ) serviceTermsModalCoordinatorBridgePresenterDelegateDidAccept : ( ServiceTermsModalCoordinatorBridgePresenter * _Nonnull ) coordinatorBridgePresenter
{
[ coordinatorBridgePresenter dismissWithAnimated : YES completion : ^ {
} ] ;
self . serviceTermsModalCoordinatorBridgePresenter = nil ;
}
2019-10-10 09:59:34 +00:00
- ( void ) serviceTermsModalCoordinatorBridgePresenterDelegateDidDecline : ( ServiceTermsModalCoordinatorBridgePresenter * ) coordinatorBridgePresenter session : ( MXSession * ) session
{
NSLog ( @ "[AppDelegate] IS Terms: User has declined the use of the default IS." ) ;
// The user does not want to use the proposed IS .
// Disable IS feature on user ' s account
[ session setIdentityServer : nil andAccessToken : nil ] ;
[ session setAccountDataIdentityServer : nil success : ^ {
} failure : ^ ( NSError * error ) {
NSLog ( @ "[AppDelegate] IS Terms: Error: %@" , error ) ;
} ] ;
[ coordinatorBridgePresenter dismissWithAnimated : YES completion : ^ {
} ] ;
self . serviceTermsModalCoordinatorBridgePresenter = nil ;
}
2019-08-30 09:15:06 +00:00
- ( void ) serviceTermsModalCoordinatorBridgePresenterDelegateDidCancel : ( ServiceTermsModalCoordinatorBridgePresenter * _Nonnull ) coordinatorBridgePresenter
{
[ coordinatorBridgePresenter dismissWithAnimated : YES completion : ^ {
} ] ;
self . serviceTermsModalCoordinatorBridgePresenter = nil ;
}
2018-07-02 12:41:55 +00:00
# pragma mark - Settings
- ( void ) setupUserDefaults
{
2018-07-03 08:31:14 +00:00
// Register "Riot-Defaults.plist" default values
2018-07-02 12:41:55 +00:00
NSString * userDefaults = [ [ NSBundle mainBundle ] objectForInfoDictionaryKey : @ "UserDefaults" ] ;
NSString * defaultsPathFromApp = [ [ NSBundle mainBundle ] pathForResource : userDefaults ofType : @ "plist" ] ;
NSDictionary * defaults = [ NSDictionary dictionaryWithContentsOfFile : defaultsPathFromApp ] ;
[ [ NSUserDefaults standardUserDefaults ] registerDefaults : defaults ] ;
// Now use RiotSettings and NSUserDefaults to store ` showDecryptedContentInNotifications` setting option
2018-07-03 08:31:14 +00:00
// Migrate this information from main MXKAccount to RiotSettings , if value is not in UserDefaults
2018-07-02 12:41:55 +00:00
2018-07-03 08:31:14 +00:00
if ( ! RiotSettings . shared . isShowDecryptedContentInNotificationsHasBeenSetOnce )
2018-07-02 12:41:55 +00:00
{
2018-07-03 08:31:14 +00:00
MXKAccount * currentAccount = [ MXKAccountManager sharedManager ] . activeAccounts . firstObject ;
2018-07-02 12:41:55 +00:00
RiotSettings . shared . showDecryptedContentInNotifications = currentAccount . showDecryptedContentInNotifications ;
}
}
2014-10-02 15:02:47 +00:00
@ end