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 .
* /
2020-08-31 17:22:37 +00:00
# import "LegacyAppDelegate.h"
2015-08-21 18:00:39 +00:00
2017-08-22 13:02:16 +00:00
# import < Intents / Intents . 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"
2020-11-05 11:09:10 +00:00
# import "DecryptionFailureTracker.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
2021-10-26 15:42:33 +00:00
# import "GeneratedInterface-Swift.h"
2020-08-31 17:22:37 +00:00
# import "PushNotificationService.h"
2018-07-02 12:41:55 +00:00
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
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
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" ;
2020-04-28 16:52:03 +00:00
NSString * const AppDelegateUniversalLinkDidChangeNotification = @ "AppDelegateUniversalLinkDidChangeNotification" ;
2021-09-30 10:14:02 +00:00
@ interface LegacyAppDelegate ( ) < GDPRConsentViewControllerDelegate , KeyVerificationCoordinatorBridgePresenterDelegate , PushNotificationServiceDelegate , SetPinCoordinatorBridgePresenterDelegate , CallPresenterDelegate , SpaceDetailPresenterDelegate >
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
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
/ * *
2020-03-18 16:39:26 +00:00
Incoming key verification requests observers
2019-04-12 21:19:07 +00:00
* /
2020-03-18 16:39:26 +00:00
id incomingKeyVerificationObserver ;
2019-04-12 21:19:07 +00:00
/ * *
2020-03-18 16:39:26 +00:00
If any the currently displayed key verification dialog
2019-04-12 21:19:07 +00:00
* /
2020-03-18 16:39:26 +00:00
KeyVerificationCoordinatorBridgePresenter * keyVerificationCoordinatorBridgePresenter ;
2019-04-12 21:19:07 +00:00
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-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-12-29 12:04:57 +00:00
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 ;
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
2020-01-17 09:52:26 +00:00
@ property ( weak , nonatomic ) UIAlertController * incomingKeyVerificationRequestAlertController ;
2019-11-28 16:38:19 +00:00
@ property ( nonatomic , strong ) SlidingModalPresenter * slidingModalPresenter ;
2020-07-21 13:16:52 +00:00
@ property ( nonatomic , strong ) SetPinCoordinatorBridgePresenter * setPinCoordinatorBridgePresenter ;
2021-09-22 12:58:19 +00:00
@ property ( nonatomic , strong ) SpaceDetailPresenter * spaceDetailPresenter ;
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 ;
2020-03-27 14:41:19 +00:00
@ property ( nonatomic , weak ) id userDidSignInOnNewDeviceObserver ;
@ property ( weak , nonatomic ) UIAlertController * userNewSignInAlertController ;
2020-10-13 15:15:47 +00:00
@ property ( nonatomic , weak ) id userDidChangeCrossSigningKeysObserver ;
2020-04-30 12:51:29 +00:00
/ * *
Related push notification service instance . Will be created when launch finished .
* /
@ property ( nonatomic , strong ) PushNotificationService * pushNotificationService ;
2020-08-20 14:43:46 +00:00
@ property ( nonatomic , strong ) PushNotificationStore * pushNotificationStore ;
2020-07-22 14:47:45 +00:00
@ property ( nonatomic , strong ) LocalAuthenticationService * localAuthenticationService ;
2021-03-18 23:57:58 +00:00
@ property ( nonatomic , strong , readwrite ) CallPresenter * callPresenter ;
2020-04-30 12:51:29 +00:00
2020-07-10 19:28:57 +00:00
@ property ( nonatomic , strong ) MajorUpdateManager * majorUpdateManager ;
2021-04-29 12:37:07 +00:00
@ property ( nonatomic , strong ) SpaceFeatureUnavailablePresenter * spaceFeatureUnavailablePresenter ;
2021-06-04 10:06:27 +00:00
@ property ( nonatomic , strong ) AppInfo * appInfo ;
2021-09-08 08:21:04 +00:00
/ * *
Listen RecentsViewControllerDataReadyNotification for changes .
* /
@ property ( nonatomic , assign , getter = isRoomListDataReady ) BOOL roomListDataReady ;
2021-09-08 11:06:41 +00:00
/ * *
Flag to indicate whether a cache clear is being performed .
* /
@ property ( nonatomic , assign , getter = isClearingCache ) BOOL clearingCache ;
2014-10-02 15:02:47 +00:00
@ end
2020-08-31 17:22:37 +00:00
@ implementation LegacyAppDelegate
2014-10-02 15:02:47 +00:00
2014-10-08 12:14:12 +00:00
# pragma mark -
2018-01-02 10:56:30 +00:00
+ ( void ) initialize
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] initialize" ) ;
2020-07-28 14:17:22 +00:00
// Set static application settings
2020-07-29 08:13:43 +00:00
[ [ AppConfiguration new ] setupSettings ] ;
2021-06-03 08:30:07 +00:00
MXLogConfiguration * configuration = [ [ MXLogConfiguration alloc ] init ] ;
configuration . logLevel = MXLogLevelVerbose ;
configuration . logFilesSizeLimit = 100 * 1024 * 1024 ; // 100 MB
configuration . maxLogFilesCount = 50 ;
2018-01-02 10:56:30 +00:00
// Redirect NSLogs to files only if we are not debugging
2021-06-03 08:30:07 +00:00
if ( ! isatty ( STDERR_FILENO ) ) {
configuration . redirectLogsToFiles = YES ;
2018-01-02 10:56:30 +00:00
}
2021-06-03 08:30:07 +00:00
[ MXLog configure : configuration ] ;
2018-01-02 10:56:30 +00:00
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] initialize: Done" ) ;
2018-01-02 10:56:30 +00:00
}
2020-08-31 17:22:37 +00:00
+ ( instancetype ) theDelegate
2015-06-18 09:19:42 +00:00
{
2020-08-31 17:22:37 +00:00
static id sharedInstance = nil ;
static dispatch_once _t onceToken ;
dispatch_once ( & onceToken , ^ {
sharedInstance = [ [ self alloc ] init ] ;
} ) ;
return sharedInstance ;
2014-10-08 12:14:12 +00:00
}
2015-02-04 17:18:38 +00:00
2020-08-31 17:22:37 +00:00
2020-04-30 12:43:08 +00:00
# pragma mark - Push Notifications
- ( void ) registerForRemoteNotificationsWithCompletion : ( nullable void ( ^ ) ( NSError * ) ) completion
{
2020-04-30 12:51:29 +00:00
[ self . pushNotificationService registerForRemoteNotificationsWithCompletion : completion ] ;
2020-04-30 12:43:08 +00:00
}
2020-05-22 11:27:30 +00:00
- ( void ) application : ( UIApplication * ) application didRegisterForRemoteNotificationsWithDeviceToken : ( NSData * ) deviceToken
{
[ self . pushNotificationService didRegisterForRemoteNotificationsWithDeviceToken : deviceToken ] ;
NSString * deviceTokenString = [ [ [ [ deviceToken description ]
stringByReplacingOccurrencesOfString : @ "<" withString : @ "" ]
stringByReplacingOccurrencesOfString : @ ">" withString : @ "" ]
stringByReplacingOccurrencesOfString : @ " " withString : @ "" ] ;
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "The generated device token string is : %@" , deviceTokenString ) ;
2020-05-22 11:27:30 +00:00
}
- ( void ) application : ( UIApplication * ) application didFailToRegisterForRemoteNotificationsWithError : ( NSError * ) error
{
[ self . pushNotificationService didFailToRegisterForRemoteNotificationsWithError : error ] ;
}
- ( void ) application : ( UIApplication * ) application didReceiveRemoteNotification : ( NSDictionary * ) userInfo fetchCompletionHandler : ( void ( ^ ) ( UIBackgroundFetchResult ) ) completionHandler
{
[ self . pushNotificationService didReceiveRemoteNotification : userInfo fetchCompletionHandler : completionHandler ] ;
}
2015-02-04 17:18:38 +00:00
# pragma mark -
2015-06-18 09:19:42 +00:00
- ( NSString * ) appVersion
{
2021-06-04 10:06:27 +00:00
return self . appInfo . appVersion . bundleShortVersion ;
2015-02-04 17:18:38 +00:00
}
2015-06-18 09:19:42 +00:00
- ( NSString * ) build
{
2021-06-04 10:06:27 +00:00
return self . appInfo . buildInfo . readableBuildVersion ;
2015-02-04 17:18:38 +00:00
}
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
}
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
2019-11-22 15:07:39 +00:00
NSURL * messageSoundURL = [ [ NSBundle mainBundle ] URLForResource : @ "message" withExtension : @ "caf" ] ;
2018-04-08 14:00:33 +00:00
AudioServicesCreateSystemSoundID ( ( __bridge CFURLRef ) messageSoundURL , & _messageSound ) ;
2021-07-05 18:19:51 +00:00
// Set app info now as Mac ( Designed for iPad ) accesses it before didFinishLaunching is called
self . appInfo = AppInfo . current ;
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] willFinishLaunchingWithOptions: Done" ) ;
2017-12-19 13:44:10 +00:00
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
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] didFinishLaunchingWithOptions: %@" , launchOptions ) ;
2016-03-23 13:45:16 +00:00
# else
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] didFinishLaunchingWithOptions" ) ;
2016-03-23 13:45:16 +00:00
# endif
2017-12-19 13:44:10 +00:00
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] didFinishLaunchingWithOptions: isProtectedDataAvailable: %@" , @ ( [ application isProtectedDataAvailable ] ) ) ;
2017-12-19 13:44:10 +00:00
2020-07-29 08:13:43 +00:00
_configuration = [ AppConfiguration new ] ;
2021-09-08 11:06:41 +00:00
self . clearingCache = NO ;
2020-07-29 08:03:37 +00:00
2017-01-24 14:46:46 +00:00
// Log app information
2021-06-04 10:06:27 +00:00
NSString * appDisplayName = self . appInfo . displayName ;
NSString * appVersion = self . appVersion ;
NSString * build = self . build ;
2017-07-14 14:41:25 +00:00
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "------------------------------" ) ;
MXLogDebug ( @ "Application info:" ) ;
MXLogDebug ( @ "%@ version: %@" , appDisplayName , appVersion ) ;
MXLogDebug ( @ "MatrixKit version: %@" , MatrixKitVersion ) ;
MXLogDebug ( @ "MatrixSDK version: %@" , MatrixSDKVersion ) ;
MXLogDebug ( @ "Build: %@\n" , build ) ;
MXLogDebug ( @ "------------------------------\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-17 23:15:52 +00:00
mxSessionArray = [ NSMutableArray array ] ;
2016-05-20 12:31:23 +00:00
callEventsListeners = [ NSMutableDictionary dictionary ] ;
2020-04-14 12:54:36 +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 ;
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
// 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 ;
2020-04-28 15:31:49 +00:00
_handleSelfVerificationRequest = YES ;
2015-11-17 23:15:52 +00:00
2018-06-27 07:55:06 +00:00
// Configure our analytics . It will indeed start if the option is enabled
2020-11-06 21:55:32 +00:00
Analytics * analytics = [ Analytics sharedInstance ] ;
[ MXSDKOptions sharedInstance ] . analyticsDelegate = analytics ;
2018-06-29 05:35:31 +00:00
[ DecryptionFailureTracker sharedInstance ] . delegate = [ Analytics sharedInstance ] ;
2020-11-06 21:55:32 +00:00
MXBaseProfiler * profiler = [ MXBaseProfiler new ] ;
profiler . analytics = analytics ;
[ MXSDKOptions sharedInstance ] . profiler = profiler ;
[ analytics start ] ;
2020-04-14 12:54:36 +00:00
2020-07-22 14:47:45 +00:00
self . localAuthenticationService = [ [ LocalAuthenticationService alloc ] initWithPinCodePreferences : [ PinCodePreferences shared ] ] ;
2020-12-01 10:41:31 +00:00
2021-02-07 16:55:36 +00:00
self . callPresenter = [ [ CallPresenter alloc ] init ] ;
self . callPresenter . delegate = self ;
2020-04-14 12:54:36 +00:00
2020-08-20 14:43:46 +00:00
self . pushNotificationStore = [ PushNotificationStore new ] ;
self . pushNotificationService = [ [ PushNotificationService alloc ] initWithPushNotificationStore : self . pushNotificationStore ] ;
2020-08-11 18:18:13 +00:00
self . pushNotificationService . delegate = self ;
2021-05-04 08:41:02 +00:00
self . spaceFeatureUnavailablePresenter = [ SpaceFeatureUnavailablePresenter new ] ;
2020-08-11 18:18:13 +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
2020-07-23 15:25:24 +00:00
# ifdef CALL_STACK _JINGLE
2019-04-19 16:33:21 +00:00
// Setup Jitsi
2020-07-31 09:28:57 +00:00
[ JitsiService . shared configureDefaultConferenceOptionsWith : BuildSettings . jitsiServerUrl ] ;
2019-04-19 16:33:21 +00:00
[ JitsiService . shared application : application didFinishLaunchingWithOptions : launchOptions ] ;
2020-07-23 15:25:24 +00:00
# endif
2020-07-10 19:28:57 +00:00
self . majorUpdateManager = [ MajorUpdateManager new ] ;
2017-12-28 13:23:12 +00:00
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] didFinishLaunchingWithOptions: Done in %.0fms" , [ [ NSDate date ] timeIntervalSinceDate : startDate ] * 1000 ) ;
2017-12-28 13:23:12 +00:00
2020-09-07 15:10:54 +00:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self configurePinCodeScreenFor : application createIfRequired : YES ] ;
} ) ;
2014-10-02 15:02:47 +00:00
return YES ;
}
2015-06-18 09:19:42 +00:00
- ( void ) applicationWillResignActive : ( UIApplication * ) application
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] applicationWillResignActive" ) ;
2016-02-03 11:21:45 +00:00
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
2020-08-11 18:18:13 +00:00
[ self . pushNotificationService applicationWillResignActive ] ;
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 ;
}
2020-09-07 15:10:54 +00:00
2020-09-30 13:03:13 +00:00
if ( [ self . localAuthenticationService isProtectionSet ] && ! [ BiometricsAuthenticationPresenter isPresenting ] )
2020-09-07 15:10:54 +00:00
{
if ( self . setPinCoordinatorBridgePresenter )
{
// it ' s already on screen , convert the viewMode
self . setPinCoordinatorBridgePresenter . viewMode = SetPinCoordinatorViewModeInactive ;
return ;
}
self . setPinCoordinatorBridgePresenter = [ [ SetPinCoordinatorBridgePresenter alloc ] initWithSession : mxSessionArray . firstObject viewMode : SetPinCoordinatorViewModeInactive ] ;
self . setPinCoordinatorBridgePresenter . delegate = self ;
[ self . setPinCoordinatorBridgePresenter presentIn : self . window ] ;
}
2014-12-09 17:21:39 +00:00
}
2015-06-18 09:19:42 +00:00
- ( void ) applicationDidEnterBackground : ( UIApplication * ) application
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] applicationDidEnterBackground" ) ;
2016-02-03 11:21:45 +00:00
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
2021-08-23 16:56:24 +00:00
// Remove expired URL previews from the cache
2021-09-08 14:10:13 +00:00
[ URLPreviewService . shared removeExpiredCacheData ] ;
2021-08-23 16:56:24 +00:00
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
2020-08-11 18:18:13 +00:00
[ self . pushNotificationService applicationDidEnterBackground ] ;
2020-11-06 21:55:32 +00:00
// Pause profiling
[ MXSDKOptions . sharedInstance . profiler pause ] ;
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
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] applicationWillEnterForeground" ) ;
2016-02-03 11:21:45 +00:00
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 .
2020-04-14 12:54:36 +00:00
2020-11-06 21:55:32 +00:00
[ MXSDKOptions . sharedInstance . profiler resume ] ;
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
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] applicationDidBecomeActive" ) ;
2016-04-06 09:28:12 +00:00
2020-08-10 15:20:02 +00:00
[ self . pushNotificationService applicationDidBecomeActive ] ;
2020-09-07 15:10:54 +00:00
[ self configurePinCodeScreenFor : application createIfRequired : NO ] ;
}
- ( void ) configurePinCodeScreenFor : ( UIApplication * ) application
createIfRequired : ( BOOL ) createIfRequired
{
2020-07-22 14:47:45 +00:00
if ( [ self . localAuthenticationService shouldShowPinCode ] )
2020-07-21 13:16:52 +00:00
{
2020-07-22 14:47:45 +00:00
if ( self . setPinCoordinatorBridgePresenter )
2020-07-21 13:16:52 +00:00
{
2020-09-07 15:10:54 +00:00
// it ' s already on screen , convert the viewMode
self . setPinCoordinatorBridgePresenter . viewMode = SetPinCoordinatorViewModeUnlock ;
2020-07-22 14:47:45 +00:00
return ;
2020-07-21 13:16:52 +00:00
}
2020-09-07 15:10:54 +00:00
if ( createIfRequired )
{
self . setPinCoordinatorBridgePresenter = [ [ SetPinCoordinatorBridgePresenter alloc ] initWithSession : mxSessionArray . firstObject viewMode : SetPinCoordinatorViewModeUnlock ] ;
self . setPinCoordinatorBridgePresenter . delegate = self ;
[ self . setPinCoordinatorBridgePresenter presentIn : self . window ] ;
}
}
else
{
[ self . setPinCoordinatorBridgePresenter dismiss ] ;
self . setPinCoordinatorBridgePresenter = nil ;
2020-07-22 19:45:20 +00:00
[ self afterAppUnlockedByPin : application ] ;
2020-07-21 13:16:52 +00:00
}
}
- ( void ) afterAppUnlockedByPin : ( UIApplication * ) application
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] afterAppUnlockedByPin" ) ;
2020-07-21 13:16:52 +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
2021-09-28 05:40:01 +00:00
userInfo : @ { NSLocalizedDescriptionKey : [ VectorL10n homeserverConnectionLost ] } ] ;
2018-08-13 15:14:32 +00:00
[ 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 ] ;
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
2021-09-28 05:40:01 +00:00
[ [ AppDelegate theDelegate ] showErrorAsAlert : [ NSError errorWithDomain : NSURLErrorDomain code : NSURLErrorNotConnectedToInternet userInfo : @ { NSLocalizedDescriptionKey : [ VectorL10n networkOfflinePrompt ] } ] ] ;
2016-02-11 14:05:24 +00:00
}
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 ;
2021-09-01 09:44:15 +00:00
// Riot has its own dark theme . Prevent iOS from applying its one
[ application keyWindow ] . accessibilityIgnoresInvertColors = YES ;
2017-01-31 23:18:01 +00:00
2020-09-29 13:15:32 +00:00
[ self handleAppState ] ;
2014-10-02 15:02:47 +00:00
}
2015-06-18 09:19:42 +00:00
- ( void ) applicationWillTerminate : ( UIApplication * ) application
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[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
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] applicationDidReceiveMemoryWarning" ) ;
2017-10-05 07:36:40 +00:00
}
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
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[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 )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] restoreInitialDisplay: keep visible log in again" ) ;
2016-12-16 12:43:08 +00:00
[ self showNotificationAlert : cryptoDataCorruptedAlert ] ;
}
2019-02-15 16:29:48 +00:00
else if ( wrongBackupVersionAlert )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] restoreInitialDisplay: keep visible wrongBackupVersionAlert" ) ;
2019-02-15 16:29:48 +00:00
[ 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
{
2020-09-04 11:17:01 +00:00
[ self . delegate legacyAppDelegateRestoreEmptyDetailsViewController : self ] ;
2017-12-21 16:19:36 +00:00
}
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
{
2021-09-28 05:40:01 +00:00
title = [ MatrixKitL10n error ] ;
2016-02-11 13:06:48 +00:00
}
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 ] ;
2021-09-28 05:40:01 +00:00
[ _errorNotification addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n ok ]
2017-07-14 14:41:25 +00:00
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
{
2020-04-03 14:33:56 +00:00
[ alert popoverPresentationController ] . sourceView = self . presentedViewController . view ;
[ alert popoverPresentationController ] . sourceRect = self . presentedViewController . view . bounds ;
[ self . presentedViewController 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
2021-09-28 05:40:01 +00:00
message : [ VectorL10n e2eNeedLogInAgain ]
2017-07-14 14:41:25 +00:00
preferredStyle : UIAlertControllerStyleAlert ] ;
2016-12-16 12:43:08 +00:00
__weak typeof ( self ) weakSelf = self ;
2017-07-14 14:41:25 +00:00
2021-09-28 05:40:01 +00:00
[ cryptoDataCorruptedAlert addAction : [ UIAlertAction actionWithTitle : [ VectorL10n later ]
2017-07-14 14:41:25 +00:00
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> cryptoDataCorruptedAlert = nil ;
}
} ] ] ;
2021-09-28 05:40:01 +00:00
[ cryptoDataCorruptedAlert addAction : [ UIAlertAction actionWithTitle : [ VectorL10n settingsSignOut ]
2017-07-14 14:41:25 +00:00
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 ] ;
}
2021-09-28 05:40:01 +00:00
wrongBackupVersionAlert = [ UIAlertController alertControllerWithTitle : [ VectorL10n e2eKeyBackupWrongVersionTitle ]
message : [ VectorL10n e2eKeyBackupWrongVersion ]
preferredStyle : UIAlertControllerStyleAlert ] ;
2017-12-29 09:31:16 +00:00
2020-04-14 12:54:36 +00:00
MXWeakify ( self ) ;
2021-09-28 05:40:01 +00:00
[ wrongBackupVersionAlert addAction : [ UIAlertAction actionWithTitle : [ VectorL10n e2eKeyBackupWrongVersionButtonSettings ]
2020-04-14 12:54:36 +00:00
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action )
{
MXStrongifyAndReturnIfNil ( self ) ;
self -> wrongBackupVersionAlert = nil ;
2017-12-29 12:04:57 +00:00
2020-04-14 12:54:36 +00:00
// TODO : Open settings
} ] ] ;
2017-12-29 09:31:16 +00:00
2021-09-28 05:40:01 +00:00
[ wrongBackupVersionAlert addAction : [ UIAlertAction actionWithTitle : [ VectorL10n e2eKeyBackupWrongVersionButtonWasme ]
2020-04-14 12:54:36 +00:00
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action )
{
MXStrongifyAndReturnIfNil ( self ) ;
self -> wrongBackupVersionAlert = nil ;
} ] ] ;
[ self showNotificationAlert : wrongBackupVersionAlert ] ;
2017-12-29 09:31:16 +00:00
}
}
2020-04-14 12:54:36 +00:00
# pragma mark
2017-12-29 12:04:57 +00:00
2020-04-14 12:54:36 +00:00
- ( void ) popToHomeViewControllerAnimated : ( BOOL ) animated completion : ( void ( ^ ) ( void ) ) completion
2017-12-29 12:04:57 +00:00
{
2020-09-04 11:17:01 +00:00
[ self . delegate legacyAppDelegate : self wantsToPopToHomeViewControllerAnimated : animated completion : completion ] ;
2020-04-14 12:54:36 +00:00
}
2019-04-02 22:29:54 +00:00
2020-04-14 12:54:36 +00:00
# pragma mark - Crash handling
2019-04-02 22:29:54 +00:00
2020-04-14 12:54:36 +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 )
2019-03-22 09:21:16 +00:00
{
2020-04-14 12:54:36 +00:00
// Do not show the crash report dialog if it is already displayed
if ( [ self . window . rootViewController . childViewControllers [ 0 ] isKindOfClass : [ UINavigationController class ] ]
&& [ ( ( UINavigationController * ) self . window . rootViewController . childViewControllers [ 0 ] ) . visibleViewController isKindOfClass : [ BugReportViewController class ] ] )
2019-03-22 09:21:16 +00:00
{
2020-04-14 12:54:36 +00:00
return ;
2019-03-22 09:21:16 +00:00
}
2020-04-14 12:54:36 +00:00
NSString * description = [ [ NSString alloc ] initWithContentsOfFile : filePath
usedEncoding : nil
error : nil ] ;
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] Promt user to report crash:\n%@" , description ) ;
2019-04-02 22:29:54 +00:00
2020-04-14 12:54:36 +00:00
// Ask the user to send a crash report
[ [ RageShakeManager sharedManager ] promptCrashReportInViewController : self . window . rootViewController ] ;
2019-03-22 09:21:16 +00:00
}
}
2020-04-14 12:54:36 +00:00
# pragma mark - PushNotificationServiceDelegate
2020-06-15 10:49:22 +00:00
- ( void ) pushNotificationService : ( PushNotificationService * ) pushNotificationService shouldNavigateToRoomWithId : ( NSString * ) roomId
2019-04-05 12:51:56 +00:00
{
2020-09-23 13:53:00 +00:00
_lastNavigatedRoomIdFromPush = roomId ;
2020-06-15 10:49:22 +00:00
[ self navigateToRoomById : roomId ] ;
2020-04-14 12:54:36 +00:00
}
# pragma mark - Badge Count
- ( void ) refreshApplicationIconBadgeNumber
{
// Consider the total number of missed discussions including the invites .
NSUInteger count = [ self . masterTabBarController missedDiscussionsCount ] ;
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] refreshApplicationIconBadgeNumber: %tu" , count ) ;
2020-04-14 12:54:36 +00:00
[ UIApplication sharedApplication ] . applicationIconBadgeNumber = count ;
2019-04-05 12:51:56 +00:00
}
2016-04-12 08:23:48 +00:00
# pragma mark - Universal link
- ( BOOL ) handleUniversalLink : ( NSUserActivity * ) userActivity
{
NSURL * webURL = userActivity . webpageURL ;
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[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
2020-04-28 16:52:03 +00:00
// Extract required parameters from the link
NSArray < NSString * > * pathParams ;
NSMutableDictionary * queryParams ;
[ self parseUniversalLinkFragment : webURL . absoluteString outPathParams : & pathParams outQueryParams : & queryParams ] ;
UniversalLink * newLink = [ [ UniversalLink alloc ] initWithUrl : webURL pathParams : pathParams queryParams : queryParams ] ;
if ( ! [ _lastHandledUniversalLink isEqual : newLink ] )
{
_lastHandledUniversalLink = [ [ UniversalLink alloc ] initWithUrl : webURL pathParams : pathParams queryParams : queryParams ] ;
// notify this change
[ [ NSNotificationCenter defaultCenter ] postNotificationName : AppDelegateUniversalLinkDidChangeNotification object : nil ] ;
}
2021-02-02 16:59:39 +00:00
if ( [ self handleServerProvionningLink : webURL ] )
2019-09-10 09:51:49 +00:00
{
2021-02-02 16:59:39 +00:00
return YES ;
2019-09-10 09:51:49 +00:00
}
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 ] ;
2020-04-28 10:04:47 +00:00
// Manage email validation links from homeserver for registration ( / registration / email / submit_token )
// and email addition ( / add_threepid / email / submit_token )
2020-04-28 09:22:04 +00:00
// They look like https : // matrix . org / _matrix / client / unstable / registration / email / submit_token ? token = vtQjQIZfwdoREDACTEDozrmKYSWlCXsJ & client_secret = 53 e679ea - oRED - ACTED -92 b8 -3012 c49c6cfa & sid = qlBCREDACTEDEtgxD
2020-04-28 10:04:47 +00:00
if ( [ webURL . path hasSuffix : @ "/email/submit_token" ] )
2020-04-28 09:22:04 +00:00
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] handleUniversalLink: Validate link" ) ;
2020-04-28 09:22:04 +00:00
// We just need to ping the link .
// The app should be in the registration flow at the "waiting for email validation" polling state . The server
// will indicate the email is validated through this polling API . Then , the app will go to the next flow step .
NSURLSessionConfiguration * conf = [ NSURLSessionConfiguration defaultSessionConfiguration ] ;
NSURLSession * urlSession = [ NSURLSession sessionWithConfiguration : conf ] ;
NSURLSessionDataTask * task = [ urlSession dataTaskWithURL : webURL completionHandler : ^ ( NSData * _Nullable data , NSURLResponse * _Nullable response , NSError * _Nullable error ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] handleUniversalLink: Link validation response: %@\nData: %@" , response ,
2020-04-28 09:22:04 +00:00
[ [ NSString alloc ] initWithData : data encoding : NSUTF8StringEncoding ] ) ;
if ( error )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] handleUniversalLink: Link validation error: %@" , error ) ;
2020-04-28 09:22:04 +00:00
[ self showErrorAsAlert : error ] ;
}
} ] ;
[ task resume ] ;
return YES ;
}
2021-07-14 11:42:27 +00:00
// Manage email validation link from identity server v1 or v2
2020-04-28 09:22:04 +00:00
else 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 ] ;
2020-04-28 16:52:03 +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
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[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
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] handleUniversalLink. Complete registration with nextLink" ) ;
2016-04-22 15:54:57 +00:00
NSURL * nextLink = [ NSURL URLWithString : queryParams [ @ "nextLink" ] ] ;
2021-05-04 08:41:02 +00:00
[ self handleUniversalLinkFragment : nextLink . fragment fromURL : nextLink ] ;
2016-04-22 15:54:57 +00:00
}
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
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[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
2021-05-04 08:41:02 +00:00
return [ self handleUniversalLinkFragment : webURL . fragment fromURL : webURL ] ;
2016-04-12 12:19:38 +00:00
}
- ( BOOL ) handleUniversalLinkFragment : ( NSString * ) fragment
2021-05-04 08:41:02 +00:00
{
return [ self handleUniversalLinkFragment : fragment fromURL : nil ] ;
}
2021-10-12 17:20:39 +00:00
2021-05-04 08:41:02 +00:00
- ( BOOL ) handleUniversalLinkFragment : ( NSString * ) fragment fromURL : ( NSURL * ) universalLinkURL
2021-10-12 17:20:39 +00:00
2016-04-12 12:19:38 +00:00
{
2021-10-21 08:00:37 +00:00
ScreenPresentationParameters * presentationParameters = [ [ ScreenPresentationParameters alloc ] initWithRestoreInitialDisplay : YES stackAboveVisibleViews : NO ] ;
2021-10-20 10:29:01 +00:00
UniversalLinkParameters * parameters = [ [ UniversalLinkParameters alloc ] initWithFragment : fragment universalLinkURL : universalLinkURL presentationParameters : presentationParameters ] ;
2021-10-12 17:20:39 +00:00
return [ self handleUniversalLinkWithParameters : parameters ] ;
}
2021-10-19 12:45:47 +00:00
- ( BOOL ) handleUniversalLinkWithParameters : ( UniversalLinkParameters * ) universalLinkParameters
2021-10-12 17:20:39 +00:00
{
2021-10-19 12:45:47 +00:00
NSString * fragment = universalLinkParameters . fragment ;
NSURL * universalLinkURL = universalLinkParameters . universalLinkURL ;
2021-10-21 13:32:43 +00:00
ScreenPresentationParameters * screenPresentationParameters = universalLinkParameters . presentationParameters ;
BOOL restoreInitialDisplay = screenPresentationParameters . restoreInitialDisplay ;
2021-10-12 17:20:39 +00:00
2016-04-12 12:19:38 +00:00
BOOL continueUserActivity = NO ;
2016-04-12 13:30:02 +00:00
MXKAccountManager * accountManager = [ MXKAccountManager sharedManager ] ;
2017-07-14 14:41:25 +00:00
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] Universal link: handleUniversalLinkFragment: %@" , fragment ) ;
2017-07-14 14:41:25 +00:00
2021-02-02 15:24:32 +00:00
// Make sure we have plain utf8 character for separators
fragment = [ fragment stringByRemovingPercentEncoding ] ;
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] Universal link: handleUniversalLinkFragment: %@" , fragment ) ;
2021-02-02 15:24:32 +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 )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] Universal link: Error: No path parameters" ) ;
2016-04-20 07:21:46 +00:00
return NO ;
}
2017-07-14 14:41:25 +00:00
2016-08-29 09:57:01 +00:00
NSString * roomIdOrAlias ;
2021-11-25 13:28:42 +00:00
NSString * threadId ;
2016-08-29 09:57:01 +00:00
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 ;
2021-05-04 08:41:02 +00:00
MXRoom * room ;
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
{
2021-05-04 08:41:02 +00:00
room = [ account . mxSession roomWithAlias : roomIdOrAlias ] ;
2016-04-12 13:30:02 +00:00
if ( room )
{
roomId = room . roomId ;
}
2016-04-12 08:23:48 +00:00
}
2021-05-04 08:41:02 +00:00
else
{
room = [ account . mxSession roomWithRoomId : roomId ] ;
}
2017-07-14 14:41:25 +00:00
2021-05-04 08:41:02 +00:00
if ( room . summary . roomType = = MXRoomTypeSpace )
{
2021-10-21 13:32:43 +00:00
SpaceNavigationParameters * spaceNavigationParameters = [ [ SpaceNavigationParameters alloc ] initWithRoomId : room . roomId mxSession : account . mxSession presentationParameters : screenPresentationParameters ] ;
2021-10-19 12:45:47 +00:00
2021-10-21 13:22:38 +00:00
[ self showSpaceWithParameters : spaceNavigationParameters ] ;
2021-05-04 08:41:02 +00:00
}
else
{
// Open the room page
2021-11-25 13:28:42 +00:00
if ( eventId )
{
MXEvent * event = [ account . mxSession . store eventWithEventId : eventId inRoom : roomId ] ;
2021-11-25 22:47:08 +00:00
threadId = event . threadId ;
2021-11-25 13:28:42 +00:00
}
RoomNavigationParameters * roomNavigationParameters = [ [ RoomNavigationParameters alloc ] initWithRoomId : roomId
threadId : threadId
eventId : eventId
mxSession : account . mxSession
presentationParameters : screenPresentationParameters ] ;
2021-10-12 17:20:39 +00:00
2021-10-21 09:40:18 +00:00
[ self showRoomWithParameters : roomNavigationParameters ] ;
2021-05-04 08:41:02 +00:00
}
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
{
2021-10-19 12:45:47 +00:00
void ( ^ findRoom ) ( void ) = ^ {
2021-09-23 15:15:32 +00:00
if ( [ _masterTabBarController . selectedViewController isKindOfClass : MXKActivityHandlingViewController . class ] )
2016-04-19 13:18:24 +00:00
{
2021-09-23 15:15:32 +00:00
MXKActivityHandlingViewController * homeViewController = ( MXKActivityHandlingViewController * ) _masterTabBarController . selectedViewController ;
2017-03-23 16:48:05 +00:00
[ 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
2021-02-02 16:07:12 +00:00
// The previous operation can fail because of percent encoding
// TBH we are not clean on data inputs . For the moment , just give another try with no encoding
// TODO : Have a dedicated module and tests to handle universal links ( matrix . to , email link , etc )
if ( [ newUniversalLinkFragment isEqualToString : fragment ] )
{
newUniversalLinkFragment =
[ fragment stringByReplacingOccurrencesOfString : roomIdOrAlias
withString : [ MXTools encodeURIComponent : roomId ] ] ;
}
2017-03-23 16:48:05 +00:00
2021-02-02 16:07:12 +00:00
if ( ! [ newUniversalLinkFragment isEqualToString : fragment ] )
{
universalLinkFragmentPendingRoomAlias = @ { roomId : roomIdOrAlias } ;
2021-10-21 13:32:43 +00:00
UniversalLinkParameters * newParameters = [ [ UniversalLinkParameters alloc ] initWithFragment : newUniversalLinkFragment universalLinkURL : universalLinkURL presentationParameters : screenPresentationParameters ] ;
2021-10-12 17:20:39 +00:00
[ self handleUniversalLinkWithParameters : newParameters ] ;
2021-02-02 16:07:12 +00:00
}
else
{
// Do not continue . Else we will loop forever
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] Universal link: Error: Cannot resolve alias in %@ to the room id %@" , fragment , roomId ) ;
2021-02-02 16:07:12 +00:00
}
2016-04-19 13:18:24 +00:00
}
2017-03-23 16:48:05 +00:00
} failure : ^ ( NSError * error ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] Universal link: Error: The homeserver failed to resolve the room alias (%@)" , roomIdOrAlias ) ;
2019-01-17 16:46:49 +00:00
[ homeViewController stopActivityIndicator ] ;
2021-09-28 05:40:01 +00:00
NSString * errorMessage = [ VectorL10n roomDoesNotExist : roomIdOrAlias ] ;
2019-01-17 16:46:49 +00:00
[ 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 ;
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] Universal link: Need to wait for the session to be sync'ed and running" ) ;
2017-03-23 16:48:05 +00:00
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 )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] Universal link: The session is running. Retry the link" ) ;
2021-10-19 12:45:47 +00:00
[ self handleUniversalLinkWithParameters : universalLinkParameters ] ;
2017-03-23 16:48:05 +00:00
}
}
} ] ;
2016-04-19 13:18:24 +00:00
}
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[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" ) ;
2017-03-23 16:48:05 +00:00
// FIXME : In case of multi - account , ask the user which one to use
MXKAccount * account = accountManager . activeAccounts . firstObject ;
2021-02-02 16:13:01 +00:00
RoomPreviewData * roomPreviewData = [ [ RoomPreviewData alloc ] initWithRoomId : roomIdOrAlias
andSession : account . mxSession ] ;
2017-03-23 16:48:05 +00:00
if ( queryParams )
{
2019-07-08 09:15:44 +00:00
roomPreviewData . viaServers = queryParams [ @ "via" ] ;
2017-03-23 16:48:05 +00:00
}
2021-02-02 16:13:01 +00:00
2021-10-21 13:32:43 +00:00
RoomPreviewNavigationParameters * roomPreviewNavigationParameters = [ [ RoomPreviewNavigationParameters alloc ] initWithPreviewData : roomPreviewData presentationParameters : screenPresentationParameters ] ;
2021-10-19 12:45:47 +00:00
2021-09-22 12:58:19 +00:00
[ account . mxSession . matrixRestClient roomSummaryWith : roomIdOrAlias via : roomPreviewData . viaServers success : ^ ( MXPublicRoom * room ) {
if ( [ room . roomTypeString isEqualToString : MXRoomTypeStringSpace ] )
2021-02-02 16:13:01 +00:00
{
2021-09-22 12:58:19 +00:00
[ homeViewController stopActivityIndicator ] ;
2021-10-21 13:32:43 +00:00
SpacePreviewNavigationParameters * spacePreviewNavigationParameters = [ [ SpacePreviewNavigationParameters alloc ] initWithPublicRoom : room mxSession : account . mxSession presentationParameters : screenPresentationParameters ] ;
2021-10-19 12:45:47 +00:00
2021-10-21 13:22:38 +00:00
[ self showSpacePreviewWithParameters : spacePreviewNavigationParameters ] ;
2021-02-02 16:13:01 +00:00
}
2021-09-22 12:58:19 +00:00
else
2021-02-02 16:13:01 +00:00
{
2021-10-21 09:57:13 +00:00
[ self peekInRoomWithNavigationParameters : roomPreviewNavigationParameters pathParams : pathParams ] ;
2021-02-02 16:13:01 +00:00
}
2021-09-22 12:58:19 +00:00
} failure : ^ ( NSError * error ) {
2021-10-21 09:57:13 +00:00
[ self peekInRoomWithNavigationParameters : roomPreviewNavigationParameters pathParams : pathParams ] ;
2021-02-02 16:13:01 +00:00
} ] ;
2016-04-12 15:44:06 +00:00
}
2017-07-14 14:41:25 +00:00
2016-04-12 13:30:02 +00:00
}
2021-10-19 12:45:47 +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
if ( restoreInitialDisplay )
{
[ self restoreInitialDisplay : ^ {
findRoom ( ) ;
} ] ;
}
else
{
findRoom ( ) ;
}
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
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[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 ] )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] Universal link: The user is now logged in. Retry the link" ) ;
2021-10-19 12:45:47 +00:00
[ self handleUniversalLinkWithParameters : universalLinkParameters ] ;
2016-04-12 15:44:06 +00:00
}
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 ] ;
2021-10-21 13:32:43 +00:00
[ self showContact : contact presentationParameters : screenPresentationParameters ] ;
2017-07-25 14:08:24 +00:00
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
2021-10-21 13:32:43 +00:00
[ self showGroup : group withMatrixSession : account . mxSession presentationParamters : screenPresentationParameters ] ;
2017-12-31 15:24:47 +00:00
continueUserActivity = YES ;
}
else
{
// There is no account . The app will display the AuthenticationVC .
// Wait for a successful login
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] Universal link: The user is not logged in. Wait for a successful login" ) ;
2017-12-31 15:24:47 +00:00
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 ] )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] Universal link: The user is now logged in. Retry the link" ) ;
2021-10-19 12:45:47 +00:00
[ self handleUniversalLinkWithParameters : universalLinkParameters ] ;
2017-12-31 15:24:47 +00:00
}
} ] ;
}
}
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
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[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
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] Universal link: TODO: Do not know what to do with the link arguments: %@" , pathParams ) ;
2017-07-14 14:41:25 +00:00
2021-10-19 12:45:47 +00:00
if ( restoreInitialDisplay )
{
[ 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 ;
}
2021-05-04 08:41:02 +00:00
- ( BOOL ) handleUniversalLinkURL : ( NSURL * ) universalLinkURL
{
// iOS Patch : fix vector . im urls before using it
NSURL * fixedURL = [ Tools fixURLWithSeveralHashKeys : universalLinkURL ] ;
return [ self handleUniversalLinkFragment : fixedURL . fragment fromURL : universalLinkURL ] ;
}
2016-04-12 15:44:06 +00:00
- ( void ) resetPendingUniversalLink
{
universalLinkFragmentPending = nil ;
if ( universalLinkWaitingObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : universalLinkWaitingObserver ] ;
universalLinkWaitingObserver = nil ;
}
}
2021-10-21 09:57:13 +00:00
- ( void ) peekInRoomWithNavigationParameters : ( RoomPreviewNavigationParameters * ) presentationParameters pathParams : ( NSArray < NSString * > * ) pathParams
2021-09-22 12:58:19 +00:00
{
2021-10-19 12:45:47 +00:00
RoomPreviewData * roomPreviewData = presentationParameters . previewData ;
NSString * roomIdOrAlias = presentationParameters . roomId ;
2021-09-22 12:58:19 +00:00
// 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 ;
MXWeakify ( self ) ;
// Try to get more information about the room before opening its preview
[ roomPreviewData peekInRoom : ^ ( BOOL succeeded ) {
MXStrongifyAndReturnIfNil ( self ) ;
MXKViewController * homeViewController = ( MXKViewController * ) self . masterTabBarController . selectedViewController ;
// 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 && self -> universalLinkFragmentPendingRoomAlias [ roomIdOrAlias ] )
{
roomPreviewData . roomName = self -> universalLinkFragmentPendingRoomAlias [ roomIdOrAlias ] ;
}
self -> universalLinkFragmentPendingRoomAlias = nil ;
2021-10-19 12:45:47 +00:00
[ self showRoomPreviewWithParameters : presentationParameters ] ;
2021-09-22 12:58:19 +00:00
} ] ;
}
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
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] handleServerProvionningLink: %@" , link ) ;
2019-09-10 09:51:49 +00:00
NSString * homeserver , * identityServer ;
[ self parseServerProvionningLink : link homeserver : & homeserver identityServer : & identityServer ] ;
if ( homeserver )
{
if ( [ MXKAccountManager sharedManager ] . activeAccounts . count )
{
[ self displayServerProvionningLinkBuyAlreadyLoggedInAlertWithCompletion : ^ ( BOOL logout ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] handleServerProvionningLink: logoutWithConfirmation: logout: %@" , @ ( logout ) ) ;
2019-09-10 09:51:49 +00:00
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
{
2020-08-06 09:48:20 +00:00
if ( [ link . path isEqualToString : @ "/" ] )
2019-09-10 09:51:49 +00:00
{
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
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] parseServerProvionningLink: Error: Unknown path: %@" , link . path ) ;
2019-09-10 09:51:49 +00:00
}
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] parseServerProvionningLink: homeserver: %@ - identityServer: %@" , * homeserver , * identityServer ) ;
2019-09-10 09:51:49 +00:00
}
- ( void ) displayServerProvionningLinkBuyAlreadyLoggedInAlertWithCompletion : ( void ( ^ ) ( BOOL logout ) ) completion
{
// Ask confirmation
2021-09-28 05:40:01 +00:00
self . logoutConfirmation = [ UIAlertController alertControllerWithTitle : [ VectorL10n errorUserAlreadyLoggedIn ] message : nil preferredStyle : UIAlertControllerStyleAlert ] ;
2019-09-10 09:51:49 +00:00
2021-09-28 05:40:01 +00:00
[ self . logoutConfirmation addAction : [ UIAlertAction actionWithTitle : [ VectorL10n settingsSignOut ]
2019-09-10 09:51:49 +00:00
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action )
{
self . logoutConfirmation = nil ;
completion ( YES ) ;
} ] ] ;
2021-09-28 05:40:01 +00:00
[ self . logoutConfirmation addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n cancel ]
2019-09-10 09:51:49 +00:00
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
2021-05-20 20:14:49 +00:00
// TODO : Move this method content in UserSessionsService
2015-06-18 09:19:42 +00:00
- ( void ) initMatrixSessions
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] initMatrixSessions" ) ;
2017-08-30 15:37:14 +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
2020-08-11 18:18:13 +00:00
[ self configureCallManagerIfRequiredForSession : mxSession ] ;
2017-07-14 14:41:25 +00:00
2020-11-17 20:27:57 +00:00
[ self . configuration setupSettingsFor : mxSession ] ;
2015-06-18 13:20:33 +00:00
}
else if ( mxSession . state = = MXSessionStateStoreDataReady )
2015-06-18 09:19:42 +00:00
{
2020-12-01 10:41:31 +00:00
// start the call service
2021-02-07 16:55:36 +00:00
[ self . callPresenter start ] ;
2017-09-07 17:28:38 +00:00
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
}
2020-01-30 17:13:49 +00:00
2020-07-29 08:03:37 +00:00
[ self . configuration setupSettingsWhenLoadedFor : mxSession ] ;
2020-03-27 14:41:19 +00:00
// Register to user new device sign in notification
[ self registerUserDidSignInOnNewDeviceNotificationForSession : mxSession ] ;
2020-04-03 14:33:56 +00:00
2020-10-13 15:15:47 +00:00
[ self registerDidChangeCrossSigningKeysNotificationForSession : mxSession ] ;
2020-04-03 14:33:56 +00:00
// Register to new key verification request
[ self registerNewRequestNotificationForSession : mxSession ] ;
2020-05-07 11:18:54 +00:00
[ self checkLocalPrivateKeysInSession : mxSession ] ;
2020-09-02 15:00:05 +00:00
[ self . pushNotificationService checkPushKitPushersInSession : mxSession ] ;
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
}
2020-09-29 13:15:32 +00:00
[ self handleAppState ] ;
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 .
2020-08-03 13:07:39 +00:00
account . pushGatewayURL = BuildSettings . serverConfigSygnalAPIUrlString ;
2019-04-02 07:35:02 +00:00
2020-04-30 12:51:29 +00:00
BOOL isPushRegistered = self . pushNotificationService . isPushRegistered ;
2020-04-14 12:54:36 +00:00
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][Push] didAddAccountNotification: isPushRegistered: %@" , @ ( isPushRegistered ) ) ;
2019-04-02 07:35:02 +00:00
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
2020-05-22 11:27:30 +00:00
[ account enablePushNotifications : YES success : nil failure : nil ] ;
2015-06-19 08:31:27 +00:00
}
else
{
// Set up push notifications
2020-04-30 12:51:29 +00:00
[ self . pushNotificationService registerUserNotificationSettings ] ;
2015-06-19 08:31:27 +00:00
}
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
2021-05-20 20:14:49 +00:00
[ self . delegate legacyAppDelegate : self didAddAccount : account ] ;
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
}
2021-05-20 20:14:49 +00:00
[ self . delegate legacyAppDelegate : self didRemoveAccount : account ] ;
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
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[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 ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] kMXSessionDidCorruptDataNotification received. Reload the app" ) ;
2016-08-12 14:50:27 +00:00
// 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 ] ;
2020-05-22 11:27:30 +00:00
2016-03-23 13:45:16 +00:00
// Observers have been defined , we can start a matrix session for each enabled accounts .
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] initMatrixSessions: prepareSessionForActiveAccounts (app state: %tu)" , [ [ UIApplication sharedApplication ] applicationState ] ) ;
2017-09-06 14:59:12 +00:00
[ 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 )
{
2020-08-03 13:07:39 +00:00
account . pushGatewayURL = BuildSettings . serverConfigSygnalAPIUrlString ;
2016-03-21 13:49:15 +00:00
}
}
2015-06-19 08:31:27 +00:00
// Set up push notifications
2020-04-30 12:51:29 +00:00
[ self . pushNotificationService registerUserNotificationSettings ] ;
2015-04-30 14:19:12 +00:00
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 ] ;
} ) ;
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
2021-01-21 11:28:56 +00:00
// register the session to the call service
2021-02-09 21:51:41 +00:00
[ _callPresenter addMatrixSession : mxSession ] ;
2021-01-21 11:28:56 +00:00
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 ] ;
2021-05-20 20:14:49 +00:00
[ self . delegate legacyAppDelegate : self didAddMatrixSession : 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 ] ;
2021-01-21 11:28:56 +00:00
// remove session from the call service
2021-02-09 21:51:41 +00:00
[ _callPresenter 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 ] ;
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
2020-03-18 16:39:26 +00:00
// Disable listening of incoming key verification requests
[ self disableIncomingKeyVerificationObserver : mxSession ] ;
2020-04-14 12:54:36 +00:00
2015-11-17 23:15:52 +00:00
[ mxSessionArray removeObject : mxSession ] ;
2017-09-07 17:28:38 +00:00
2020-12-01 10:41:31 +00:00
if ( ! mxSessionArray . count )
2017-09-07 17:28:38 +00:00
{
2020-12-01 10:41:31 +00:00
// if no session left , stop the call service
2021-02-07 16:55:36 +00:00
[ self . callPresenter stop ] ;
2017-09-07 17:28:38 +00:00
}
2021-05-20 20:14:49 +00:00
[ self . delegate legacyAppDelegate : self didRemoveMatrixSession : mxSession ] ;
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 )
{
2021-09-08 11:06:41 +00:00
self . clearingCache = YES ;
2021-08-30 14:24:16 +00:00
[ self 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 ;
2021-09-28 05:40:01 +00:00
NSString * message = [ VectorL10n settingsSignOutConfirmation ] ;
2018-04-17 19:39:31 +00:00
// 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
{
2021-09-28 05:40:01 +00:00
message = [ message stringByAppendingString : [ NSString stringWithFormat : @ "\n\n%@" , [ VectorL10n settingsSignOutE2eWarn ] ] ] ;
2018-04-17 19:39:31 +00:00
break ;
}
}
// Ask confirmation
2021-09-28 05:40:01 +00:00
self . logoutConfirmation = [ UIAlertController alertControllerWithTitle : [ VectorL10n settingsSignOut ] message : message preferredStyle : UIAlertControllerStyleAlert ] ;
2018-04-17 19:39:31 +00:00
2021-09-28 05:40:01 +00:00
[ self . logoutConfirmation addAction : [ UIAlertAction actionWithTitle : [ VectorL10n settingsSignOut ]
2018-04-17 19:39:31 +00:00
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 ] ;
} ) ;
}
} ] ] ;
2021-09-28 05:40:01 +00:00
[ self . logoutConfirmation addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n cancel ]
2018-04-17 19:39:31 +00:00
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
{
2020-04-30 12:51:29 +00:00
[ self . pushNotificationService deregisterRemoteNotifications ] ;
2020-04-14 12:54:36 +00:00
2014-10-17 23:04:05 +00:00
// Clear cache
2021-08-30 14:24:16 +00:00
[ self clearCache ] ;
2017-07-14 14:41:25 +00:00
2019-01-31 18:53:12 +00:00
// Reset key backup banner preferences
2020-06-26 11:07:53 +00:00
[ SecureBackupBannerPreferences . shared reset ] ;
2019-01-31 18:53:12 +00:00
2020-06-30 15:30:45 +00:00
// Reset key verification banner preferences
2020-06-30 19:46:10 +00:00
[ CrossSigningBannerPreferences . shared reset ] ;
2020-06-30 15:30:45 +00:00
2020-07-21 13:16:52 +00:00
// Reset user pin code
[ PinCodePreferences . shared reset ] ;
2020-08-20 14:43:46 +00:00
// Reset push notification store
[ self . pushNotificationStore reset ] ;
2020-08-11 18:18:13 +00:00
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
}
2020-09-29 13:15:32 +00:00
- ( void ) handleAppState
2017-01-31 23:18:01 +00:00
{
MXSession * mainSession = self . mxSessions . firstObject ;
if ( mainSession )
{
BOOL isLaunching = NO ;
2020-09-15 14:48:35 +00:00
if ( _masterTabBarController . authenticationInProgress )
2017-01-31 23:18:01 +00:00
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] handleAppState: Authentication still in progress" ) ;
2020-09-15 14:48:35 +00:00
// Wait for the return of masterTabBarControllerDidCompleteAuthentication
2020-09-24 09:37:24 +00:00
isLaunching = YES ;
2020-09-15 14:48:35 +00:00
}
else
{
2021-09-29 21:02:19 +00:00
MXLogDebug ( @ "[AppDelegate] handleAppState: mainSession.state: %@" , [ MXTools readableSessionState : mainSession . state ] ) ;
2020-09-15 14:48:35 +00:00
switch ( mainSession . state )
{
case MXSessionStateClosed :
case MXSessionStateInitialised :
2021-09-08 11:06:41 +00:00
case MXSessionStateBackgroundSyncInProgress :
self . roomListDataReady = NO ;
2020-09-15 14:48:35 +00:00
isLaunching = YES ;
break ;
case MXSessionStateStoreDataReady :
case MXSessionStateSyncInProgress :
// Stay in launching during the first server sync if the store is empty .
isLaunching = ( mainSession . rooms . count = = 0 && launchAnimationContainerView ) ;
2021-01-28 18:53:20 +00:00
if ( mainSession . crypto . crossSigning && mainSession . crypto . crossSigning . state = = MXCrossSigningStateCrossSigningExists )
{
[ mainSession . crypto setOutgoingKeyRequestsEnabled : NO onComplete : nil ] ;
}
2020-09-15 14:48:35 +00:00
break ;
2021-09-08 11:06:41 +00:00
case MXSessionStateRunning :
self . clearingCache = NO ;
isLaunching = NO ;
break ;
2020-09-15 14:48:35 +00:00
default :
isLaunching = NO ;
break ;
}
2017-01-31 23:18:01 +00:00
}
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] handleAppState: isLaunching: %@" , isLaunching ? @ "YES" : @ "NO" ) ;
2020-09-29 13:15:32 +00:00
2017-01-31 23:18:01 +00:00
if ( isLaunching )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] handleAppState: LaunchLoadingView" ) ;
2020-09-29 13:15:32 +00:00
[ self showLaunchAnimation ] ;
2017-02-21 15:08:10 +00:00
return ;
2017-01-31 23:18:01 +00:00
}
2020-09-29 15:02:02 +00:00
2021-09-08 11:06:41 +00:00
if ( self . isClearingCache )
2020-09-29 15:02:02 +00:00
{
2021-09-08 11:06:41 +00:00
// wait for another session state change to check room list data is ready
2020-09-29 15:02:02 +00:00
return ;
}
2021-09-08 08:21:04 +00:00
[ self ensureRoomListDataReadyWithCompletion : ^ {
2021-09-07 15:23:23 +00:00
[ self hideLaunchAnimation ] ;
2020-09-30 11:26:47 +00:00
2021-09-07 15:23:23 +00:00
if ( self . setPinCoordinatorBridgePresenter )
{
MXLogDebug ( @ "[AppDelegate] handleAppState: PIN code is presented. Do not go further" ) ;
return ;
}
2021-09-29 21:02:19 +00:00
MXLogDebug ( @ "[AppDelegate] handleAppState: Check cross-signing" ) ;
[ self checkCrossSigningForSession : mainSession ] ;
2020-09-30 11:26:47 +00:00
2021-09-07 15:23:23 +00:00
// TODO : We should wait that cross - signing screens are done before going further but it seems fine . Those screens
// protect each other .
2020-09-30 11:26:47 +00:00
2021-09-07 15:23:23 +00:00
// This is the time to check existing requests
MXLogDebug ( @ "[AppDelegate] handleAppState: Check pending verification requests" ) ;
[ self checkPendingRoomKeyRequests ] ;
[ self checkPendingIncomingKeyVerificationsInSession : mainSession ] ;
// TODO : When we will have an application state , we will do all of this in a dedicated initialisation state
// For the moment , reuse an existing boolean to avoid register things several times
if ( ! self -> incomingKeyVerificationObserver )
{
MXLogDebug ( @ "[AppDelegate] handleAppState: Set up observers for the crypto module" ) ;
// Enable listening of incoming key share requests
[ self enableRoomKeyRequestObserver : mainSession ] ;
// Enable listening of incoming key verification requests
[ self enableIncomingKeyVerificationObserver : mainSession ] ;
}
2021-09-08 08:21:04 +00:00
} ] ;
2017-02-21 15:08:10 +00:00
}
2020-09-29 13:15:32 +00:00
}
- ( void ) showLaunchAnimation
{
UIWindow * window = [ [ UIApplication sharedApplication ] keyWindow ] ;
2017-02-21 15:08:10 +00:00
2020-09-29 13:15:32 +00:00
if ( ! launchAnimationContainerView && window )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] showLaunchAnimation" ) ;
2020-09-29 13:15:32 +00:00
LaunchLoadingView * launchLoadingView = [ LaunchLoadingView instantiate ] ;
launchLoadingView . frame = window . bounds ;
[ launchLoadingView updateWithTheme : ThemeService . shared . theme ] ;
launchLoadingView . autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight ;
[ window addSubview : launchLoadingView ] ;
launchAnimationContainerView = launchLoadingView ;
2020-11-06 21:55:32 +00:00
[ MXSDKOptions . sharedInstance . profiler startMeasuringTaskWithName : kMXAnalyticsStartupLaunchScreen
category : kMXAnalyticsStartupCategory ] ;
2020-09-29 13:15:32 +00:00
}
}
- ( void ) hideLaunchAnimation
{
2017-02-21 15:08:10 +00:00
if ( launchAnimationContainerView )
{
2020-11-06 21:55:32 +00:00
id < MXProfiler > profiler = MXSDKOptions . sharedInstance . profiler ;
MXTaskProfile * launchTaskProfile = [ profiler taskProfileWithName : kMXAnalyticsStartupLaunchScreen category : kMXAnalyticsStartupCategory ] ;
if ( launchTaskProfile )
{
[ profiler stopMeasuringTaskWithProfile : launchTaskProfile ] ;
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] hideLaunchAnimation: LaunchAnimation was shown for %.3fms" , launchTaskProfile . duration * 1000 ) ;
2020-11-06 21:55:32 +00:00
}
2017-02-21 15:08:10 +00:00
2020-07-10 16:59:57 +00:00
[ self -> launchAnimationContainerView removeFromSuperview ] ;
self -> launchAnimationContainerView = nil ;
2017-01-31 23:18:01 +00:00
}
}
2020-08-11 18:18:13 +00:00
- ( void ) configureCallManagerIfRequiredForSession : ( MXSession * ) mxSession
{
if ( mxSession . callManager )
{
// already configured
return ;
}
// 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 ] ;
# endif
# ifdef CALL_STACK _JINGLE
callStack = [ [ MXJingleCallStack alloc ] init ] ;
# endif
if ( callStack )
{
[ mxSession enableVoIPWithCallStack : callStack ] ;
// 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 ] ;
}
else
{
[ self enableCallKit : NO forCallManager : mxSession . callManager ] ;
}
}
else
{
// When there is no call stack , display alerts on call invites
[ self enableNoVoIPOnMatrixSession : mxSession ] ;
}
}
2017-06-20 20:16:54 +00:00
- ( void ) enableCallKit : ( BOOL ) enable forCallManager : ( MXCallManager * ) callManager
{
2020-07-23 15:25:24 +00:00
# ifdef CALL_STACK _JINGLE
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 ] ;
2020-07-09 19:51:37 +00:00
callKitConfiguration . iconName = @ "callkit_icon" ;
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 ] ;
2020-07-23 15:25:24 +00:00
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 ;
audioSessionConfigurator = [ [ MXJingleCallAudioSessionConfigurator alloc ] init ] ;
callKitAdapter . audioSessionConfigurator = audioSessionConfigurator ;
callManager . callKitAdapter = callKitAdapter ;
}
else
{
callManager . callKitAdapter = nil ;
}
2020-07-23 15:25:24 +00:00
# endif
2017-06-20 20:16:54 +00:00
}
2020-05-07 11:18:54 +00:00
- ( void ) checkLocalPrivateKeysInSession : ( MXSession * ) mxSession
{
id < MXCryptoStore > cryptoStore = mxSession . crypto . store ;
NSUInteger keysCount = 0 ;
if ( [ cryptoStore secretWithSecretId : MXSecretId . keyBackup ] )
{
keysCount + + ;
}
if ( [ cryptoStore secretWithSecretId : MXSecretId . crossSigningUserSigning ] )
{
keysCount + + ;
}
if ( [ cryptoStore secretWithSecretId : MXSecretId . crossSigningSelfSigning ] )
{
keysCount + + ;
}
2020-05-07 11:30:40 +00:00
if ( ( keysCount > 0 && keysCount < 3 )
|| ( mxSession . crypto . crossSigning . canTrustCrossSigning && ! mxSession . crypto . crossSigning . canCrossSign ) )
2020-05-07 11:18:54 +00:00
{
// We should have 3 of them . If not , request them again as mitigation
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] checkLocalPrivateKeysInSession: request keys because keysCount = %@" , @ ( keysCount ) ) ;
2020-05-07 11:18:54 +00:00
[ mxSession . crypto requestAllPrivateKeys ] ;
}
}
2020-09-24 09:37:24 +00:00
- ( void ) authenticationDidComplete
{
2020-09-29 13:15:32 +00:00
[ self handleAppState ] ;
2020-09-24 09:37:24 +00:00
}
2021-09-08 08:21:04 +00:00
/ * *
Ensures room list data is ready .
@ param completion Completion block to be called when it ' s ready . Not dispatched in case the data is already ready .
* /
- ( void ) ensureRoomListDataReadyWithCompletion : ( void ( ^ ) ( void ) ) completion
{
if ( self . isRoomListDataReady )
{
completion ( ) ;
}
else
{
NSNotificationCenter * __weak notificationCenter = [ NSNotificationCenter defaultCenter ] ;
__block id observer = [ [ NSNotificationCenter defaultCenter ] addObserverForName : RecentsViewControllerDataReadyNotification
object : nil
queue : [ NSOperationQueue mainQueue ]
usingBlock : ^ ( NSNotification * _Nonnull notification ) {
[ notificationCenter removeObserver : observer ] ;
self . roomListDataReady = YES ;
completion ( ) ;
} ] ;
}
}
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 )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "WARNING: The user has no device. Prompt for login again" ) ;
2017-03-23 16:48:05 +00:00
2021-09-22 13:59:47 +00:00
NSString * msg = [ VectorL10n e2eEnablingOnAppUpdate : AppInfo . current . displayName ] ;
2017-03-23 16:48:05 +00:00
__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
2021-09-28 05:40:01 +00:00
[ _errorNotification addAction : [ UIAlertAction actionWithTitle : [ VectorL10n later ]
2017-07-14 14:41:25 +00:00
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> _errorNotification = nil ;
}
} ] ] ;
2017-03-23 16:48:05 +00:00
2021-09-28 05:40:01 +00:00
[ _errorNotification addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n ok ]
2017-07-14 14:41:25 +00:00
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 ] ;
}
}
}
2020-04-03 14:33:56 +00:00
- ( void ) presentViewController : ( UIViewController * ) viewController
animated : ( BOOL ) animated
completion : ( void ( ^ ) ( void ) ) completion
{
[ self . presentedViewController presentViewController : viewController animated : animated completion : completion ] ;
}
- ( UIViewController * ) presentedViewController
{
return self . window . rootViewController . presentedViewController ? : self . window . rootViewController ;
}
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
2021-09-28 05:40:01 +00:00
[ self . mxInAppNotification addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n 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 ] ;
}
} ] ] ;
2021-09-28 05:40:01 +00:00
[ self . mxInAppNotification addAction : [ UIAlertAction actionWithTitle : [ VectorL10n view ]
2017-07-14 14:41:25 +00:00
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
2021-09-28 05:40:01 +00:00
accountPicker = [ UIAlertController alertControllerWithTitle : [ MatrixKitL10n selectAccount ] 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
}
2021-09-28 05:40:01 +00:00
[ accountPicker addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n 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
2020-06-15 10:49:22 +00:00
- ( void ) navigateToRoomById : ( NSString * ) roomId
2020-04-14 12:54:36 +00:00
{
if ( roomId . length )
{
// TODO retrieve the right matrix session
// We can use the "user_id" value in notification . userInfo
// * * * * * * * * * * * * * *
// Patch consider the first session which knows the room id
MXKAccount * dedicatedAccount = nil ;
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
if ( mxAccounts . count = = 1 )
{
dedicatedAccount = mxAccounts . firstObject ;
}
else
{
for ( MXKAccount * account in mxAccounts )
{
if ( [ account . mxSession roomWithRoomId : roomId ] )
{
dedicatedAccount = account ;
break ;
}
}
}
// sanity checks
if ( dedicatedAccount && dedicatedAccount . mxSession )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][Push] navigateToRoomById: open the roomViewController %@" , roomId ) ;
2020-04-14 12:54:36 +00:00
2020-06-15 10:49:22 +00:00
[ self showRoom : roomId andEventId : nil withMatrixSession : dedicatedAccount . mxSession ] ;
2020-04-14 12:54:36 +00:00
}
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][Push] navigateToRoomById : no linked session / account has been found." ) ;
2020-04-14 12:54:36 +00:00
}
}
}
2021-10-21 09:40:18 +00:00
- ( void ) showRoomWithParameters : ( RoomNavigationParameters * ) parameters
2015-08-26 17:04:30 +00:00
{
2021-10-12 17:20:39 +00:00
[ self showRoomWithParameters : parameters completion : nil ] ;
}
2021-10-21 09:40:18 +00:00
- ( void ) showRoomWithParameters : ( RoomNavigationParameters * ) parameters completion : ( void ( ^ ) ( void ) ) completion
2021-10-12 17:20:39 +00:00
{
NSString * roomId = parameters . roomId ;
MXSession * mxSession = parameters . mxSession ;
2021-10-20 16:27:42 +00:00
BOOL restoreInitialDisplay = parameters . presentationParameters . restoreInitialDisplay ;
2021-10-12 17:20:39 +00:00
2021-04-29 12:37:07 +00:00
if ( roomId && mxSession )
{
MXRoom * room = [ mxSession roomWithRoomId : roomId ] ;
2021-05-04 08:41:02 +00:00
// Indicates that spaces are not supported
2021-04-29 12:37:07 +00:00
if ( room . summary . roomType = = MXRoomTypeSpace )
{
2021-05-06 11:31:49 +00:00
[ self . spaceFeatureUnavailablePresenter presentUnavailableFeatureFrom : self . presentedViewController animated : YES ] ;
2021-04-29 12:37:07 +00:00
if ( completion )
{
completion ( ) ;
}
return ;
}
}
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 )
2021-10-20 16:32:38 +00:00
2021-10-12 17:20:39 +00:00
[ self . masterTabBarController selectRoomWithParameters : parameters completion : ^ {
2019-04-05 12:51:56 +00:00
// Remove delivered notifications for this room
2020-04-30 12:51:29 +00:00
[ self . pushNotificationService 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 ( ) ;
}
}
2021-10-12 17:20:39 +00:00
- ( void ) showRoom : ( NSString * ) roomId andEventId : ( NSString * ) eventId withMatrixSession : ( MXSession * ) mxSession
{
2021-10-20 06:31:51 +00:00
// Ask to restore initial display
2021-10-21 08:00:37 +00:00
ScreenPresentationParameters * presentationParameters = [ [ ScreenPresentationParameters alloc ] initWithRestoreInitialDisplay : YES ] ;
2021-10-20 06:31:51 +00:00
2021-10-21 09:40:18 +00:00
RoomNavigationParameters * parameters = [ [ RoomNavigationParameters alloc ] initWithRoomId : roomId
2021-11-25 13:28:42 +00:00
threadId : nil
eventId : eventId
mxSession : mxSession
presentationParameters : presentationParameters ] ;
2021-10-12 17:20:39 +00:00
[ self showRoomWithParameters : parameters ] ;
}
2021-10-21 09:57:13 +00:00
- ( void ) showRoomPreviewWithParameters : ( RoomPreviewNavigationParameters * ) parameters completion : ( void ( ^ ) ( void ) ) completion
2019-04-29 12:22:01 +00:00
{
2021-10-12 17:20:39 +00:00
void ( ^ showRoomPreview ) ( void ) = ^ ( ) {
[ self . masterTabBarController selectRoomPreviewWithParameters : parameters completion : completion ] ;
} ;
2021-10-20 06:31:51 +00:00
if ( parameters . presentationParameters . restoreInitialDisplay )
2021-10-12 17:20:39 +00:00
{
[ self restoreInitialDisplay : ^ {
showRoomPreview ( ) ;
} ] ;
}
else
{
showRoomPreview ( ) ;
}
2015-08-26 17:04:30 +00:00
}
2021-10-21 09:57:13 +00:00
- ( void ) showRoomPreviewWithParameters : ( RoomPreviewNavigationParameters * ) parameters
2019-04-08 11:32:46 +00:00
{
2021-10-12 17:20:39 +00:00
[ self showRoomPreviewWithParameters : parameters completion : nil ] ;
2019-04-08 11:32:46 +00:00
}
2016-04-15 07:43:56 +00:00
- ( void ) showRoomPreview : ( RoomPreviewData * ) roomPreviewData
{
2021-10-20 06:31:51 +00:00
// Ask to restore initial display
2021-10-21 08:00:37 +00:00
ScreenPresentationParameters * presentationParameters = [ [ ScreenPresentationParameters alloc ] initWithRestoreInitialDisplay : YES ] ;
2021-10-20 06:31:51 +00:00
2021-10-21 09:57:13 +00:00
RoomPreviewNavigationParameters * parameters = [ [ RoomPreviewNavigationParameters alloc ] initWithPreviewData : roomPreviewData presentationParameters : presentationParameters ] ;
2021-10-12 17:20:39 +00:00
[ self showRoomPreviewWithParameters : parameters ] ;
2016-04-15 07:43:56 +00:00
}
2021-10-21 13:22:38 +00:00
- ( void ) showSpacePreviewWithParameters : ( SpacePreviewNavigationParameters * ) parameters
2021-10-19 12:45:47 +00:00
{
2021-10-20 05:37:13 +00:00
UIViewController * presentingViewController ;
UIView * sourceView ;
if ( parameters . presentationParameters . presentingViewController )
{
presentingViewController = parameters . presentationParameters . presentingViewController ;
sourceView = parameters . presentationParameters . sourceView ;
}
else
{
presentingViewController = self . masterNavigationController ;
}
2021-10-19 12:45:47 +00:00
self . spaceDetailPresenter = [ SpaceDetailPresenter new ] ;
self . spaceDetailPresenter . delegate = self ;
void ( ^ showSpace ) ( void ) = ^ {
[ self . spaceDetailPresenter presentForSpaceWithPublicRoom : parameters . publicRoom
2021-10-20 16:32:57 +00:00
from : presentingViewController
sourceView : sourceView
session : parameters . mxSession
animated : YES ] ;
2021-10-19 12:45:47 +00:00
} ;
2021-10-20 05:37:13 +00:00
if ( parameters . presentationParameters . restoreInitialDisplay )
2021-10-19 12:45:47 +00:00
{
[ self restoreInitialDisplay : ^ {
showSpace ( ) ;
} ] ;
}
else
{
showSpace ( ) ;
}
}
2021-10-21 13:22:38 +00:00
- ( void ) showSpaceWithParameters : ( SpaceNavigationParameters * ) parameters
2021-10-19 12:45:47 +00:00
{
2021-10-20 05:37:13 +00:00
UIViewController * presentingViewController ;
UIView * sourceView ;
if ( parameters . presentationParameters . presentingViewController )
{
presentingViewController = parameters . presentationParameters . presentingViewController ;
sourceView = parameters . presentationParameters . sourceView ;
}
else
{
presentingViewController = self . masterNavigationController ;
}
2021-10-19 12:45:47 +00:00
self . spaceDetailPresenter = [ SpaceDetailPresenter new ] ;
self . spaceDetailPresenter . delegate = self ;
void ( ^ showSpace ) ( void ) = ^ {
[ self . spaceDetailPresenter presentForSpaceWithId : parameters . roomId
from : presentingViewController
sourceView : sourceView
session : parameters . mxSession
animated : YES ] ;
} ;
2021-10-20 05:37:13 +00:00
if ( parameters . presentationParameters . restoreInitialDisplay )
2021-10-19 12:45:47 +00:00
{
[ self restoreInitialDisplay : ^ {
showSpace ( ) ;
} ] ;
}
else
{
showSpace ( ) ;
}
}
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 ) ;
2020-01-28 13:33:56 +00:00
2020-01-28 14:10:00 +00:00
void ( ^ onFailure ) ( NSError * ) = ^ ( NSError * error ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] Create direct chat failed" ) ;
2020-01-28 14:10:00 +00:00
// Alert user
2021-09-28 05:40:01 +00:00
[ self showAlertWithTitle : nil message : [ VectorL10n roomCreationDmError ] ] ;
2020-01-28 13:33:56 +00:00
if ( completion )
{
completion ( ) ;
}
2020-01-28 14:10:00 +00:00
} ;
2020-01-28 13:33:56 +00:00
2020-06-25 08:23:24 +00:00
[ mxSession vc_canEnableE2EByDefaultInNewRoomWithUsers : invite success : ^ ( BOOL canEnableE2E ) {
2020-01-28 14:10:00 +00:00
MXRoomCreationParameters * roomCreationParameters = [ MXRoomCreationParameters new ] ;
roomCreationParameters . visibility = kMXRoomDirectoryVisibilityPrivate ;
roomCreationParameters . inviteArray = invite ;
roomCreationParameters . isDirect = ( invite . count ! = 0 ) ;
roomCreationParameters . preset = kMXRoomPresetTrustedPrivateChat ;
2020-01-28 13:33:56 +00:00
2020-01-28 14:10:00 +00:00
if ( canEnableE2E )
2020-01-28 13:33:56 +00:00
{
2020-01-28 14:10:00 +00:00
roomCreationParameters . initialStateEvents = @ [
[ MXRoomCreationParameters initialStateEventForEncryptionWithAlgorithm : kMXCryptoMegolmAlgorithm
] ] ;
2020-01-28 13:33:56 +00:00
}
2020-01-28 14:10:00 +00:00
[ mxSession createRoomWithParameters : roomCreationParameters success : ^ ( MXRoom * room ) {
// Open created room
[ self showRoom : room . roomId andEventId : nil withMatrixSession : mxSession ] ;
if ( completion )
{
completion ( ) ;
}
} failure : onFailure ] ;
} failure : onFailure ] ;
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
2021-10-21 08:00:37 +00:00
- ( void ) showContact : ( MXKContact * ) contact presentationParameters : ( ScreenPresentationParameters * ) presentationParameters
2017-07-25 14:08:24 +00:00
{
2021-10-20 09:57:56 +00:00
void ( ^ showContact ) ( void ) = ^ {
2021-10-20 13:55:46 +00:00
[ self . masterTabBarController selectContact : contact withPresentationParameters : presentationParameters ] ;
2021-10-20 09:57:56 +00:00
} ;
if ( presentationParameters . restoreInitialDisplay )
{
[ self restoreInitialDisplay : ^ {
showContact ( ) ;
} ] ;
}
else
{
showContact ( ) ;
}
2017-07-25 14:08:24 +00:00
}
2017-12-31 15:24:47 +00:00
# pragma mark - Matrix Groups handling
2021-10-21 08:00:37 +00:00
- ( void ) showGroup : ( MXGroup * ) group withMatrixSession : ( MXSession * ) mxSession presentationParamters : ( ScreenPresentationParameters * ) presentationParameters
2017-12-31 15:24:47 +00:00
{
2021-10-20 10:29:01 +00:00
void ( ^ showGroup ) ( void ) = ^ {
2017-12-31 15:24:47 +00:00
// Select group to display its details ( dispatch this action in order to let TabBarController end its refresh )
2021-10-20 13:55:46 +00:00
[ self . masterTabBarController selectGroup : group inMatrixSession : mxSession presentationParameters : presentationParameters ] ;
2021-10-20 10:29:01 +00:00
} ;
if ( presentationParameters . restoreInitialDisplay )
{
[ self restoreInitialDisplay : ^ {
showGroup ( ) ;
} ] ;
}
else
{
showGroup ( ) ;
}
2017-12-31 15:24:47 +00:00
}
2019-08-28 15:04:49 +00:00
- ( void ) promptForStunServerFallback
{
[ _errorNotification dismissViewControllerAnimated : NO completion : nil ] ;
2020-07-31 06:49:30 +00:00
NSString * stunFallbackHost = BuildSettings . stunServerFallbackUrlString ;
2019-08-28 15:04:49 +00:00
// Remove "stun:"
stunFallbackHost = [ stunFallbackHost componentsSeparatedByString : @ ":" ] . lastObject ;
MXSession * mainSession = self . mxSessions . firstObject ;
NSString * homeServerName = mainSession . matrixRestClient . credentials . homeServerName ;
NSString * message = [ NSString stringWithFormat : @ "%@\n\n%@" ,
2021-09-28 05:40:01 +00:00
[ VectorL10n callNoStunServerErrorMessage1 : homeServerName ] ,
[ VectorL10n callNoStunServerErrorMessage2 : stunFallbackHost ] ] ;
_errorNotification = [ UIAlertController alertControllerWithTitle : [ VectorL10n callNoStunServerErrorTitle ]
2019-08-28 15:04:49 +00:00
message : message
preferredStyle : UIAlertControllerStyleAlert ] ;
2021-09-28 05:40:01 +00:00
[ _errorNotification addAction : [ UIAlertAction actionWithTitle : [ VectorL10n callNoStunServerErrorUseFallbackButton : stunFallbackHost ]
2019-08-28 15:04:49 +00:00
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-09-28 05:40:01 +00:00
RiotSettings . shared . allowStunServerFallback = YES ;
2020-07-31 06:49:30 +00:00
mainSession . callManager . fallbackSTUNServer = BuildSettings . stunServerFallbackUrlString ;
2019-08-28 15:04:49 +00:00
[ AppDelegate theDelegate ] . errorNotification = nil ;
} ] ] ;
2021-09-28 05:40:01 +00:00
[ _errorNotification addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n cancel ]
2019-08-28 15:04:49 +00:00
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 ] ;
}
}
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 )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[WidgetVC] setPermissionForWidget failed. Error: %@" , error ) ;
2019-11-22 10:18:08 +00:00
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 ] ;
2021-09-28 05:40:01 +00:00
NSString * widgetCreatorUserId = widget . widgetEvent . sender ? : [ VectorL10n roomParticipantsUnknown ] ;
2019-11-28 16:38:19 +00:00
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 = @ [
2021-09-28 05:40:01 +00:00
[ VectorL10n roomWidgetPermissionDisplayNamePermission ] ,
[ VectorL10n roomWidgetPermissionAvatarUrlPermission ]
2019-11-28 16:38:19 +00:00
] ;
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 ) ;
} ] ;
} ;
[ self . slidingModalPresenter present : widgetPermissionViewController
2020-04-03 14:33:56 +00:00
from : self . presentedViewController
2019-11-28 16:38:19 +00:00
animated : YES
completion : nil ] ;
2019-11-22 10:18:08 +00:00
}
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 ,
2020-11-12 14:07:07 +00:00
kMXEventTypeStringCallSelectAnswer ,
kMXEventTypeStringCallHangup ,
2020-11-16 12:58:19 +00:00
kMXEventTypeStringCallReject ,
kMXEventTypeStringCallNegotiate
2016-05-20 12:31:23 +00:00
]
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
2021-09-28 05:40:01 +00:00
NSString * message = [ VectorL10n noVoip : callerDisplayname : appDisplayName ] ;
2017-07-14 14:41:25 +00:00
2021-09-28 05:40:01 +00:00
noCallSupportAlert = [ UIAlertController alertControllerWithTitle : [ VectorL10n noVoipTitle ]
2017-07-14 14:41:25 +00:00
message : message
preferredStyle : UIAlertControllerStyleAlert ] ;
__weak typeof ( self ) weakSelf = self ;
2021-09-28 05:40:01 +00:00
[ noCallSupportAlert addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n ignore ]
2017-07-14 14:41:25 +00:00
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> noCallSupportAlert = nil ;
}
} ] ] ;
2021-09-28 05:40:01 +00:00
[ noCallSupportAlert addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n rejectCall ]
2017-07-14 14:41:25 +00:00
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2020-11-12 14:07:07 +00:00
// Reject the call by sending the hangup event
NSDictionary * content = @ {
@ "call_id" : callInviteEventContent . callId ,
@ "version" : kMXCallVersion ,
@ "party_id" : mxSession . myDeviceId
} ;
2021-11-03 23:07:09 +00:00
[ mxSession . matrixRestClient sendEventToRoom : event . roomId threadId : nil eventType : kMXEventTypeStringCallReject content : content txnId : nil success : nil failure : ^ ( NSError * error ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] enableNoVoIPOnMatrixSession: ERROR: Cannot send m.call.reject event." ) ;
2020-11-12 14:07:07 +00:00
} ] ;
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> noCallSupportAlert = nil ;
}
} ] ] ;
2017-07-14 14:41:25 +00:00
[ self showNotificationAlert : noCallSupportAlert ] ;
break ;
}
case MXEventTypeCallAnswer :
case MXEventTypeCallHangup :
2020-11-12 14:07:07 +00:00
case MXEventTypeCallReject :
2017-07-14 14:41:25 +00:00
// 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
}
2021-09-29 21:02:19 +00:00
# pragma mark - Cross - signing
- ( void ) checkCrossSigningForSession : ( MXSession * ) mxSession
{
if ( [ UIApplication sharedApplication ] . applicationState ! = UIApplicationStateActive )
{
MXLogDebug ( @ "[AppDelegate] checkCrossSigningForSession called while the app is not active. Ignore it." ) ;
return ;
}
if ( mxSession . crypto . crossSigning )
{
// Get the up - to - date cross - signing state
MXWeakify ( self ) ;
[ mxSession . crypto . crossSigning refreshStateWithSuccess : ^ ( BOOL stateUpdated ) {
MXStrongifyAndReturnIfNil ( self ) ;
MXLogDebug ( @ "[AppDelegate] handleAppState: crossSigning.state: %@" , @ ( mxSession . crypto . crossSigning . state ) ) ;
switch ( mxSession . crypto . crossSigning . state )
{
case MXCrossSigningStateCrossSigningExists :
MXLogDebug ( @ "[AppDelegate] handleAppState: presentVerifyCurrentSessionAlertIfNeededWithSession" ) ;
[ self . masterTabBarController presentVerifyCurrentSessionAlertIfNeededWithSession : mxSession ] ;
break ;
case MXCrossSigningStateCanCrossSign :
MXLogDebug ( @ "[AppDelegate] handleAppState: presentReviewUnverifiedSessionsAlertIfNeededWithSession" ) ;
[ self . masterTabBarController presentReviewUnverifiedSessionsAlertIfNeededWithSession : mxSession ] ;
break ;
default :
break ;
}
} failure : ^ ( NSError * _Nonnull error ) {
MXLogDebug ( @ "[AppDelegate] handleAppState: crossSigning.state: %@. Error: %@" , @ ( mxSession . crypto . crossSigning . state ) , error ) ;
} ] ;
}
}
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 )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession called while the app is not active. Ignore it." ) ;
2017-11-14 17:21:01 +00:00
return ;
}
2021-01-27 08:38:11 +00:00
MXWeakify ( self ) ;
2017-11-14 17:21:01 +00:00
[ mxSession . crypto pendingKeyRequests : ^ ( MXUsersDevicesMap < NSArray < MXIncomingRoomKeyRequest * > * > * pendingKeyRequests ) {
2021-01-26 16:33:05 +00:00
2021-01-27 08:38:11 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession: cross-signing state: %ld, pendingKeyRequests.count: %@. Already displayed: %@" ,
mxSession . crypto . crossSigning . state ,
@ ( pendingKeyRequests . count ) ,
self -> roomKeyRequestViewController ? @ "YES" : @ "NO" ) ;
2017-11-14 17:21:01 +00:00
2021-01-28 13:47:12 +00:00
if ( ! mxSession . crypto . crossSigning || mxSession . crypto . crossSigning . state = = MXCrossSigningStateNotBootstrapped )
2017-11-14 17:21:01 +00:00
{
2021-01-27 08:38:11 +00:00
if ( self -> roomKeyRequestViewController )
{
// Check if the current RoomKeyRequestViewController is still valid
MXSession * currentMXSession = self -> roomKeyRequestViewController . mxSession ;
NSString * currentUser = self -> roomKeyRequestViewController . device . userId ;
NSString * currentDevice = self -> roomKeyRequestViewController . device . deviceId ;
2017-11-14 17:21:01 +00:00
2021-01-27 08:38:11 +00:00
NSArray < MXIncomingRoomKeyRequest * > * currentPendingRequest = [ pendingKeyRequests objectForDevice : currentDevice forUser : currentUser ] ;
2017-11-14 17:21:01 +00:00
2021-01-27 08:38:11 +00:00
if ( currentMXSession = = mxSession && currentPendingRequest . count = = 0 )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession: Cancel current dialog" ) ;
2017-11-14 17:21:01 +00:00
2021-01-27 08:38:11 +00:00
// The key request has been probably cancelled , remove the popup
[ self -> roomKeyRequestViewController hide ] ;
self -> roomKeyRequestViewController = nil ;
}
2017-11-14 17:21:01 +00:00
}
}
2021-01-27 08:38:11 +00:00
if ( ! self -> roomKeyRequestViewController && pendingKeyRequests . count )
2017-11-14 17:21:01 +00:00
{
// 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 ;
2021-01-26 16:33:05 +00:00
2017-12-05 14:47:39 +00:00
// Give the client a chance to refresh the device list
2021-01-27 08:38:11 +00:00
MXWeakify ( self ) ;
2020-01-15 07:38:35 +00:00
[ mxSession . crypto downloadKeys : @ [ userId ] forceDownload : NO success : ^ ( MXUsersDevicesMap < MXDeviceInfo * > * usersDevicesInfoMap , NSDictionary < NSString * , MXCrossSigningInfo * > * crossSigningKeysMap ) {
2021-01-27 08:38:11 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2017-12-05 14:47:39 +00:00
MXDeviceInfo * deviceInfo = [ usersDevicesInfoMap objectForDevice : deviceId forUser : userId ] ;
if ( deviceInfo )
{
2021-01-28 13:48:23 +00:00
if ( ! mxSession . crypto . crossSigning || mxSession . crypto . crossSigning . state = = MXCrossSigningStateNotBootstrapped )
2017-12-05 14:47:39 +00:00
{
2021-01-27 08:38:11 +00:00
BOOL wasNewDevice = ( deviceInfo . trustLevel . localVerificationStatus = = MXDeviceUnknown ) ;
void ( ^ openDialog ) ( void ) = ^ void ( )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession: Open dialog for %@" , deviceInfo ) ;
2017-11-14 17:21:01 +00:00
2021-01-27 08:38:11 +00:00
self -> roomKeyRequestViewController = [ [ RoomKeyRequestViewController alloc ] initWithDeviceInfo : deviceInfo wasNewDevice : wasNewDevice andMatrixSession : mxSession onComplete : ^ {
2017-11-14 17:21:01 +00:00
2021-01-27 08:38:11 +00:00
self -> roomKeyRequestViewController = nil ;
2017-12-05 14:47:39 +00:00
2021-01-27 08:38:11 +00:00
// Check next pending key request , if any
[ self checkPendingRoomKeyRequests ] ;
} ] ;
2017-12-05 14:47:39 +00:00
2021-01-27 08:38:11 +00:00
[ self -> roomKeyRequestViewController show ] ;
} ;
2017-11-14 17:21:01 +00:00
2021-01-27 08:38:11 +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 if ( deviceInfo . trustLevel . isVerified )
2017-12-05 14:47:39 +00:00
{
2021-01-27 08:38:11 +00:00
[ mxSession . crypto acceptAllPendingKeyRequestsFromUser : userId andDevice : deviceId onComplete : ^ {
[ self checkPendingRoomKeyRequests ] ;
} ] ;
2017-12-05 14:47:39 +00:00
}
else
{
2021-01-27 08:38:11 +00:00
[ mxSession . crypto ignoreAllPendingKeyRequestsFromUser : userId andDevice : deviceId onComplete : ^ {
[ self checkPendingRoomKeyRequests ] ;
} ] ;
2017-12-05 14:47:39 +00:00
}
}
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession: No details found for device %@:%@" , userId , deviceId ) ;
2017-12-05 14:47:39 +00:00
[ mxSession . crypto ignoreAllPendingKeyRequestsFromUser : userId andDevice : deviceId onComplete : ^ {
[ self checkPendingRoomKeyRequests ] ;
} ] ;
}
} failure : ^ ( NSError * error ) {
// Retry later
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession: Failed to download device keys. Retry" ) ;
2017-12-05 14:47:39 +00:00
[ 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 ] ;
}
}
2020-03-18 16:39:26 +00:00
# pragma mark - Incoming key verification handling
2019-04-12 21:19:07 +00:00
2020-03-18 16:39:26 +00:00
- ( void ) enableIncomingKeyVerificationObserver : ( MXSession * ) mxSession
2019-04-12 21:19:07 +00:00
{
2020-03-18 16:39:26 +00:00
incomingKeyVerificationObserver =
2020-02-11 12:56:36 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserverForName : MXKeyVerificationManagerNewTransactionNotification
object : mxSession . crypto . keyVerificationManager
2019-04-12 21:19:07 +00:00
queue : [ NSOperationQueue mainQueue ]
usingBlock : ^ ( NSNotification * notif )
{
2020-02-11 12:56:36 +00:00
NSObject * object = notif . userInfo [ MXKeyVerificationManagerNotificationTransactionKey ] ;
2019-04-12 21:19:07 +00:00
if ( [ object isKindOfClass : MXIncomingSASTransaction . class ] )
{
2020-03-18 16:39:26 +00:00
[ self checkPendingIncomingKeyVerificationsInSession : mxSession ] ;
2019-04-12 21:19:07 +00:00
}
} ] ;
}
2020-03-18 16:39:26 +00:00
- ( void ) disableIncomingKeyVerificationObserver : ( MXSession * ) mxSession
2019-04-12 21:19:07 +00:00
{
2020-03-18 16:39:26 +00:00
if ( incomingKeyVerificationObserver )
2019-04-12 21:19:07 +00:00
{
2020-03-18 16:39:26 +00:00
[ [ NSNotificationCenter defaultCenter ] removeObserver : incomingKeyVerificationObserver ] ;
incomingKeyVerificationObserver = nil ;
2019-04-12 21:19:07 +00:00
}
}
2020-03-18 16:39:26 +00:00
// Check if an incoming key verification dialog must be displayed for the given session
- ( void ) checkPendingIncomingKeyVerificationsInSession : ( MXSession * ) mxSession
2019-04-12 21:19:07 +00:00
{
if ( [ UIApplication sharedApplication ] . applicationState ! = UIApplicationStateActive )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][MXKeyVerification] checkPendingIncomingKeyVerificationsInSession: called while the app is not active. Ignore it." ) ;
2019-04-12 21:19:07 +00:00
return ;
}
2020-02-11 12:56:36 +00:00
[ mxSession . crypto . keyVerificationManager transactions : ^ ( NSArray < MXKeyVerificationTransaction * > * _Nonnull transactions ) {
2019-04-18 21:15:01 +00:00
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][MXKeyVerification] checkPendingIncomingKeyVerificationsInSession: transactions: %@" , transactions ) ;
2019-04-18 21:15:01 +00:00
2020-02-11 12:56:36 +00:00
for ( MXKeyVerificationTransaction * transaction in transactions )
2019-04-18 21:15:01 +00:00
{
if ( transaction . isIncoming )
{
MXIncomingSASTransaction * incomingTransaction = ( MXIncomingSASTransaction * ) transaction ;
if ( incomingTransaction . state = = MXSASTransactionStateIncomingShowAccept )
{
2020-03-18 16:39:26 +00:00
[ self presentIncomingKeyVerification : incomingTransaction inSession : mxSession ] ;
2019-04-18 21:15:01 +00:00
break ;
}
}
}
} ] ;
2019-04-12 21:19:07 +00:00
}
2020-03-18 16:39:26 +00:00
// Check all opened MXSessions for incoming key verification dialog
- ( void ) checkPendingIncomingKeyVerifications
2019-04-12 21:19:07 +00:00
{
for ( MXSession * mxSession in mxSessionArray )
{
2020-03-18 16:39:26 +00:00
[ self checkPendingIncomingKeyVerificationsInSession : mxSession ] ;
2019-04-12 21:19:07 +00:00
}
}
2020-01-14 20:47:16 +00:00
- ( BOOL ) presentIncomingKeyVerificationRequest : ( MXKeyVerificationRequest * ) incomingKeyVerificationRequest
inSession : ( MXSession * ) session
{
BOOL presented = NO ;
2020-04-30 17:11:13 +00:00
if ( ! keyVerificationCoordinatorBridgePresenter . isPresenting )
2020-01-14 20:47:16 +00:00
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] presentIncomingKeyVerificationRequest" ) ;
2020-01-14 20:47:16 +00:00
2020-03-18 16:39:26 +00:00
keyVerificationCoordinatorBridgePresenter = [ [ KeyVerificationCoordinatorBridgePresenter alloc ] initWithSession : session ] ;
keyVerificationCoordinatorBridgePresenter . delegate = self ;
2020-01-14 20:47:16 +00:00
2020-04-03 14:33:56 +00:00
[ keyVerificationCoordinatorBridgePresenter presentFrom : self . presentedViewController incomingKeyVerificationRequest : incomingKeyVerificationRequest animated : YES ] ;
2020-01-14 20:47:16 +00:00
presented = YES ;
}
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][MXKeyVerification] presentIncomingKeyVerificationRequest: Controller already presented." ) ;
2020-01-14 20:47:16 +00:00
}
return presented ;
}
2020-03-18 16:39:26 +00:00
- ( BOOL ) presentIncomingKeyVerification : ( MXIncomingSASTransaction * ) transaction inSession : ( MXSession * ) mxSession
2019-04-12 21:19:07 +00:00
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][MXKeyVerification] presentIncomingKeyVerification: %@" , transaction ) ;
2019-04-18 21:15:01 +00:00
2019-04-12 21:19:07 +00:00
BOOL presented = NO ;
2020-04-30 17:11:13 +00:00
if ( ! keyVerificationCoordinatorBridgePresenter . isPresenting )
2019-04-12 21:19:07 +00:00
{
2020-03-18 16:39:26 +00:00
keyVerificationCoordinatorBridgePresenter = [ [ KeyVerificationCoordinatorBridgePresenter alloc ] initWithSession : mxSession ] ;
keyVerificationCoordinatorBridgePresenter . delegate = self ;
2019-04-12 21:19:07 +00:00
2020-04-03 14:33:56 +00:00
[ keyVerificationCoordinatorBridgePresenter presentFrom : self . presentedViewController incomingTransaction : transaction animated : YES ] ;
2019-04-12 21:19:07 +00:00
presented = YES ;
}
2019-04-18 21:15:01 +00:00
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][MXKeyVerification] presentIncomingKeyVerification: Controller already presented." ) ;
2019-04-18 21:15:01 +00:00
}
2019-04-12 21:19:07 +00:00
return presented ;
}
2020-01-30 16:59:42 +00:00
- ( BOOL ) presentUserVerificationForRoomMember : ( MXRoomMember * ) roomMember session : ( MXSession * ) mxSession
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][MXKeyVerification] presentUserVerificationForRoomMember: %@" , roomMember ) ;
2020-01-30 16:59:42 +00:00
BOOL presented = NO ;
2020-04-30 17:11:13 +00:00
if ( ! keyVerificationCoordinatorBridgePresenter . isPresenting )
2020-01-30 16:59:42 +00:00
{
2020-03-18 16:39:26 +00:00
keyVerificationCoordinatorBridgePresenter = [ [ KeyVerificationCoordinatorBridgePresenter alloc ] initWithSession : mxSession ] ;
keyVerificationCoordinatorBridgePresenter . delegate = self ;
2020-01-30 16:59:42 +00:00
2020-04-03 14:33:56 +00:00
[ keyVerificationCoordinatorBridgePresenter presentFrom : self . presentedViewController roomMember : roomMember animated : YES ] ;
2020-01-30 16:59:42 +00:00
presented = YES ;
}
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][MXKeyVerification] presentUserVerificationForRoomMember: Controller already presented." ) ;
2020-01-30 16:59:42 +00:00
}
return presented ;
}
2020-03-27 14:41:19 +00:00
- ( BOOL ) presentSelfVerificationForOtherDeviceId : ( NSString * ) deviceId inSession : ( MXSession * ) mxSession
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][MXKeyVerification] presentSelfVerificationForOtherDeviceId: %@" , deviceId ) ;
2020-03-27 14:41:19 +00:00
BOOL presented = NO ;
2020-04-30 17:11:13 +00:00
if ( ! keyVerificationCoordinatorBridgePresenter . isPresenting )
2020-03-27 14:41:19 +00:00
{
keyVerificationCoordinatorBridgePresenter = [ [ KeyVerificationCoordinatorBridgePresenter alloc ] initWithSession : mxSession ] ;
keyVerificationCoordinatorBridgePresenter . delegate = self ;
2020-04-03 14:33:56 +00:00
[ keyVerificationCoordinatorBridgePresenter presentFrom : self . presentedViewController otherUserId : mxSession . myUser . userId otherDeviceId : deviceId animated : YES ] ;
2020-03-27 14:41:19 +00:00
presented = YES ;
}
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][MXKeyVerification] presentUserVerificationForRoomMember: Controller already presented." ) ;
2020-03-27 14:41:19 +00:00
}
return presented ;
}
2020-03-18 16:39:26 +00:00
- ( void ) keyVerificationCoordinatorBridgePresenterDelegateDidComplete : ( KeyVerificationCoordinatorBridgePresenter * ) coordinatorBridgePresenter otherUserId : ( NSString * _Nonnull ) otherUserId otherDeviceId : ( NSString * _Nonnull ) otherDeviceId
2020-04-14 10:50:53 +00:00
{
2021-02-01 08:53:17 +00:00
MXCrypto * crypto = coordinatorBridgePresenter . session . crypto ;
if ( ! crypto . backup . hasPrivateKeyInCryptoStore || ! crypto . backup . enabled )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][MXKeyVerification] requestAllPrivateKeys: Request key backup private keys" ) ;
2021-02-01 08:53:17 +00:00
[ crypto setOutgoingKeyRequestsEnabled : YES onComplete : nil ] ;
}
2020-04-14 10:50:53 +00:00
[ self dismissKeyVerificationCoordinatorBridgePresenter ] ;
}
- ( void ) keyVerificationCoordinatorBridgePresenterDelegateDidCancel : ( KeyVerificationCoordinatorBridgePresenter * _Nonnull ) coordinatorBridgePresenter
{
[ self dismissKeyVerificationCoordinatorBridgePresenter ] ;
}
- ( void ) dismissKeyVerificationCoordinatorBridgePresenter
2019-04-12 21:19:07 +00:00
{
2020-03-18 16:39:26 +00:00
[ keyVerificationCoordinatorBridgePresenter dismissWithAnimated : YES completion : ^ {
[ self checkPendingIncomingKeyVerifications ] ;
2019-04-24 06:55:45 +00:00
} ] ;
2020-03-18 16:39:26 +00:00
keyVerificationCoordinatorBridgePresenter = nil ;
2019-04-12 21:19:07 +00:00
}
2020-04-03 14:33:56 +00:00
# pragma mark - New request
- ( void ) registerNewRequestNotificationForSession : ( MXSession * ) session
{
MXKeyVerificationManager * keyverificationManager = session . crypto . keyVerificationManager ;
if ( ! keyverificationManager )
{
return ;
}
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( keyVerificationNewRequestNotification : ) name : MXKeyVerificationManagerNewRequestNotification object : keyverificationManager ] ;
}
- ( void ) keyVerificationNewRequestNotification : ( NSNotification * ) notification
{
if ( [ [ UIApplication sharedApplication ] applicationState ] = = UIApplicationStateBackground )
{
return ;
}
2020-09-15 15:29:30 +00:00
if ( _masterTabBarController . authenticationInProgress )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][KeyVerification] keyVerificationNewRequestNotification: Postpone requests during the authentication process" ) ;
2020-09-15 15:29:30 +00:00
// 10 s is quite arbitrary
dispatch_after ( dispatch_time ( DISPATCH_TIME _NOW , ( int64_t ) ( 10 * NSEC_PER _SEC ) ) , dispatch_get _main _queue ( ) , ^ {
[ self keyVerificationNewRequestNotification : notification ] ;
} ) ;
return ;
}
2020-04-03 14:33:56 +00:00
NSDictionary * userInfo = notification . userInfo ;
MXKeyVerificationRequest * keyVerificationRequest = userInfo [ MXKeyVerificationManagerNotificationRequestKey ] ;
if ( [ keyVerificationRequest isKindOfClass : MXKeyVerificationByDMRequest . class ] )
{
MXKeyVerificationByDMRequest * keyVerificationByDMRequest = ( MXKeyVerificationByDMRequest * ) keyVerificationRequest ;
if ( ! keyVerificationByDMRequest . isFromMyUser && keyVerificationByDMRequest . state = = MXKeyVerificationRequestStatePending )
{
MXKAccount * currentAccount = [ MXKAccountManager sharedManager ] . activeAccounts . firstObject ;
MXSession * session = currentAccount . mxSession ;
MXRoom * room = [ currentAccount . mxSession roomWithRoomId : keyVerificationByDMRequest . roomId ] ;
if ( ! room )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][KeyVerification] keyVerificationRequestDidChangeNotification: Unknown room" ) ;
2020-04-03 14:33:56 +00:00
return ;
}
NSString * sender = keyVerificationByDMRequest . otherUser ;
[ room state : ^ ( MXRoomState * roomState ) {
NSString * senderName = [ roomState . members memberName : sender ] ;
[ self presentNewKeyVerificationRequestAlertForSession : session senderName : senderName senderId : sender request : keyVerificationByDMRequest ] ;
} ] ;
}
}
else if ( [ keyVerificationRequest isKindOfClass : MXKeyVerificationByToDeviceRequest . class ] )
{
MXKeyVerificationByToDeviceRequest * keyVerificationByToDeviceRequest = ( MXKeyVerificationByToDeviceRequest * ) keyVerificationRequest ;
2020-04-23 07:25:46 +00:00
if ( ! keyVerificationByToDeviceRequest . isFromMyDevice
2020-04-03 14:33:56 +00:00
&& keyVerificationByToDeviceRequest . state = = MXKeyVerificationRequestStatePending )
{
2020-04-23 07:25:46 +00:00
if ( keyVerificationByToDeviceRequest . isFromMyUser )
2020-04-03 14:33:56 +00:00
{
2020-04-23 07:25:46 +00:00
// Self verification
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][KeyVerification] keyVerificationNewRequestNotification: Self verification from %@" , keyVerificationByToDeviceRequest . otherDevice ) ;
2020-04-28 15:31:49 +00:00
if ( ! self . handleSelfVerificationRequest )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][KeyVerification] keyVerificationNewRequestNotification: Self verification handled elsewhere" ) ;
2020-04-28 15:31:49 +00:00
return ;
}
2020-04-23 07:25:46 +00:00
NSString * myUserId = keyVerificationByToDeviceRequest . otherUser ;
MXKAccount * account = [ [ MXKAccountManager sharedManager ] accountForUserId : myUserId ] ;
if ( account )
{
MXSession * session = account . mxSession ;
MXUser * user = [ session userWithUserId : myUserId ] ;
[ self presentNewKeyVerificationRequestAlertForSession : session senderName : user . displayname senderId : user . userId request : keyVerificationRequest ] ;
}
2020-04-03 14:33:56 +00:00
}
2020-04-23 07:25:46 +00:00
else
{
// Device verification from other user
// This happens when they or our user do not have cross - signing enabled
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][KeyVerification] keyVerificationNewRequestNotification: Device verification from other user %@:%@" , keyVerificationByToDeviceRequest . otherUser , keyVerificationByToDeviceRequest . otherDevice ) ;
2020-04-23 07:25:46 +00:00
NSString * myUserId = keyVerificationByToDeviceRequest . to ;
NSString * userId = keyVerificationByToDeviceRequest . otherUser ;
MXKAccount * account = [ [ MXKAccountManager sharedManager ] accountForUserId : myUserId ] ;
if ( account )
{
MXSession * session = account . mxSession ;
MXUser * user = [ session userWithUserId : userId ] ;
[ self presentNewKeyVerificationRequestAlertForSession : session senderName : user . displayname senderId : user . userId request : keyVerificationRequest ] ;
}
}
}
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][KeyVerification] keyVerificationNewRequestNotification. Bad request state: %@" , keyVerificationByToDeviceRequest ) ;
2020-04-03 14:33:56 +00:00
}
}
}
- ( void ) presentNewKeyVerificationRequestAlertForSession : ( MXSession * ) session
senderName : ( NSString * ) senderName
senderId : ( NSString * ) senderId
request : ( MXKeyVerificationRequest * ) keyVerificationRequest
{
2020-04-28 15:14:17 +00:00
if ( keyVerificationRequest . state ! = MXKeyVerificationRequestStatePending )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] presentNewKeyVerificationRequest: Request already accepted. Do not display it" ) ;
2020-04-28 15:14:17 +00:00
return ;
}
2020-04-03 14:33:56 +00:00
if ( self . incomingKeyVerificationRequestAlertController )
{
[ self . incomingKeyVerificationRequestAlertController dismissViewControllerAnimated : NO completion : nil ] ;
}
2021-04-14 16:50:37 +00:00
if ( self . userNewSignInAlertController
&& [ session . myUserId isEqualToString : senderId ] )
{
// If it is a self verification for my device , we can discard the new signin alert .
// Note : It will not work well with several devices to verify at the same time .
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] presentNewKeyVerificationRequest: Remove the alert for new sign in detected" ) ;
2021-04-14 16:50:37 +00:00
[ self . userNewSignInAlertController dismissViewControllerAnimated : NO completion : ^ {
self . userNewSignInAlertController = nil ;
[ self presentNewKeyVerificationRequestAlertForSession : session senderName : senderName senderId : senderId request : keyVerificationRequest ] ;
} ] ;
}
2020-04-03 14:33:56 +00:00
NSString * senderInfo ;
if ( senderName )
{
senderInfo = [ NSString stringWithFormat : @ "%@ (%@)" , senderName , senderId ] ;
}
else
{
senderInfo = senderId ;
}
2021-09-28 05:40:01 +00:00
2020-04-03 14:33:56 +00:00
2020-04-28 15:14:17 +00:00
__block id observer ;
void ( ^ removeObserver ) ( void ) = ^ ( ) {
if ( observer )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : observer ] ;
observer = nil ;
}
} ;
2020-04-03 14:33:56 +00:00
2021-09-28 05:40:01 +00:00
UIAlertController * alertController = [ UIAlertController alertControllerWithTitle : [ VectorL10n keyVerificationTileRequestIncomingTitle ]
message : senderInfo
preferredStyle : UIAlertControllerStyleAlert ] ;
2020-04-03 14:33:56 +00:00
2021-09-28 05:40:01 +00:00
[ alertController addAction : [ UIAlertAction actionWithTitle : [ VectorL10n keyVerificationTileRequestIncomingApprovalAccept ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
removeObserver ( ) ;
[ self presentIncomingKeyVerificationRequest : keyVerificationRequest inSession : session ] ;
} ] ] ;
2020-04-03 14:33:56 +00:00
2021-09-28 05:40:01 +00:00
[ alertController addAction : [ UIAlertAction actionWithTitle : [ VectorL10n keyVerificationTileRequestIncomingApprovalDecline ]
style : UIAlertActionStyleDestructive
handler : ^ ( UIAlertAction * action ) {
removeObserver ( ) ;
[ keyVerificationRequest cancelWithCancelCode : MXTransactionCancelCode . user success : ^ {
} failure : ^ ( NSError * _Nonnull error ) {
MXLogDebug ( @ "[AppDelegate][KeyVerification] Fail to cancel incoming key verification request with error: %@" , error ) ;
} ] ;
} ] ] ;
[ alertController addAction : [ UIAlertAction actionWithTitle : [ VectorL10n later ]
style : UIAlertActionStyleCancel
handler : ^ ( UIAlertAction * action ) {
removeObserver ( ) ;
} ] ] ;
2020-04-03 14:33:56 +00:00
[ self presentViewController : alertController animated : YES completion : nil ] ;
self . incomingKeyVerificationRequestAlertController = alertController ;
2020-04-28 15:14:17 +00:00
observer = [ [ NSNotificationCenter defaultCenter ] addObserverForName : MXKeyVerificationRequestDidChangeNotification
object : keyVerificationRequest
queue : [ NSOperationQueue mainQueue ]
2021-09-28 05:40:01 +00:00
usingBlock : ^ ( NSNotification * note ) {
if ( keyVerificationRequest . state ! = MXKeyVerificationRequestStatePending )
{
if ( self . incomingKeyVerificationRequestAlertController = = alertController )
{
[ self . incomingKeyVerificationRequestAlertController dismissViewControllerAnimated : NO completion : nil ] ;
removeObserver ( ) ;
}
}
} ] ;
2020-04-03 14:33:56 +00:00
}
2020-03-27 14:41:19 +00:00
# pragma mark - New Sign In
- ( void ) registerUserDidSignInOnNewDeviceNotificationForSession : ( MXSession * ) session
{
MXCrossSigning * crossSigning = session . crypto . crossSigning ;
if ( ! crossSigning )
{
return ;
}
self . userDidSignInOnNewDeviceObserver = [ NSNotificationCenter . defaultCenter addObserverForName : MXCrossSigningMyUserDidSignInOnNewDeviceNotification
object : crossSigning
queue : [ NSOperationQueue mainQueue ]
usingBlock : ^ ( NSNotification * notification )
{
NSArray < NSString * > * deviceIds = notification . userInfo [ MXCrossSigningNotificationDeviceIdsKey ] ;
[ session . matrixRestClient devices : ^ ( NSArray < MXDevice * > * devices ) {
NSPredicate * predicate = [ NSPredicate predicateWithFormat : @ "SELF.deviceId IN %@" , deviceIds ] ;
NSArray < MXDevice * > * newDevices = [ devices filteredArrayUsingPredicate : predicate ] ;
NSArray * sortedDevices = [ newDevices sortedArrayUsingComparator : ^ NSComparisonResult ( MXDevice * _Nonnull device1 , MXDevice * _Nonnull device2 ) {
if ( device1 . lastSeenTs = = device2 . lastSeenTs )
{
return NSOrderedSame ;
}
return device1 . lastSeenTs > device2 . lastSeenTs ? NSOrderedDescending : NSOrderedAscending ;
} ] ;
MXDevice * mostRecentDevice = sortedDevices . lastObject ;
if ( mostRecentDevice )
{
[ self presentNewSignInAlertForDevice : mostRecentDevice inSession : session ] ;
}
} failure : ^ ( NSError * error ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][NewSignIn] Fail to fetch devices" ) ;
2020-03-27 14:41:19 +00:00
} ] ;
} ] ;
}
- ( void ) presentNewSignInAlertForDevice : ( MXDevice * ) device inSession : ( MXSession * ) session
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] presentNewSignInAlertForDevice: %@" , device . deviceId ) ;
2020-10-13 16:04:21 +00:00
2020-03-27 14:41:19 +00:00
if ( self . userNewSignInAlertController )
{
2020-10-13 16:04:21 +00:00
[ self . userNewSignInAlertController dismissViewControllerAnimated : NO completion : nil ] ;
2020-03-27 14:41:19 +00:00
}
NSString * deviceInfo ;
if ( device . displayName )
{
deviceInfo = [ NSString stringWithFormat : @ "%@ (%@)" , device . displayName , device . deviceId ] ;
}
else
{
deviceInfo = device . deviceId ;
}
2021-09-28 05:40:01 +00:00
NSString * alertMessage = [ VectorL10n deviceVerificationSelfVerifyAlertMessage : deviceInfo ] ;
2020-03-27 14:41:19 +00:00
2021-09-28 05:40:01 +00:00
UIAlertController * alert = [ UIAlertController alertControllerWithTitle : [ VectorL10n deviceVerificationSelfVerifyAlertTitle ]
2020-03-27 14:41:19 +00:00
message : alertMessage
preferredStyle : UIAlertControllerStyleAlert ] ;
2021-09-28 05:40:01 +00:00
[ alert addAction : [ UIAlertAction actionWithTitle : [ VectorL10n deviceVerificationSelfVerifyAlertValidateAction ]
2020-03-27 14:41:19 +00:00
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2020-10-13 16:04:21 +00:00
self . userNewSignInAlertController = nil ;
[ self presentSelfVerificationForOtherDeviceId : device . deviceId inSession : session ] ;
} ] ] ;
2020-03-27 14:41:19 +00:00
2021-09-28 05:40:01 +00:00
[ alert addAction : [ UIAlertAction actionWithTitle : [ VectorL10n later ]
2020-10-13 16:04:21 +00:00
style : UIAlertActionStyleCancel
handler : ^ ( UIAlertAction * action ) {
self . userNewSignInAlertController = nil ;
} ] ] ;
2021-09-28 05:40:01 +00:00
2020-04-03 14:33:56 +00:00
[ self presentViewController : alert animated : YES completion : nil ] ;
2020-03-27 14:41:19 +00:00
self . userNewSignInAlertController = alert ;
}
2020-10-13 15:15:47 +00:00
# pragma mark - Cross - signing reset detection
- ( void ) registerDidChangeCrossSigningKeysNotificationForSession : ( MXSession * ) session
{
MXCrossSigning * crossSigning = session . crypto . crossSigning ;
if ( ! crossSigning )
{
return ;
}
2020-10-13 16:32:00 +00:00
MXWeakify ( self ) ;
2020-10-13 15:15:47 +00:00
self . userDidChangeCrossSigningKeysObserver = [ NSNotificationCenter . defaultCenter addObserverForName : MXCrossSigningDidChangeCrossSigningKeysNotification
2020-10-13 16:04:21 +00:00
object : crossSigning
queue : [ NSOperationQueue mainQueue ]
usingBlock : ^ ( NSNotification * notification )
{
2020-10-13 16:32:00 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] registerDidChangeCrossSigningKeysNotificationForSession" ) ;
2020-10-13 16:04:21 +00:00
if ( self . userNewSignInAlertController )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] registerDidChangeCrossSigningKeysNotificationForSession: Hide NewSignInAlertController" ) ;
2020-10-13 16:04:21 +00:00
2020-10-14 13:38:18 +00:00
[ self . userNewSignInAlertController dismissViewControllerAnimated : NO completion : ^ {
[ self . masterTabBarController presentVerifyCurrentSessionAlertIfNeededWithSession : session ] ;
} ] ;
2020-10-13 16:04:21 +00:00
self . userNewSignInAlertController = nil ;
}
2020-10-14 13:38:18 +00:00
else
{
[ self . masterTabBarController presentVerifyCurrentSessionAlertIfNeededWithSession : session ] ;
}
2020-10-13 15:15:47 +00:00
} ] ;
}
2020-04-23 09:58:19 +00:00
# pragma mark - Complete security
- ( BOOL ) presentCompleteSecurityForSession : ( MXSession * ) mxSession
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][MXKeyVerification] presentCompleteSecurityForSession" ) ;
2020-04-23 09:58:19 +00:00
BOOL presented = NO ;
2020-04-30 17:11:13 +00:00
if ( ! keyVerificationCoordinatorBridgePresenter . isPresenting )
2020-04-23 09:58:19 +00:00
{
keyVerificationCoordinatorBridgePresenter = [ [ KeyVerificationCoordinatorBridgePresenter alloc ] initWithSession : mxSession ] ;
keyVerificationCoordinatorBridgePresenter . delegate = self ;
[ keyVerificationCoordinatorBridgePresenter presentCompleteSecurityFrom : self . presentedViewController isNewSignIn : NO animated : YES ] ;
presented = YES ;
}
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate][MXKeyVerification] presentCompleteSecurityForSession: Controller already presented." ) ;
2020-04-23 09:58:19 +00:00
}
return presented ;
}
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
__weak typeof ( self ) weakSelf = self ;
MXSession * mainSession = self . mxSessions . firstObject ;
NSString * homeServerName = mainSession . matrixRestClient . credentials . homeServerName ;
2021-09-28 05:40:01 +00:00
NSString * alertMessage = [ VectorL10n gdprConsentNotGivenAlertMessage : homeServerName ] ;
2018-05-23 15:01:18 +00:00
2021-09-28 05:40:01 +00:00
UIAlertController * alert = [ UIAlertController alertControllerWithTitle : [ VectorL10n settingsTermConditions ]
2018-05-23 15:01:18 +00:00
message : alertMessage
preferredStyle : UIAlertControllerStyleAlert ] ;
2021-09-28 05:40:01 +00:00
[ alert addAction : [ UIAlertAction actionWithTitle : [ VectorL10n gdprConsentNotGivenAlertReviewNowAction ]
2018-05-23 15:01:18 +00:00
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
typeof ( weakSelf ) strongSelf = weakSelf ;
if ( strongSelf )
{
2020-04-03 14:33:56 +00:00
[ strongSelf presentGDPRConsentFromViewController : self . presentedViewController consentURI : consentURI ] ;
2018-05-23 15:01:18 +00:00
}
} ] ] ;
2021-09-28 05:40:01 +00:00
[ alert addAction : [ UIAlertAction actionWithTitle : [ VectorL10n later ]
2018-05-23 15:01:18 +00:00
style : UIAlertActionStyleCancel
handler : nil ] ] ;
2020-04-03 14:33:56 +00:00
[ self presentViewController : alert animated : YES completion : nil ] ;
2018-05-23 15:01:18 +00:00
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
2021-09-28 05:40:01 +00:00
UIBarButtonItem * closeBarButtonItem = [ [ UIBarButtonItem alloc ] initWithTitle : [ MatrixKitL10n close ]
2018-05-23 15:01:18 +00:00
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
2020-04-08 15:02:00 +00:00
UINavigationController * navigationController = [ [ RiotNavigationController 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 ] ;
2020-07-08 15:34:50 +00:00
BOOL botCreationEnabled = [ [ NSUserDefaults standardUserDefaults ] boolForKey : @ "enableBotCreation" ] ;
2018-08-24 10:31:57 +00:00
2020-07-08 15:34:50 +00:00
if ( botCreationEnabled )
{
// And create the room with riot bot in //
self . onBoardingManager = [ [ OnBoardingManager alloc ] initWithSession : session ] ;
MXWeakify ( self ) ;
void ( ^ createRiotBotDMcompletion ) ( void ) = ^ ( ) {
MXStrongifyAndReturnIfNil ( self ) ;
2018-08-24 10:31:57 +00:00
2020-07-08 15:34:50 +00:00
self . onBoardingManager = nil ;
} ;
[ self . onBoardingManager createRiotBotDirectMessageIfNeededWithSuccess : ^ {
createRiotBotDMcompletion ( ) ;
} failure : ^ ( NSError * _Nonnull error ) {
createRiotBotDMcompletion ( ) ;
} ] ;
}
2018-05-23 15:01:18 +00:00
}
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" ] ;
2020-08-05 14:12:21 +00:00
NSMutableDictionary * defaults = [ [ NSDictionary dictionaryWithContentsOfFile : defaultsPathFromApp ] mutableCopy ] ;
// add pusher ids , as they don ' t belong to plist anymore
defaults [ @ "pushKitAppIdProd" ] = BuildSettings . pushKitAppIdProd ;
defaults [ @ "pushKitAppIdDev" ] = BuildSettings . pushKitAppIdDev ;
defaults [ @ "pusherAppIdProd" ] = BuildSettings . pusherAppIdProd ;
defaults [ @ "pusherAppIdDev" ] = BuildSettings . pusherAppIdDev ;
2018-07-02 12:41:55 +00:00
[ [ NSUserDefaults standardUserDefaults ] registerDefaults : defaults ] ;
2021-07-05 14:27:52 +00:00
// Migrates old UserDefaults values if showDecryptedContentInNotifications hasn ' t been set
2020-05-22 11:27:30 +00:00
if ( ! RiotSettings . shared . isUserDefaultsMigrated )
{
[ RiotSettings . shared migrate ] ;
}
2021-07-05 14:27:52 +00:00
// Show encrypted message notification content by default .
2018-07-03 08:31:14 +00:00
if ( ! RiotSettings . shared . isShowDecryptedContentInNotificationsHasBeenSetOnce )
2018-07-02 12:41:55 +00:00
{
2021-07-09 14:00:03 +00:00
RiotSettings . shared . showDecryptedContentInNotifications = BuildSettings . decryptNotificationsByDefault ;
2018-07-02 12:41:55 +00:00
}
}
2020-07-10 19:28:57 +00:00
# pragma mark - App version management
- ( void ) checkAppVersion
{
// Check if we should display a major update alert
[ self checkMajorUpdate ] ;
// Update the last app version used
[ AppVersion updateLastUsedVersion ] ;
}
- ( void ) checkMajorUpdate
{
if ( self . majorUpdateManager . shouldShowMajorUpdate )
{
2020-07-21 14:26:37 +00:00
// When you do not understand why the UI does not work as expected . . .
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self showMajorUpdate ] ;
} ) ;
2020-07-10 19:28:57 +00:00
}
}
- ( void ) showMajorUpdate
{
if ( ! self . slidingModalPresenter )
{
self . slidingModalPresenter = [ SlidingModalPresenter new ] ;
}
[ self . slidingModalPresenter dismissWithAnimated : NO completion : nil ] ;
MajorUpdateViewController * majorUpdateViewController = [ MajorUpdateViewController instantiate ] ;
MXWeakify ( self ) ;
majorUpdateViewController . didTapLearnMoreButton = ^ {
MXStrongifyAndReturnIfNil ( self ) ;
2020-07-11 07:53:19 +00:00
[ [ UIApplication sharedApplication ] vc_open : self . majorUpdateManager . learnMoreURL completionHandler : ^ ( BOOL success ) {
if ( ! success )
{
2021-09-28 05:40:01 +00:00
[ self showAlertWithTitle : [ MatrixKitL10n error ] message : [ VectorL10n roomMessageUnableOpenLinkErrorMessage ] ] ;
2020-07-11 07:53:19 +00:00
}
} ] ;
2020-07-10 19:28:57 +00:00
[ self . slidingModalPresenter dismissWithAnimated : YES completion : ^ {
} ] ;
} ;
majorUpdateViewController . didTapDoneButton = ^ {
MXStrongifyAndReturnIfNil ( self ) ;
[ self . slidingModalPresenter dismissWithAnimated : YES completion : ^ {
} ] ;
} ;
[ self . slidingModalPresenter present : majorUpdateViewController
from : self . presentedViewController
animated : YES
completion : nil ] ;
}
2020-07-21 13:16:52 +00:00
# pragma mark - SetPinCoordinatorBridgePresenterDelegate
- ( void ) setPinCoordinatorBridgePresenterDelegateDidComplete : ( SetPinCoordinatorBridgePresenter * ) coordinatorBridgePresenter
{
[ coordinatorBridgePresenter dismiss ] ;
self . setPinCoordinatorBridgePresenter = nil ;
2020-07-22 19:45:20 +00:00
[ self afterAppUnlockedByPin : [ UIApplication sharedApplication ] ] ;
2020-07-21 13:16:52 +00:00
}
2020-09-29 14:46:07 +00:00
- ( void ) setPinCoordinatorBridgePresenterDelegateDidCompleteWithReset : ( SetPinCoordinatorBridgePresenter * ) coordinatorBridgePresenter dueToTooManyErrors : ( BOOL ) dueToTooManyErrors
2020-07-21 13:16:52 +00:00
{
2020-09-29 14:46:07 +00:00
if ( dueToTooManyErrors )
{
2021-09-28 05:40:01 +00:00
[ self showAlertWithTitle : nil message : [ VectorL10n pinProtectionKickUserAlertMessage ] ] ;
2020-09-29 14:57:23 +00:00
[ self logoutWithConfirmation : NO completion : nil ] ;
2020-09-29 14:46:07 +00:00
}
else
{
2020-09-29 15:42:19 +00:00
[ coordinatorBridgePresenter dismiss ] ;
self . setPinCoordinatorBridgePresenter = nil ;
2020-09-29 14:46:07 +00:00
[ self logoutWithConfirmation : NO completion : nil ] ;
}
2020-07-21 13:16:52 +00:00
}
2020-07-10 19:28:57 +00:00
2021-02-07 16:55:36 +00:00
# pragma mark - CallPresenterDelegate
2020-12-01 10:41:31 +00:00
2021-02-07 16:55:36 +00:00
- ( void ) callPresenter : ( CallPresenter * ) presenter presentCallViewController : ( CallViewController * ) viewController completion : ( void ( ^ ) ( void ) ) completion
2020-12-01 10:41:31 +00:00
{
if ( @ available ( iOS 13.0 , * ) )
{
viewController . modalPresentationStyle = UIModalPresentationFullScreen ;
}
2021-04-19 12:58:30 +00:00
[ self presentViewController : viewController animated : NO completion : completion ] ;
2020-12-01 10:41:31 +00:00
}
2021-04-02 11:22:03 +00:00
- ( void ) callPresenter : ( CallPresenter * ) presenter dismissCallViewController : ( UIViewController * ) viewController completion : ( void ( ^ ) ( void ) ) completion
2020-12-01 10:41:31 +00:00
{
// Check whether the call view controller is actually presented
if ( viewController . presentingViewController )
{
2021-04-19 12:58:30 +00:00
[ viewController . presentingViewController dismissViewControllerAnimated : NO completion : ^ {
2020-12-01 10:41:31 +00:00
2021-04-02 11:22:03 +00:00
if ( [ viewController isKindOfClass : CallViewController . class ] )
2020-12-01 10:41:31 +00:00
{
2021-04-02 11:22:03 +00:00
CallViewController * callVC = ( CallViewController * ) viewController ;
if ( callVC . shouldPromptForStunServerFallback )
{
[ self promptForStunServerFallback ] ;
}
2020-12-01 10:41:31 +00:00
}
if ( completion )
{
completion ( ) ;
}
} ] ;
}
else
{
if ( completion )
{
completion ( ) ;
}
}
}
2021-04-02 11:22:03 +00:00
- ( void ) callPresenter : ( CallPresenter * ) presenter enterPipForCallViewController : ( UIViewController * ) viewController completion : ( void ( ^ ) ( void ) ) completion
2021-01-08 00:57:39 +00:00
{
// Check whether the call view controller is actually presented
if ( viewController . presentingViewController )
{
[ viewController dismissViewControllerAnimated : YES completion : completion ] ;
}
else
{
if ( completion )
{
completion ( ) ;
}
}
}
2021-04-02 11:22:03 +00:00
- ( void ) callPresenter : ( CallPresenter * ) presenter exitPipForCallViewController : ( UIViewController * ) viewController completion : ( void ( ^ ) ( void ) ) completion
2021-01-08 00:57:39 +00:00
{
if ( @ available ( iOS 13.0 , * ) )
{
viewController . modalPresentationStyle = UIModalPresentationFullScreen ;
}
[ self presentViewController : viewController animated : YES completion : completion ] ;
}
2021-01-07 13:38:27 +00:00
# pragma mark - Authentication
- ( BOOL ) continueSSOLoginWithToken : ( NSString * ) loginToken txnId : ( NSString * ) txnId
{
AuthenticationViewController * authVC = self . masterTabBarController . authViewController ;
if ( ! authVC )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[AppDelegate] Fail to continue SSO login" ) ;
2021-01-07 13:38:27 +00:00
return NO ;
}
return [ authVC continueSSOLoginWithToken : loginToken txnId : txnId ] ;
}
2020-12-02 20:17:06 +00:00
2021-08-30 14:24:16 +00:00
# pragma mark - Private
- ( void ) clearCache
{
[ MXMediaManager clearCache ] ;
[ MXKAttachment clearCache ] ;
[ VoiceMessageAttachmentCacheManagerBridge clearCache ] ;
2021-09-08 14:10:13 +00:00
[ URLPreviewService . shared clearStore ] ;
2021-08-30 14:24:16 +00:00
}
2021-09-22 12:58:19 +00:00
# pragma mark - Spaces
- ( void ) openSpaceWithId : ( NSString * ) spaceId
{
MXSession * session = mxSessionArray . firstObject ;
if ( [ session . spaceService getSpaceWithId : spaceId ] ) {
[ self restoreInitialDisplay : ^ {
[ self . delegate legacyAppDelegate : self didNavigateToSpaceWithId : spaceId ] ;
} ] ;
}
else
{
MXWeakify ( self ) ;
__block __weak id observer = [ [ NSNotificationCenter defaultCenter ] addObserverForName : MXSpaceService . didBuildSpaceGraph object : nil queue : nil usingBlock : ^ ( NSNotification * _Nonnull note ) {
MXStrongifyAndReturnIfNil ( self ) ;
[ [ NSNotificationCenter defaultCenter ] removeObserver : observer ] ;
if ( [ session . spaceService getSpaceWithId : spaceId ] ) {
[ self restoreInitialDisplay : ^ {
[ self . delegate legacyAppDelegate : self didNavigateToSpaceWithId : spaceId ] ;
} ] ;
}
} ] ;
}
}
# pragma mark - SpaceDetailPresenterDelegate
- ( void ) spaceDetailPresenterDidComplete : ( SpaceDetailPresenter * ) presenter
{
self . spaceDetailPresenter = nil ;
}
- ( void ) spaceDetailPresenter : ( SpaceDetailPresenter * ) presenter didOpenSpaceWithId : ( NSString * ) spaceId
{
self . spaceDetailPresenter = nil ;
[ self openSpaceWithId : spaceId ] ;
}
- ( void ) spaceDetailPresenter : ( SpaceDetailPresenter * ) presenter didJoinSpaceWithId : ( NSString * ) spaceId
{
self . spaceDetailPresenter = nil ;
[ self openSpaceWithId : spaceId ] ;
}
2014-10-02 15:02:47 +00:00
@ end