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
2014-10-02 15:02:47 +00:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : // www . apache . org / licenses / LICENSE -2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
# import "AppDelegate.h"
2015-08-21 18:00:39 +00:00
2017-08-22 13:02:16 +00:00
# import < Intents / Intents . h >
2017-08-21 10:28:05 +00:00
# import < PushKit / PushKit . h >
2017-08-22 13:02:16 +00:00
2015-11-20 17:14:15 +00:00
# import "RecentsDataSource.h"
2015-08-21 18:00:39 +00:00
# import "RoomDataSource.h"
2015-11-17 23:15:52 +00:00
2015-08-21 18:00:39 +00:00
# import "EventFormatter.h"
2015-11-17 23:15:52 +00:00
# import "RoomViewController.h"
2016-06-21 19:47:20 +00:00
# import "DirectoryViewController.h"
# import "SettingsViewController.h"
# import "ContactDetailsViewController.h"
2017-06-29 15:33:08 +00:00
# import "BugReportViewController.h"
2017-11-14 17:21:01 +00:00
# import "RoomKeyRequestViewController.h"
2017-06-29 15:33:08 +00:00
2017-11-23 15:09:19 +00:00
# import < MatrixKit / MatrixKit . h >
2018-01-03 13:59:01 +00:00
# import "MatrixSDK/MXGoogleAnalytics.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 >
2017-12-22 12:19:40 +00:00
// Google Analytics
# import "GAI.h"
# import "GAIFields.h"
# import "GAIDictionaryBuilder.h"
2017-12-06 14:57:48 +00:00
# include < MatrixSDK / MXGoogleAnalytics . 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
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 >
2017-06-21 10:02:49 +00:00
# endif
2017-06-14 15:37:22 +00:00
2016-09-02 20:21:02 +00:00
# define CALL_STATUS _BAR _HEIGHT 44
2015-02-04 23:16:11 +00:00
# define MAKE_STRING ( x ) # x
# define MAKE_NS _STRING ( x ) @ MAKE_STRING ( x )
2015-02-04 17:18:38 +00:00
2016-05-19 15:22:29 +00:00
NSString * const kAppDelegateDidTapStatusBarNotification = @ "kAppDelegateDidTapStatusBarNotification" ;
2016-08-11 08:38:10 +00:00
NSString * const kAppDelegateNetworkStatusDidChangeNotification = @ "kAppDelegateNetworkStatusDidChangeNotification" ;
2016-05-19 15:22:29 +00:00
2017-08-21 10:28:05 +00:00
@ interface AppDelegate ( ) < PKPushRegistryDelegate >
2015-06-18 09:19:42 +00:00
{
2015-05-11 21:11:23 +00:00
/ * *
Reachability observer
* /
2015-02-18 16:40:55 +00:00
id reachabilityObserver ;
2015-04-17 13:45:32 +00:00
2015-08-04 16:37:48 +00:00
/ * *
MatrixKit error observer
* /
id matrixKitErrorObserver ;
2015-05-11 21:11:23 +00:00
/ * *
matrix session observer used to detect new opened sessions .
* /
2015-04-17 13:45:32 +00:00
id matrixSessionStateObserver ;
2015-04-30 14:19:12 +00:00
2015-05-11 21:11:23 +00:00
/ * *
2015-06-18 13:20:33 +00:00
matrix account observers .
2015-05-11 21:11:23 +00:00
* /
2015-06-18 13:20:33 +00:00
id addedAccountObserver ;
id removedAccountObserver ;
2015-05-11 21:11:23 +00:00
/ * *
matrix call observer used to handle incoming / outgoing call .
* /
id matrixCallObserver ;
/ * *
The current call view controller ( if any ) .
* /
2016-08-19 15:43:29 +00:00
CallViewController * currentCallViewController ;
2017-11-14 17:21:01 +00:00
/ * *
Incoming room key requests observers
* /
id roomKeyRequestObserver ;
id roomKeyRequestCancellationObserver ;
/ * *
If any the currently displayed sharing key dialog
* /
RoomKeyRequestViewController * roomKeyRequestViewController ;
2015-05-28 16:31:08 +00:00
/ * *
Account picker used in case of multiple account .
* /
2017-07-14 14:41:25 +00:00
UIAlertController * accountPicker ;
2015-11-17 23:15:52 +00:00
/ * *
Array of ` MXSession` instances .
* /
NSMutableArray * mxSessionArray ;
2016-04-06 09:28:12 +00:00
2016-04-12 15:44:06 +00:00
/ * *
The fragment of the universal link being processing .
Only one fragment is handled at a time .
* /
NSString * universalLinkFragmentPending ;
2017-02-13 16:58:29 +00:00
/ * *
The potential room alias related to the fragment of the universal link being processing .
Only one alias is handled at a time , the key is the room id and the value is the alias .
* /
NSDictionary * universalLinkFragmentPendingRoomAlias ;
2017-07-14 14:41:25 +00:00
2016-04-12 15:44:06 +00:00
/ * *
An universal link may need to wait for an account to be logged in or for a
session to be running . Hence , this observer .
* /
id universalLinkWaitingObserver ;
2017-07-14 14:41:25 +00:00
2016-06-16 14:50:41 +00:00
/ * *
Suspend the error notifications when the navigation stack of the root view controller is updating .
* /
BOOL isErrorNotificationSuspended ;
2017-07-14 14:41:25 +00:00
2016-04-13 15:08:02 +00:00
/ * *
Completion block called when [ self popToHomeViewControllerAnimated : ] has been
completed .
* /
void ( ^ popToHomeViewControllerCompletion ) ( ) ;
2017-07-14 14:41:25 +00:00
2016-05-20 12:31:23 +00:00
/ * *
The listeners to call events .
There is one listener per MXSession .
2016-05-20 13:45:29 +00:00
The key is an identifier of the MXSession . The value , the listener .
2016-05-20 12:31:23 +00:00
* /
NSMutableDictionary * callEventsListeners ;
2017-07-14 14:41:25 +00:00
2017-10-18 17:18:14 +00:00
/ * *
The notification listener blocks .
There is one block per MXSession .
The key is an identifier of the MXSession . The value , the listener block .
* /
NSMutableDictionary < NSNumber * , MXOnNotification > * notificationListenerBlocks ;
/ * *
The list of the events which need to be notified at the end of the background sync .
There is one list per MXSession .
The key is an identifier of the MXSession . The value , an array of dictionaries ( eventId , roomId . . . for each event ) .
* /
NSMutableDictionary < NSNumber * , NSMutableArray < NSDictionary * > * > * eventsToNotify ;
2017-12-29 12:04:57 +00:00
/ * *
Cache for payloads received with incoming push notifications .
The key is the event id . The value , the payload .
* /
NSMutableDictionary < NSString * , NSDictionary * > * incomingPushPayloads ;
2016-05-20 12:31:23 +00:00
/ * *
Currently displayed "Call not supported" alert .
* /
2017-07-14 14:41:25 +00:00
UIAlertController * noCallSupportAlert ;
2016-12-16 12:43:08 +00:00
/ * *
Prompt to ask the user to log in again .
* /
2017-07-14 14:41:25 +00:00
UIAlertController * cryptoDataCorruptedAlert ;
2017-01-31 23:18:01 +00:00
/ * *
The launch animation container view
* /
UIView * launchAnimationContainerView ;
NSDate * launchAnimationStart ;
2015-02-18 16:40:55 +00:00
}
2014-10-02 15:02:47 +00:00
2017-07-14 14:41:25 +00:00
@ property ( strong , nonatomic ) UIAlertController * mxInAppNotification ;
2015-04-30 14:19:12 +00:00
2017-08-23 16:07:14 +00:00
@ property ( nonatomic , nullable , copy ) void ( ^ registrationForRemoteNotificationsCompletion ) ( NSError * ) ;
2015-04-30 14:19:12 +00:00
2017-09-04 12:06:48 +00:00
2017-08-21 10:28:05 +00:00
@ property ( nonatomic , strong ) PKPushRegistry * pushRegistry ;
2017-10-23 14:58:55 +00:00
@ property ( nonatomic ) NSMutableDictionary < NSNumber * , NSMutableArray < NSString * > * > * incomingPushEventIds ;
2017-08-21 10:28:05 +00:00
2014-10-02 15:02:47 +00:00
@ end
@ implementation AppDelegate
2014-10-08 12:14:12 +00:00
# pragma mark -
2018-01-02 10:56:30 +00:00
+ ( void ) initialize
{
NSLog ( @ "[AppDelegate] initialize" ) ;
// Set the App Group identifier .
MXSDKOptions * sdkOptions = [ MXSDKOptions sharedInstance ] ;
sdkOptions . applicationGroupIdentifier = @ "group.im.vector" ;
// Track SDK performance on Google analytics
sdkOptions . analyticsDelegate = [ [ MXGoogleAnalytics alloc ] init ] ;
// Redirect NSLogs to files only if we are not debugging
if ( ! isatty ( STDERR_FILENO ) )
{
[ MXLogger redirectNSLogToFiles : YES ] ;
}
NSLog ( @ "[AppDelegate] initialize: Done" ) ;
}
2015-06-18 09:19:42 +00:00
+ ( AppDelegate * ) theDelegate
{
2014-10-08 12:14:12 +00:00
return ( AppDelegate * ) [ [ UIApplication sharedApplication ] delegate ] ;
}
2015-02-04 17:18:38 +00:00
# pragma mark -
2015-06-18 09:19:42 +00:00
- ( NSString * ) appVersion
{
if ( ! _appVersion )
{
2015-02-04 17:18:38 +00:00
_appVersion = [ [ NSBundle mainBundle ] objectForInfoDictionaryKey : @ "CFBundleShortVersionString" ] ;
}
return _appVersion ;
}
2015-06-18 09:19:42 +00:00
- ( NSString * ) build
{
if ( ! _build )
{
2015-02-04 17:18:38 +00:00
NSString * buildBranch = nil ;
NSString * buildNumber = nil ;
// Check whether GIT_BRANCH and BUILD_NUMBER were provided during compilation in command line argument .
2015-02-04 23:16:11 +00:00
# ifdef GIT_BRANCH
buildBranch = MAKE_NS _STRING ( GIT_BRANCH ) ;
2015-02-04 17:18:38 +00:00
# endif
2015-02-04 23:16:11 +00:00
# ifdef BUILD_NUMBER
buildNumber = [ NSString stringWithFormat : @ "#%d" , BUILD_NUMBER ] ;
2015-02-04 17:18:38 +00:00
# endif
2015-06-18 09:19:42 +00:00
if ( buildBranch && buildNumber )
{
2015-02-04 17:18:38 +00:00
_build = [ NSString stringWithFormat : @ "%@ %@" , buildBranch , buildNumber ] ;
} else if ( buildNumber ) {
_build = buildNumber ;
2015-06-18 09:19:42 +00:00
} else
{
2015-08-12 07:32:45 +00:00
_build = buildBranch ? buildBranch : NSLocalizedStringFromTable ( @ "settings_config_no_build_info" , @ "Vector" , nil ) ;
2015-02-04 17:18:38 +00:00
}
}
return _build ;
}
2016-02-11 15:09:35 +00:00
- ( void ) setIsOffline : ( BOOL ) isOffline
{
if ( ! reachabilityObserver )
{
// Define reachability observer when isOffline property is set for the first time
reachabilityObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : AFNetworkingReachabilityDidChangeNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * note ) {
NSNumber * statusItem = note . userInfo [ AFNetworkingReachabilityNotificationStatusItem ] ;
if ( statusItem )
{
AFNetworkReachabilityStatus reachabilityStatus = statusItem . integerValue ;
if ( reachabilityStatus = = AFNetworkReachabilityStatusNotReachable )
{
[ AppDelegate theDelegate ] . isOffline = YES ;
}
else
{
[ AppDelegate theDelegate ] . isOffline = NO ;
}
}
} ] ;
}
2016-08-11 08:38:10 +00:00
if ( _isOffline ! = isOffline )
{
_isOffline = isOffline ;
[ [ NSNotificationCenter defaultCenter ] postNotificationName : kAppDelegateNetworkStatusDidChangeNotification object : nil ] ;
}
2016-02-11 15:09:35 +00:00
}
2017-03-14 16:25:46 +00:00
- ( UINavigationController * ) secondaryNavigationController
{
UIViewController * rootViewController = self . window . rootViewController ;
if ( [ rootViewController isKindOfClass : [ UISplitViewController class ] ] )
{
UISplitViewController * splitViewController = ( UISplitViewController * ) rootViewController ;
if ( splitViewController . viewControllers . count = = 2 )
{
UIViewController * secondViewController = [ splitViewController . viewControllers lastObject ] ;
if ( [ secondViewController isKindOfClass : [ UINavigationController class ] ] )
{
return ( UINavigationController * ) secondViewController ;
}
}
}
return nil ;
}
2015-02-12 10:16:28 +00:00
# pragma mark - UIApplicationDelegate
2014-10-02 15:02:47 +00:00
2017-11-15 14:49:54 +00:00
- ( BOOL ) application : ( UIApplication * ) application willFinishLaunchingWithOptions : ( nullable NSDictionary * ) launchOptions
{
2017-12-19 13:44:10 +00:00
NSLog ( @ "[AppDelegate] willFinishLaunchingWithOptions: Done" ) ;
2017-11-15 14:49:54 +00:00
return YES ;
}
2015-06-18 09:19:42 +00:00
- ( BOOL ) application : ( UIApplication * ) application didFinishLaunchingWithOptions : ( NSDictionary * ) launchOptions
{
2017-12-28 13:23:12 +00:00
NSDate * startDate = [ NSDate date ] ;
2016-03-23 13:45:16 +00:00
# ifdef DEBUG
// log the full launchOptions only in DEBUG
2016-02-03 11:21:45 +00:00
NSLog ( @ "[AppDelegate] didFinishLaunchingWithOptions: %@" , launchOptions ) ;
2016-03-23 13:45:16 +00:00
# else
NSLog ( @ "[AppDelegate] didFinishLaunchingWithOptions" ) ;
# endif
2017-12-19 13:44:10 +00:00
2017-12-27 14:40:25 +00:00
// User credentials ( in MXKAccount ) are no more stored in NSUserDefaults but in a file
// as advised at https : // forums . developer . apple . com / thread / 15685 #45849 .
// So , there is no more need to loop ( sometimes forever ) until
// [ application isProtectedDataAvailable ] becomes YES .
2017-12-29 16:01:23 +00:00
// But , as we are not so sure , loop but no more than 10 s .
2017-12-31 09:21:22 +00:00
// // TODO : Remove this loop .
// NSUInteger loopCount = 0 ;
//
// // Check whether the content protection is active before going further .
// // Should fix the spontaneous logout .
// while ( ! [ application isProtectedDataAvailable ] && loopCount + + < 50 )
// {
// // Wait for protected data .
// [ [ NSRunLoop currentRunLoop ] runUntilDate : [ NSDate dateWithTimeIntervalSinceNow : 0.2 f ] ] ;
// }
//
// NSLog ( @ "[AppDelegate] didFinishLaunchingWithOptions: isProtectedDataAvailable: %@ (%tu)" , @ ( [ application isProtectedDataAvailable ] ) , loopCount ) ;
NSLog ( @ "[AppDelegate] didFinishLaunchingWithOptions: isProtectedDataAvailable: %@" , @ ( [ application isProtectedDataAvailable ] ) ) ;
2017-12-19 13:44:10 +00:00
2017-01-24 14:46:46 +00:00
// Log app information
NSString * appDisplayName = [ [ [ NSBundle mainBundle ] infoDictionary ] objectForKey : @ "CFBundleDisplayName" ] ;
NSString * appVersion = [ AppDelegate theDelegate ] . appVersion ;
NSString * build = [ AppDelegate theDelegate ] . build ;
2017-07-14 14:41:25 +00:00
2017-01-24 14:46:46 +00:00
NSLog ( @ "------------------------------" ) ;
NSLog ( @ "Application info:" ) ;
NSLog ( @ "%@ version: %@" , appDisplayName , appVersion ) ;
NSLog ( @ "MatrixKit version: %@" , MatrixKitVersion ) ;
NSLog ( @ "MatrixSDK version: %@" , MatrixSDKVersion ) ;
NSLog ( @ "Build: %@\n" , build ) ;
NSLog ( @ "------------------------------\n" ) ;
2017-07-21 09:08:33 +00:00
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" ] ;
[ sharedUserDefaults synchronize ] ;
[ [ NSUserDefaults standardUserDefaults ] removeObjectForKey : @ "appLanguage" ] ;
[ [ NSUserDefaults standardUserDefaults ] synchronize ] ;
}
}
[ NSBundle mxk_setLanguage : language ] ;
2017-07-21 09:08:33 +00:00
[ NSBundle mxk_setFallbackLanguage : @ "en" ] ;
2016-02-11 14:19:49 +00:00
// Define the navigation bar text color
2017-03-08 15:14:41 +00:00
[ [ UINavigationBar appearance ] setTintColor : kRiotColorGreen ] ;
2015-11-26 17:23:04 +00:00
2015-12-21 09:35:48 +00:00
// Customize the localized string table
[ NSBundle mxk_customizeLocalizedStringTableName : @ "Vector" ] ;
2015-11-17 23:15:52 +00:00
mxSessionArray = [ NSMutableArray array ] ;
2016-05-20 12:31:23 +00:00
callEventsListeners = [ NSMutableDictionary dictionary ] ;
2017-10-18 17:18:14 +00:00
notificationListenerBlocks = [ NSMutableDictionary dictionary ] ;
eventsToNotify = [ NSMutableDictionary dictionary ] ;
2017-12-29 12:04:57 +00:00
incomingPushPayloads = [ NSMutableDictionary dictionary ] ;
2015-11-17 23:15:52 +00:00
2017-03-28 16:17:14 +00:00
// To simplify navigation into the app , we retrieve here the main navigation controller and the tab bar controller .
UISplitViewController * splitViewController = ( UISplitViewController * ) self . window . rootViewController ;
splitViewController . delegate = self ;
2015-11-17 23:15:52 +00:00
2017-03-28 16:17:14 +00:00
_masterNavigationController = [ splitViewController . viewControllers objectAtIndex : 0 ] ;
_masterTabBarController = _masterNavigationController . viewControllers . firstObject ;
2015-11-17 23:15:52 +00:00
2017-08-14 20:35:16 +00:00
// Force the background color of the fake view controller displayed when there is no details .
UINavigationController * secondNavController = self . secondaryNavigationController ;
if ( secondNavController )
{
secondNavController . navigationBar . barTintColor = kRiotPrimaryBgColor ;
secondNavController . topViewController . view . backgroundColor = kRiotPrimaryBgColor ;
}
2017-03-28 16:17:14 +00:00
// on IOS 8 iPad devices , force to display the primary and the secondary viewcontroller
// to avoid empty room View Controller in portrait orientation
// else , the user cannot select a room
// shouldHideViewController delegate method is also implemented
2017-04-21 14:28:57 +00:00
if ( [ ( NSString * ) [ UIDevice currentDevice ] . model hasPrefix : @ "iPad" ] )
2015-11-17 23:15:52 +00:00
{
2017-03-28 16:17:14 +00:00
splitViewController . preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible ;
2014-10-02 15:02:47 +00:00
}
2015-11-17 23:15:52 +00:00
// Sanity check
2017-03-23 16:48:05 +00:00
NSAssert ( _masterTabBarController , @ "Something wrong in Main.storyboard" ) ;
2015-11-17 23:15:52 +00:00
_isAppForeground = NO ;
// Retrieve custom configuration
NSString * userDefaults = [ [ NSBundle mainBundle ] objectForInfoDictionaryKey : @ "UserDefaults" ] ;
NSString * defaultsPathFromApp = [ [ NSBundle mainBundle ] pathForResource : userDefaults ofType : @ "plist" ] ;
NSDictionary * defaults = [ NSDictionary dictionaryWithContentsOfFile : defaultsPathFromApp ] ;
[ [ NSUserDefaults standardUserDefaults ] registerDefaults : defaults ] ;
[ [ NSUserDefaults standardUserDefaults ] synchronize ] ;
2016-06-03 14:07:34 +00:00
// Configure Google Analytics here if the option is enabled
2017-12-22 12:19:40 +00:00
[ self startAnalytics ] ;
2017-07-14 14:41:25 +00:00
2017-10-18 09:58:24 +00:00
// Prepare Pushkit handling
2017-10-23 14:58:55 +00:00
_incomingPushEventIds = [ NSMutableDictionary dictionary ] ;
2017-10-18 09:58:24 +00:00
2016-03-23 13:45:16 +00:00
// Add matrix observers , and initialize matrix sessions if the app is not launched in background .
2015-11-17 23:15:52 +00:00
[ self initMatrixSessions ] ;
2017-12-28 13:23:12 +00:00
NSLog ( @ "[AppDelegate] didFinishLaunchingWithOptions: Done in %.0fms" , [ [ NSDate date ] timeIntervalSinceDate : startDate ] * 1000 ) ;
2014-10-02 15:02:47 +00:00
return YES ;
}
2015-06-18 09:19:42 +00:00
- ( void ) applicationWillResignActive : ( UIApplication * ) application
{
2016-02-03 11:21:45 +00:00
NSLog ( @ "[AppDelegate] applicationWillResignActive" ) ;
2014-10-02 15:02:47 +00:00
// Sent when the application is about to move from active to inactive state . This can occur for certain types of temporary interruptions ( such as an incoming phone call or SMS message ) or when the user quits the application and it begins the transition to the background state .
// Use this method to pause ongoing tasks , disable timers , and throttle down OpenGL ES frame rates . Games should use this method to pause the game .
2015-08-04 16:37:48 +00:00
// Release MatrixKit error observer
if ( matrixKitErrorObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : matrixKitErrorObserver ] ;
matrixKitErrorObserver = nil ;
}
2015-06-18 09:19:42 +00:00
if ( self . errorNotification )
{
2017-07-14 14:41:25 +00:00
[ self . errorNotification dismissViewControllerAnimated : NO completion : nil ] ;
2014-10-31 17:54:32 +00:00
self . errorNotification = nil ;
}
2015-05-28 16:31:08 +00:00
2015-06-18 09:19:42 +00:00
if ( accountPicker )
{
2017-07-14 14:41:25 +00:00
[ accountPicker dismissViewControllerAnimated : NO completion : nil ] ;
2015-05-28 16:31:08 +00:00
accountPicker = nil ;
}
2017-07-14 14:41:25 +00:00
2016-05-20 12:31:23 +00:00
if ( noCallSupportAlert )
{
2017-07-14 14:41:25 +00:00
[ noCallSupportAlert dismissViewControllerAnimated : NO completion : nil ] ;
2016-05-20 12:31:23 +00:00
noCallSupportAlert = nil ;
}
2017-07-14 14:41:25 +00:00
2016-12-16 12:43:08 +00:00
if ( cryptoDataCorruptedAlert )
{
2017-07-14 14:41:25 +00:00
[ cryptoDataCorruptedAlert dismissViewControllerAnimated : NO completion : nil ] ;
2016-12-16 12:43:08 +00:00
cryptoDataCorruptedAlert = nil ;
}
2014-12-09 17:21:39 +00:00
}
2015-06-18 09:19:42 +00:00
- ( void ) applicationDidEnterBackground : ( UIApplication * ) application
{
2016-02-03 11:21:45 +00:00
NSLog ( @ "[AppDelegate] applicationDidEnterBackground" ) ;
2014-12-09 17:21:39 +00:00
// Use this method to release shared resources , save user data , invalidate timers , and store enough application state information to restore your application to its current state in case it is terminated later .
// If your application supports background execution , this method is called instead of applicationWillTerminate : when the user quits .
2015-01-12 13:19:23 +00:00
2015-02-12 10:16:28 +00:00
// Stop reachability monitoring
2016-02-11 14:05:24 +00:00
if ( reachabilityObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : reachabilityObserver ] ;
reachabilityObserver = nil ;
}
2016-02-11 15:09:35 +00:00
[ [ AFNetworkReachabilityManager sharedManager ] setReachabilityStatusChangeBlock : nil ] ;
2015-02-12 10:16:28 +00:00
[ [ AFNetworkReachabilityManager sharedManager ] stopMonitoring ] ;
2015-04-30 14:19:12 +00:00
// check if some media must be released to reduce the cache size
2016-12-01 13:55:29 +00:00
[ MXMediaManager reduceCacheSizeToInsert : 0 ] ;
2015-04-30 14:19:12 +00:00
// Hide potential notification
2015-06-18 09:19:42 +00:00
if ( self . mxInAppNotification )
{
2017-07-14 14:41:25 +00:00
[ self . mxInAppNotification dismissViewControllerAnimated : NO completion : nil ] ;
2015-04-30 14:19:12 +00:00
self . mxInAppNotification = nil ;
}
2017-07-14 14:41:25 +00:00
2016-04-12 15:44:06 +00:00
// Discard any process on pending universal link
[ self resetPendingUniversalLink ] ;
2015-04-30 14:19:12 +00:00
// Suspend all running matrix sessions
2015-06-19 08:31:27 +00:00
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
2015-06-18 09:19:42 +00:00
for ( MXKAccount * account in mxAccounts )
{
2015-04-30 14:19:12 +00:00
[ account pauseInBackgroundTask ] ;
}
2015-01-30 08:05:15 +00:00
2016-03-03 08:15:44 +00:00
// Refresh the notifications counter
[ self refreshApplicationIconBadgeNumber ] ;
2015-10-20 08:19:21 +00:00
2015-02-02 15:43:47 +00:00
_isAppForeground = NO ;
2016-06-03 14:07:34 +00:00
// GA : End a session while the app is in background
[ [ [ GAI sharedInstance ] defaultTracker ] set : kGAISessionControl value : @ "end" ] ;
2014-10-02 15:02:47 +00:00
}
2015-06-18 09:19:42 +00:00
- ( void ) applicationWillEnterForeground : ( UIApplication * ) application
{
2016-02-03 11:21:45 +00:00
NSLog ( @ "[AppDelegate] applicationWillEnterForeground" ) ;
2017-10-18 09:58:24 +00:00
// Flush all the pending push notifications .
2017-10-23 15:34:42 +00:00
for ( NSMutableArray * array in self . incomingPushEventIds . allValues )
2017-10-23 14:58:55 +00:00
{
[ array removeAllObjects ] ;
}
2017-12-29 14:20:12 +00:00
[ incomingPushPayloads removeAllObjects ] ;
2017-10-18 09:58:24 +00:00
2014-10-02 15:02:47 +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 .
2016-02-26 11:27:22 +00:00
2015-02-02 15:43:47 +00:00
_isAppForeground = YES ;
2016-06-03 14:07:34 +00:00
// GA : Start a new session . The next hit from this tracker will be the first in a new session .
[ [ [ GAI sharedInstance ] defaultTracker ] set : kGAISessionControl value : @ "start" ] ;
2014-10-02 15:02:47 +00:00
}
2015-06-18 09:19:42 +00:00
- ( void ) applicationDidBecomeActive : ( UIApplication * ) application
{
2016-02-03 11:21:45 +00:00
NSLog ( @ "[AppDelegate] applicationDidBecomeActive" ) ;
2016-04-06 09:28:12 +00:00
2016-08-01 12:21:58 +00:00
// Check if there is crash log to send
if ( [ [ NSUserDefaults standardUserDefaults ] boolForKey : @ "enableCrashReport" ] )
{
[ 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 .
2015-02-12 10:16:28 +00:00
// Start monitoring reachability
2016-02-11 15:09:35 +00:00
[ [ AFNetworkReachabilityManager sharedManager ] setReachabilityStatusChangeBlock : ^ ( AFNetworkReachabilityStatus status ) {
2016-02-11 14:05:24 +00:00
2016-02-11 15:09:35 +00:00
// Check whether monitoring is ready
if ( status ! = AFNetworkReachabilityStatusUnknown )
2016-02-11 14:05:24 +00:00
{
2016-02-11 15:09:35 +00:00
if ( status = = AFNetworkReachabilityStatusNotReachable )
2016-02-11 14:05:24 +00:00
{
2016-02-11 15:09:35 +00:00
// Prompt user
2016-02-11 14:05:24 +00:00
[ [ AppDelegate theDelegate ] showErrorAsAlert : [ NSError errorWithDomain : NSURLErrorDomain code : NSURLErrorNotConnectedToInternet userInfo : @ { NSLocalizedDescriptionKey : NSLocalizedStringFromTable ( @ "network_offline_prompt" , @ "Vector" , nil ) } ] ] ;
}
else
{
self . isOffline = NO ;
}
2017-07-14 14:41:25 +00:00
2016-08-01 15:07:39 +00:00
// Use a dispatch to avoid to kill ourselves
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ [ AFNetworkReachabilityManager sharedManager ] setReachabilityStatusChangeBlock : nil ] ;
} ) ;
2016-02-11 14:05:24 +00:00
}
} ] ;
2015-02-12 10:16:28 +00:00
[ [ AFNetworkReachabilityManager sharedManager ] startMonitoring ] ;
2015-08-04 16:37:48 +00:00
// Observe matrixKit error to alert user on error
matrixKitErrorObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXKErrorNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * note ) {
[ self showErrorAsAlert : note . object ] ;
} ] ;
2017-07-14 14:41:25 +00:00
2016-12-16 12:43:08 +00:00
// Observe crypto data storage corruption
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( onSessionCryptoDidCorruptData : ) name : kMXSessionCryptoDidCorruptDataNotification object : nil ] ;
2015-08-04 16:37:48 +00:00
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
2017-01-17 22:13:30 +00:00
// Refresh local contact from the contact book .
[ self refreshLocalContacts ] ;
2016-04-05 13:31:52 +00:00
_isAppForeground = YES ;
2017-09-21 13:29:46 +00:00
2017-09-27 07:26:34 +00:00
if ( @ available ( iOS 11.0 , * ) )
2017-09-21 13:29:46 +00:00
{
// Riot has its own dark theme . Prevent iOS from applying its one
2017-11-24 16:30:55 +00:00
[ application keyWindow ] . accessibilityIgnoresInvertColors = YES ;
2017-09-21 13:29:46 +00:00
}
2017-01-31 23:18:01 +00:00
[ self handleLaunchAnimation ] ;
2014-10-02 15:02:47 +00:00
}
2015-06-18 09:19:42 +00:00
- ( void ) applicationWillTerminate : ( UIApplication * ) application
{
2016-02-03 11:21:45 +00:00
NSLog ( @ "[AppDelegate] applicationWillTerminate" ) ;
2014-10-02 15:02:47 +00:00
// Called when the application is about to terminate . Save data if appropriate . See also applicationDidEnterBackground : .
2016-06-03 14:07:34 +00:00
2017-12-22 12:19:40 +00:00
[ self stopAnalytics ] ;
2014-10-02 15:02:47 +00:00
}
2017-10-05 07:36:40 +00:00
- ( void ) applicationDidReceiveMemoryWarning : ( UIApplication * ) application
{
NSLog ( @ "[AppDelegate] applicationDidReceiveMemoryWarning" ) ;
}
2016-04-06 16:05:26 +00:00
- ( BOOL ) application : ( UIApplication * ) application continueUserActivity : ( NSUserActivity * ) userActivity restorationHandler : ( void ( ^ ) ( NSArray * _Nullable ) ) restorationHandler
{
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 ;
NSNumber * backgroundTaskIdentifier ;
// Start background task since we need time for MXSession preparasion because our app can be launched in the background
if ( application . applicationState = = UIApplicationStateBackground )
backgroundTaskIdentifier = @ ( [ application beginBackgroundTaskWithExpirationHandler : ^ { } ] ) ;
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 )
{
[ application endBackgroundTask : backgroundTaskIdentifier . unsignedIntegerValue ] ;
[ center removeObserver : token ] ;
}
} ] ;
}
}
failure : ^ ( NSError * error ) {
if ( backgroundTaskIdentifier )
[ application endBackgroundTask : backgroundTaskIdentifier . unsignedIntegerValue ] ;
} ] ;
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
2016-04-05 16:05:50 +00:00
- ( void ) restoreInitialDisplay : ( void ( ^ ) ( ) ) completion
2015-11-17 23:15:52 +00:00
{
2016-06-16 14:50:41 +00:00
// Suspend error notifications during navigation stack change .
isErrorNotificationSuspended = YES ;
2016-05-23 14:01:11 +00:00
// Dismiss potential view controllers that were presented modally ( like the media picker ) .
2016-04-05 16:05:50 +00:00
if ( self . window . rootViewController . presentedViewController )
2015-11-17 23:15:52 +00:00
{
2016-04-05 16:05:50 +00:00
// Do it asynchronously to avoid hasardous dispatch_async after calling restoreInitialDisplay
[ self . window . rootViewController dismissViewControllerAnimated : NO completion : ^ {
2016-05-23 14:01:11 +00:00
[ self popToHomeViewControllerAnimated : NO completion : ^ {
if ( completion )
{
completion ( ) ;
}
2016-09-06 15:20:16 +00:00
// Enable error notifications
isErrorNotificationSuspended = NO ;
2017-08-27 13:43:42 +00:00
if ( noCallSupportAlert )
2016-05-23 14:01:11 +00:00
{
NSLog ( @ "[AppDelegate] restoreInitialDisplay: keep visible noCall support alert" ) ;
2016-09-06 15:20:16 +00:00
[ self showNotificationAlert : noCallSupportAlert ] ;
2016-05-23 14:01:11 +00:00
}
2016-12-16 12:43:08 +00:00
else if ( cryptoDataCorruptedAlert )
{
NSLog ( @ "[AppDelegate] restoreInitialDisplay: keep visible log in again" ) ;
[ self showNotificationAlert : cryptoDataCorruptedAlert ] ;
}
2016-09-06 15:20:16 +00:00
// Check whether an error notification is pending
else if ( _errorNotification )
2016-06-16 14:50:41 +00:00
{
2016-09-06 15:20:16 +00:00
[ self showNotificationAlert : _errorNotification ] ;
2016-06-16 14:50:41 +00:00
}
2016-05-23 14:01:11 +00:00
} ] ;
2016-05-23 13:39:48 +00:00
2016-04-05 16:05:50 +00:00
} ] ;
}
else
{
2016-06-16 14:50:41 +00:00
[ self popToHomeViewControllerAnimated : NO completion : ^ {
if ( completion )
{
completion ( ) ;
}
// Enable error notification ( Check whether a notification is pending )
isErrorNotificationSuspended = NO ;
2016-09-06 15:20:16 +00:00
if ( _errorNotification )
2016-06-16 14:50:41 +00:00
{
2016-09-06 15:20:16 +00:00
[ self showNotificationAlert : _errorNotification ] ;
2016-06-16 14:50:41 +00:00
}
} ] ;
2015-11-17 23:15:52 +00:00
}
}
2017-12-21 16:19:36 +00:00
- ( void ) restoreEmptyDetailsViewController
{
UIViewController * rootViewController = self . window . rootViewController ;
if ( [ rootViewController isKindOfClass : [ UISplitViewController class ] ] )
{
UISplitViewController * splitViewController = ( UISplitViewController * ) rootViewController ;
// Be sure that the primary is then visible too .
if ( splitViewController . displayMode = = UISplitViewControllerDisplayModePrimaryHidden )
{
splitViewController . preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible ;
}
if ( splitViewController . viewControllers . count = = 2 )
{
UIViewController * mainViewController = splitViewController . viewControllers [ 0 ] ;
UIStoryboard * storyboard = [ UIStoryboard storyboardWithName : @ "Main" bundle : [ NSBundle mainBundle ] ] ;
UIViewController * emptyDetailsViewController = [ storyboard instantiateViewControllerWithIdentifier : @ "EmptyDetailsViewControllerStoryboardId" ] ;
emptyDetailsViewController . view . backgroundColor = kRiotPrimaryBgColor ;
splitViewController . viewControllers = @ [ mainViewController , emptyDetailsViewController ] ;
}
}
// Release the current selected item ( room / contact / group . . . ) .
[ _masterTabBarController releaseSelectedItem ] ;
}
2015-11-17 23:15:52 +00:00
2017-07-14 14:41:25 +00:00
- ( UIAlertController * ) showErrorAsAlert : ( NSError * ) error
2015-11-17 23:15:52 +00:00
{
// Ignore fake error , or connection cancellation error
if ( ! error || ( [ error . domain isEqualToString : NSURLErrorDomain ] && error . code = = NSURLErrorCancelled ) )
{
return nil ;
}
// Ignore network reachability error when the app is already offline
if ( self . isOffline && [ error . domain isEqualToString : NSURLErrorDomain ] && error . code = = NSURLErrorNotConnectedToInternet )
{
return nil ;
}
2017-07-14 14:41:25 +00:00
[ _errorNotification dismissViewControllerAnimated : NO completion : nil ] ;
2015-11-17 23:15:52 +00:00
NSString * title = [ error . userInfo valueForKey : NSLocalizedFailureReasonErrorKey ] ;
2016-02-11 13:06:48 +00:00
NSString * msg = [ error . userInfo valueForKey : NSLocalizedDescriptionKey ] ;
2015-11-17 23:15:52 +00:00
if ( ! title )
{
2016-02-11 13:06:48 +00:00
if ( msg )
{
title = msg ;
msg = nil ;
}
else
{
title = [ NSBundle mxk_localizedStringForKey : @ "error" ] ;
}
2015-11-17 23:15:52 +00:00
}
2017-07-14 14:41:25 +00:00
_errorNotification = [ UIAlertController alertControllerWithTitle : title message : msg preferredStyle : UIAlertControllerStyleAlert ] ;
[ _errorNotification addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "ok" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
[ AppDelegate theDelegate ] . errorNotification = nil ;
} ] ] ;
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 ] ;
}
2015-11-17 23:15:52 +00:00
// Switch in offline mode in case of network reachability error
if ( [ error . domain isEqualToString : NSURLErrorDomain ] && error . code = = NSURLErrorNotConnectedToInternet )
{
self . isOffline = YES ;
}
return self . errorNotification ;
}
2017-07-14 14:41:25 +00:00
- ( void ) showNotificationAlert : ( UIAlertController * ) alert
2016-06-16 14:50:41 +00:00
{
2016-09-06 15:20:16 +00:00
if ( self . window . rootViewController . presentedViewController )
2016-06-16 14:50:41 +00:00
{
2017-07-14 14:41:25 +00:00
[ alert popoverPresentationController ] . sourceView = self . window . rootViewController . presentedViewController . view ;
[ alert popoverPresentationController ] . sourceRect = self . window . rootViewController . presentedViewController . view . bounds ;
[ self . window . rootViewController . presentedViewController presentViewController : alert animated : YES completion : nil ] ;
2016-09-06 15:20:16 +00:00
}
else
{
2017-07-14 14:41:25 +00:00
[ alert popoverPresentationController ] . sourceView = self . window . rootViewController . view ;
[ alert popoverPresentationController ] . sourceRect = self . window . rootViewController . view . bounds ;
[ self . window . rootViewController presentViewController : alert animated : YES completion : nil ] ;
2016-06-16 14:50:41 +00:00
}
}
2016-12-16 12:43:08 +00:00
- ( void ) onSessionCryptoDidCorruptData : ( NSNotification * ) notification
{
NSString * userId = notification . object ;
2017-07-14 14:41:25 +00:00
2016-12-16 12:43:08 +00:00
MXKAccount * account = [ [ MXKAccountManager sharedManager ] accountForUserId : userId ] ;
if ( account )
{
if ( cryptoDataCorruptedAlert )
{
2017-07-14 14:41:25 +00:00
[ cryptoDataCorruptedAlert dismissViewControllerAnimated : NO completion : nil ] ;
2016-12-16 12:43:08 +00:00
}
2017-07-14 14:41:25 +00:00
cryptoDataCorruptedAlert = [ UIAlertController alertControllerWithTitle : nil
message : NSLocalizedStringFromTable ( @ "e2e_need_log_in_again" , @ "Vector" , nil )
preferredStyle : UIAlertControllerStyleAlert ] ;
2016-12-16 12:43:08 +00:00
__weak typeof ( self ) weakSelf = self ;
2017-07-14 14:41:25 +00:00
[ cryptoDataCorruptedAlert addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "later" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> cryptoDataCorruptedAlert = nil ;
}
} ] ] ;
[ cryptoDataCorruptedAlert addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "settings_sign_out" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> cryptoDataCorruptedAlert = nil ;
[ [ MXKAccountManager sharedManager ] removeAccount : account completion : nil ] ;
}
} ] ] ;
2016-12-16 12:43:08 +00:00
[ self showNotificationAlert : cryptoDataCorruptedAlert ] ;
}
}
2016-04-05 16:05:50 +00:00
# pragma mark
2016-04-13 15:08:02 +00:00
- ( void ) popToHomeViewControllerAnimated : ( BOOL ) animated completion : ( void ( ^ ) ( ) ) completion
2016-04-05 16:05:50 +00:00
{
2017-03-14 16:25:46 +00:00
UINavigationController * secondNavController = self . secondaryNavigationController ;
if ( secondNavController )
{
[ secondNavController popToRootViewControllerAnimated : animated ] ;
}
2017-07-14 14:41:25 +00:00
2017-03-23 16:48:05 +00:00
// Force back to the main screen if this is not the one that is displayed
if ( _masterTabBarController && _masterTabBarController ! = _masterNavigationController . visibleViewController )
2016-04-05 16:05:50 +00:00
{
2017-03-23 16:48:05 +00:00
// Listen to the masterNavigationController changes
// We need to be sure that masterTabBarController is back to the screen
2016-04-13 15:08:02 +00:00
popToHomeViewControllerCompletion = completion ;
2017-03-23 16:48:05 +00:00
_masterNavigationController . delegate = self ;
2017-07-14 14:41:25 +00:00
2017-03-23 16:48:05 +00:00
[ _masterNavigationController popToViewController : _masterTabBarController animated : animated ] ;
2016-04-05 16:05:50 +00:00
}
2016-04-13 15:08:02 +00:00
else
{
2017-03-23 16:48:05 +00:00
// Select the Home tab
_masterTabBarController . selectedIndex = TABBAR_HOME _INDEX ;
2016-06-16 14:50:41 +00:00
if ( completion )
{
completion ( ) ;
}
2016-04-13 15:08:02 +00:00
}
}
# pragma mark - UINavigationController delegate
- ( void ) navigationController : ( UINavigationController * ) navigationController didShowViewController : ( UIViewController * ) viewController animated : ( BOOL ) animated
{
2017-03-23 16:48:05 +00:00
if ( viewController = = _masterTabBarController )
2016-04-13 15:08:02 +00:00
{
2017-03-23 16:48:05 +00:00
_masterNavigationController . delegate = nil ;
2016-06-10 14:11:09 +00:00
// For unknown reason , the navigation bar is not restored correctly by [ popToViewController : animated : ]
// when a ViewController has hidden it ( see MXKAttachmentsViewController ) .
// Patch : restore navigation bar by default here .
2017-03-23 16:48:05 +00:00
_masterNavigationController . navigationBarHidden = NO ;
2016-06-10 14:11:09 +00:00
2017-03-28 15:26:50 +00:00
// Release the current selected item ( room / contact /. . . ) .
[ _masterTabBarController releaseSelectedItem ] ;
2017-07-14 14:41:25 +00:00
2016-04-13 15:08:02 +00:00
if ( popToHomeViewControllerCompletion )
{
2016-04-19 07:14:32 +00:00
void ( ^ popToHomeViewControllerCompletion2 ) ( ) = popToHomeViewControllerCompletion ;
popToHomeViewControllerCompletion = nil ;
2017-07-14 14:41:25 +00:00
2016-04-19 07:14:32 +00:00
// Dispatch the completion in order to let navigation stack refresh itself .
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
popToHomeViewControllerCompletion2 ( ) ;
} ) ;
2016-04-13 15:08:02 +00:00
}
}
2016-04-05 16:05:50 +00:00
}
2017-12-22 12:19:40 +00:00
# pragma mark - Analytics
2016-06-03 14:07:34 +00:00
2017-12-22 12:19:40 +00:00
- ( void ) startAnalytics
2016-06-03 14:07:34 +00:00
{
// Check whether the user has enabled the sending of crash reports .
if ( [ [ NSUserDefaults standardUserDefaults ] boolForKey : @ "enableCrashReport" ] )
{
2016-06-03 14:38:02 +00:00
// Retrieve trackerId from GoogleService - Info . plist .
NSString * googleServiceInfoPath = [ [ NSBundle mainBundle ] pathForResource : @ "GoogleService-Info" ofType : @ "plist" ] ;
NSDictionary * googleServiceInfo = [ NSDictionary dictionaryWithContentsOfFile : googleServiceInfoPath ] ;
NSString * gaTrackingID = [ googleServiceInfo objectForKey : @ "TRACKING_ID" ] ;
if ( gaTrackingID )
{
// Catch and log crashes
[ MXLogger logCrashes : YES ] ;
[ MXLogger setBuildVersion : [ AppDelegate theDelegate ] . build ] ;
// Configure GAI options .
GAI * gai = [ GAI sharedInstance ] ;
// Disable GA UncaughtException : their crash reports are quite limited ( 100 first chars of the stack trace )
// Let ' s MXLogger manage them
gai . trackUncaughtExceptions = NO ;
// Initialize it with the app tracker ID
[ gai trackerWithTrackingId : gaTrackingID ] ;
// Set Google Analytics dispatch interval to e . g . 20 seconds .
gai . dispatchInterval = 20 ;
2017-07-14 14:41:25 +00:00
2017-06-30 11:37:36 +00:00
# ifdef DEBUG
// Disable GAI in debug as it pollutes stats and crashes in GA
gai . dryRun = YES ;
# endif
2016-06-03 14:38:02 +00:00
}
else
{
NSLog ( @ "[AppDelegate] Unable to find tracker id for Google Analytics" ) ;
}
2016-06-03 14:07:34 +00:00
}
else if ( [ [ NSUserDefaults standardUserDefaults ] objectForKey : @ "enableCrashReport" ] )
{
NSLog ( @ "[AppDelegate] The user decides to do not use Google Analytics" ) ;
}
}
2017-12-22 12:19:40 +00:00
- ( void ) stopAnalytics
2016-06-04 15:37:23 +00:00
{
GAI * gai = [ GAI sharedInstance ] ;
// End a session . The next hit from this tracker will be the last in the current session .
[ [ gai defaultTracker ] set : kGAISessionControl value : @ "end" ] ;
// Flush pending GA messages
[ gai dispatch ] ;
[ gai removeTrackerByName : [ gai defaultTracker ] . name ] ;
[ MXLogger logCrashes : NO ] ;
}
2017-12-22 12:19:40 +00:00
- ( void ) trackScreen : ( NSString * ) screenName
{
// Screen tracking ( via Google Analytics )
id < GAITracker > tracker = [ [ GAI sharedInstance ] defaultTracker ] ;
if ( tracker )
{
[ tracker set : kGAIScreenName value : screenName ] ;
[ tracker send : [ [ GAIDictionaryBuilder createScreenView ] build ] ] ;
}
}
2016-06-03 14:07:34 +00:00
// Check if there is a crash log to send to server
- ( void ) checkExceptionToReport
{
// Check if the app crashed last time
NSString * filePath = [ MXLogger crashLog ] ;
if ( filePath )
{
2017-06-29 15:33:08 +00:00
// Do not show the crash report dialog if it is already displayed
2017-06-29 15:45:54 +00:00
if ( [ self . window . rootViewController . childViewControllers [ 0 ] isKindOfClass : [ UINavigationController class ] ]
2017-07-14 14:41:25 +00:00
&& [ ( ( UINavigationController * ) self . window . rootViewController . childViewControllers [ 0 ] ) . visibleViewController isKindOfClass : [ BugReportViewController class ] ] )
2017-06-29 15:33:08 +00:00
{
return ;
}
2017-07-14 14:41:25 +00:00
2016-06-03 14:07:34 +00:00
NSString * description = [ [ NSString alloc ] initWithContentsOfFile : filePath
usedEncoding : nil
error : nil ] ;
NSLog ( @ "[AppDelegate] Send crash log to Google Analytics:\n%@" , description ) ;
// Send it via Google Analytics
// The doc says the exception description must not exceeed 100 chars but it seems
// to accept much more .
// https : // developers . google . com / analytics / devguides / collection / ios / v3 / exceptions # overview
2016-06-03 15:29:34 +00:00
id < GAITracker > tracker = [ [ GAI sharedInstance ] defaultTracker ] ;
2016-06-03 14:07:34 +00:00
[ tracker send : [ [ GAIDictionaryBuilder
createExceptionWithDescription : description
withFatal : [ NSNumber numberWithBool : YES ] ] build ] ] ;
[ [ GAI sharedInstance ] dispatch ] ;
2017-07-14 14:41:25 +00:00
2016-08-01 12:21:58 +00:00
// Ask the user to send a crash report by email too
// The email will provide logs and thus more context to the crash
[ [ RageShakeManager sharedManager ] promptCrashReportInViewController : self . window . rootViewController ] ;
2016-06-03 14:07:34 +00:00
}
}
2017-09-19 14:06:44 +00:00
# pragma mark - Push notifications
2014-12-12 09:49:15 +00:00
2015-06-18 09:19:42 +00:00
- ( void ) registerUserNotificationSettings
{
2017-09-19 14:06:44 +00:00
if ( ! isPushRegistered )
2015-06-18 09:19:42 +00:00
{
2015-12-21 09:35:48 +00:00
// Registration on iOS 8 and later
UIUserNotificationSettings * settings = [ UIUserNotificationSettings settingsForTypes : ( UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert ) categories : nil ] ;
[ [ UIApplication sharedApplication ] registerUserNotificationSettings : settings ] ;
2014-12-18 15:29:34 +00:00
}
2014-12-12 09:49:15 +00:00
}
2017-08-23 16:07:14 +00:00
- ( void ) registerForRemoteNotificationsWithCompletion : ( nullable void ( ^ ) ( NSError * ) ) completion
{
self . registrationForRemoteNotificationsCompletion = completion ;
2017-08-21 10:28:05 +00:00
self . pushRegistry = [ [ PKPushRegistry alloc ] initWithQueue : nil ] ;
self . pushRegistry . delegate = self ;
self . pushRegistry . desiredPushTypes = [ NSSet setWithObject : PKPushTypeVoIP ] ;
2014-12-12 09:49:15 +00:00
}
2017-09-10 13:41:50 +00:00
- ( void ) application : ( UIApplication * ) application didRegisterUserNotificationSettings : ( UIUserNotificationSettings * ) notificationSettings
{
// Register for remote notifications only if user provide access to notification feature
if ( notificationSettings . types ! = UIUserNotificationTypeNone )
{
[ self registerForRemoteNotificationsWithCompletion : nil ] ;
}
else
{
// Clear existing token
MXKAccountManager * accountManager = [ MXKAccountManager sharedManager ] ;
2017-09-19 14:06:44 +00:00
[ accountManager setPushDeviceToken : nil withPushOptions : nil ] ;
2017-09-10 13:41:50 +00:00
}
}
2017-08-21 10:28:05 +00:00
- ( void ) application : ( UIApplication * ) application didReceiveLocalNotification : ( UILocalNotification * ) notification
2015-10-20 08:19:21 +00:00
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] didReceiveLocalNotification: applicationState: %@" , @ ( application . applicationState ) ) ;
2014-12-12 09:49:15 +00:00
2017-08-21 10:28:05 +00:00
NSString * roomId = notification . userInfo [ @ "room_id" ] ;
2015-10-20 08:19:21 +00:00
if ( roomId . length )
2015-06-18 09:19:42 +00:00
{
2015-10-20 08:19:21 +00:00
// TODO retrieve the right matrix session
2017-12-29 12:04:57 +00:00
// We can use the "user_id" value in notification . userInfo
2015-10-20 08:19:21 +00:00
// * * * * * * * * * * * * * *
// Patch consider the first session which knows the room id
MXKAccount * dedicatedAccount = nil ;
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
if ( mxAccounts . count = = 1 )
2015-06-18 09:19:42 +00:00
{
2015-10-20 08:19:21 +00:00
dedicatedAccount = mxAccounts . firstObject ;
}
else
{
for ( MXKAccount * account in mxAccounts )
{
if ( [ account . mxSession roomWithRoomId : roomId ] )
{
dedicatedAccount = account ;
break ;
}
}
}
// sanity checks
if ( dedicatedAccount && dedicatedAccount . mxSession )
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] didReceiveLocalNotification: open the roomViewController %@" , roomId ) ;
2015-05-28 17:20:18 +00:00
2017-08-21 10:28:05 +00:00
[ self showRoom : roomId andEventId : nil withMatrixSession : dedicatedAccount . mxSession ] ;
2015-10-20 08:19:21 +00:00
}
else
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] didReceiveLocalNotification : no linked session / account has been found." ) ;
2015-02-19 17:23:24 +00:00
}
2015-02-13 15:16:27 +00:00
}
2014-12-12 09:49:15 +00:00
}
2017-08-21 10:28:05 +00:00
- ( void ) pushRegistry : ( PKPushRegistry * ) registry didUpdatePushCredentials : ( PKPushCredentials * ) credentials forType : ( PKPushType ) type
2016-09-12 08:33:10 +00:00
{
2017-08-21 10:28:05 +00:00
NSData * token = credentials . token ;
NSUInteger len = ( ( token . length > 8 ) ? 8 : token . length / 2 ) ;
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] Got Push token! (%@ ...)" , [ token subdataWithRange : NSMakeRange ( 0 , len ) ] ) ;
2017-08-21 10:28:05 +00:00
MXKAccountManager * accountManager = [ MXKAccountManager sharedManager ] ;
2017-09-19 14:06:44 +00:00
[ accountManager setPushDeviceToken : token withPushOptions : @ { @ "format" : @ "event_id_only" } ] ;
2017-08-21 10:28:05 +00:00
2017-09-19 14:06:44 +00:00
isPushRegistered = YES ;
2017-09-10 13:41:50 +00:00
if ( self . registrationForRemoteNotificationsCompletion )
{
self . registrationForRemoteNotificationsCompletion ( nil ) ;
self . registrationForRemoteNotificationsCompletion = nil ;
}
2017-08-21 10:28:05 +00:00
}
- ( void ) pushRegistry : ( PKPushRegistry * ) registry didInvalidatePushTokenForType : ( PKPushType ) type
{
MXKAccountManager * accountManager = [ MXKAccountManager sharedManager ] ;
2017-09-19 14:06:44 +00:00
[ accountManager setPushDeviceToken : nil withPushOptions : nil ] ;
2017-08-21 10:28:05 +00:00
}
- ( void ) pushRegistry : ( PKPushRegistry * ) registry didReceiveIncomingPushWithPayload : ( PKPushPayload * ) payload forType : ( PKPushType ) type
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] didReceiveIncomingPushWithPayload: applicationState: %tu - type: %@ - payload: %@" , [ UIApplication sharedApplication ] . applicationState , payload . type , payload . dictionaryPayload ) ;
2017-10-18 09:58:24 +00:00
// Display local notifications only when the app is running in background .
if ( [ UIApplication sharedApplication ] . applicationState = = UIApplicationStateBackground )
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] didReceiveIncomingPushWithPayload while app is in background" ) ;
2017-10-18 09:58:24 +00:00
2017-10-18 17:18:14 +00:00
// Check whether an event id is provided .
NSString * eventId = payload . dictionaryPayload [ @ "event_id" ] ;
if ( eventId )
2017-10-18 12:10:08 +00:00
{
2017-10-23 14:58:55 +00:00
// Add this event identifier in the pending push array for each session .
2017-10-23 15:34:42 +00:00
for ( NSMutableArray * array in self . incomingPushEventIds . allValues )
2017-10-23 14:58:55 +00:00
{
[ array addObject : eventId ] ;
}
2017-12-29 12:04:57 +00:00
// Cache payload for further usage
incomingPushPayloads [ eventId ] = payload . dictionaryPayload ;
2017-10-18 12:10:08 +00:00
}
else
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] didReceiveIncomingPushWithPayload - Unexpected payload %@" , payload . dictionaryPayload ) ;
2017-10-18 12:10:08 +00:00
}
2017-10-18 17:18:14 +00:00
2017-10-19 14:09:50 +00:00
// Trigger a background sync to handle notifications .
[ self launchBackgroundSync ] ;
2017-10-18 09:58:24 +00:00
}
2017-09-06 14:59:12 +00:00
}
2017-10-19 14:09:50 +00:00
- ( void ) launchBackgroundSync
2017-09-06 14:59:12 +00:00
{
// Launch a background sync for all existing matrix sessions
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
for ( MXKAccount * account in mxAccounts )
{
// Check the current session state
2017-10-18 09:58:24 +00:00
if ( account . mxSession . state = = MXSessionStatePaused )
2017-08-21 10:28:05 +00:00
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] launchBackgroundSync" ) ;
2017-10-18 09:58:24 +00:00
__weak typeof ( self ) weakSelf = self ;
2017-12-29 12:33:55 +00:00
NSMutableArray < NSString * > * incomingPushEventIds = self . incomingPushEventIds [ @ ( account . mxSession . hash ) ] ;
NSMutableArray < NSString * > * incomingPushEventIdsCopy = [ incomingPushEventIds copy ] ;
2017-09-06 14:59:12 +00:00
2017-10-23 14:58:55 +00:00
// Flush all the pending push notifications for this session .
2017-12-29 12:33:55 +00:00
[ incomingPushEventIds removeAllObjects ] ;
2017-10-23 14:58:55 +00:00
2017-10-18 09:58:24 +00:00
[ account backgroundSync : 20000 success : ^ {
// Sanity check
if ( ! weakSelf )
{
return ;
}
typeof ( self ) self = weakSelf ;
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] launchBackgroundSync: the background sync succeeds" ) ;
2017-10-18 09:58:24 +00:00
2017-10-19 14:09:50 +00:00
// Trigger local notifcations
[ self handleLocalNotificationsForAccount : account ] ;
2017-10-18 09:58:24 +00:00
2017-10-19 14:09:50 +00:00
// Update app icon badge number
[ self refreshApplicationIconBadgeNumber ] ;
2017-10-18 17:18:14 +00:00
2017-10-19 14:09:50 +00:00
} failure : ^ ( NSError * error ) {
2017-12-29 12:33:55 +00:00
NSLog ( @ "[AppDelegate][Push] launchBackgroundSync: the background sync failed. Error: %@ (%@). incomingPushEventIdsCopy: %@ - self.incomingPushEventIds: %@" , error . domain , @ ( error . code ) , incomingPushEventIdsCopy , incomingPushEventIds ) ;
2017-12-29 12:40:55 +00:00
// Trigger limited local notifications when the sync with HS fails
2017-12-29 12:33:55 +00:00
[ self handleLimitedLocalNotifications : account . mxSession events : incomingPushEventIdsCopy ] ;
// Update app icon badge number
[ self refreshApplicationIconBadgeNumber ] ;
2017-12-29 09:31:16 +00:00
2017-10-19 14:09:50 +00:00
} ] ;
}
}
}
- ( void ) handleLocalNotificationsForAccount : ( MXKAccount * ) account
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: %@" , account . mxCredentials . userId ) ;
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: eventsToNotify: %@" , eventsToNotify [ @ ( account . mxSession . hash ) ] ) ;
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: incomingPushEventIds: %@" , self . incomingPushEventIds [ @ ( account . mxSession . hash ) ] ) ;
2017-12-15 08:26:44 +00:00
NSUInteger scheduledNotifications = 0 ;
2017-10-19 14:09:50 +00:00
// The call invite are handled here only when the callkit is not active .
BOOL isCallKitActive = [ MXCallKitAdapter callKitAvailable ] && [ MXKAppSettings standardAppSettings ] . isCallKitEnabled ;
NSMutableArray * eventsArray = eventsToNotify [ @ ( account . mxSession . hash ) ] ;
// Display a local notification for each event retrieved by the bg sync .
for ( NSUInteger index = 0 ; index < eventsArray . count ; index + + )
{
NSDictionary * eventDict = eventsArray [ index ] ;
NSString * eventId = eventDict [ @ "event_id" ] ;
NSString * roomId = eventDict [ @ "room_id" ] ;
BOOL checkReadEvent = YES ;
MXEvent * event ;
2017-12-29 12:04:57 +00:00
// Ignore event already notified to the user
2017-12-29 15:43:20 +00:00
if ( [ self displayedLocalNotificationForEvent : eventId andUser : account . mxCredentials . userId type : nil ] )
2017-12-29 12:04:57 +00:00
{
2017-12-29 15:43:20 +00:00
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@" , eventId ) ;
2017-12-29 12:04:57 +00:00
continue ;
}
2017-10-19 14:09:50 +00:00
if ( eventId && roomId )
{
event = [ account . mxSession . store eventWithEventId : eventId inRoom : roomId ] ;
}
if ( event )
{
// Ignore redacted event .
if ( event . isRedactedEvent )
{
2017-12-29 09:31:16 +00:00
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: Skip redacted event. Event id: %@" , event . eventId ) ;
2017-10-19 14:09:50 +00:00
continue ;
}
// Consider here the call invites
if ( event . eventType = = MXEventTypeCallInvite )
{
// Ignore call invite when callkit is active .
if ( isCallKitActive )
{
2017-12-29 09:31:16 +00:00
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: Skip call event. Event id: %@" , event . eventId ) ;
2017-10-19 14:09:50 +00:00
continue ;
}
else
2017-10-18 09:58:24 +00:00
{
2017-10-19 14:09:50 +00:00
// Retrieve the current call state from the call manager
MXCallInviteEventContent * callInviteEventContent = [ MXCallInviteEventContent modelFromJSON : event . content ] ;
MXCall * call = [ account . mxSession . callManager callWithCallId : callInviteEventContent . callId ] ;
2017-10-18 09:58:24 +00:00
2017-10-19 14:09:50 +00:00
if ( call . state <= MXCallStateRinging )
2017-10-18 09:58:24 +00:00
{
2017-10-19 14:09:50 +00:00
// Keep display a local notification even if the event has been read on another device .
checkReadEvent = NO ;
2017-10-18 09:58:24 +00:00
}
2017-10-19 14:09:50 +00:00
}
}
if ( checkReadEvent )
{
// Ignore event which has been read on another device .
MXReceiptData * readReceipt = [ account . mxSession . store getReceiptInRoom : roomId forUserId : account . mxCredentials . userId ] ;
if ( readReceipt )
{
MXEvent * readReceiptEvent = [ account . mxSession . store eventWithEventId : readReceipt . eventId inRoom : roomId ] ;
if ( event . originServerTs <= readReceiptEvent . originServerTs )
2017-10-18 09:58:24 +00:00
{
2017-12-29 09:31:16 +00:00
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: Skip already read event. Event id: %@" , event . eventId ) ;
2017-10-19 14:09:50 +00:00
continue ;
}
}
}
// Prepare the local notification
MXPushRule * rule = eventDict [ @ "push_rule" ] ;
NSString * notificationBody = [ self notificationBodyForEvent : event pushRule : rule inAccount : account ] ;
if ( notificationBody )
{
2017-10-19 14:25:59 +00:00
// Printf style escape characters are stripped from the string prior to display ;
// to include a percent symbol ( % ) in the message , use two percent symbols ( % % ) .
notificationBody = [ notificationBody stringByReplacingOccurrencesOfString : @ "%" withString : @ "%%" ] ;
2017-10-19 14:09:50 +00:00
UILocalNotification * eventNotification = [ [ UILocalNotification alloc ] init ] ;
eventNotification . alertBody = notificationBody ;
2017-12-29 12:04:57 +00:00
eventNotification . userInfo = @ {
2017-12-29 15:43:20 +00:00
@ "type" : @ "full" ,
2017-12-29 12:04:57 +00:00
@ "room_id" : event . roomId ,
@ "event_id" : event . eventId ,
@ "user_id" : account . mxCredentials . userId
} ;
2017-10-19 14:09:50 +00:00
// Set sound name based on the value provided in action of MXPushRule
for ( MXPushRuleAction * action in rule . actions )
{
if ( action . actionType = = MXPushRuleActionTypeSetTweak )
{
if ( [ action . parameters [ @ "set_tweak" ] isEqualToString : @ "sound" ] )
2017-10-18 09:58:24 +00:00
{
2017-10-19 14:09:50 +00:00
NSString * soundName = action . parameters [ @ "value" ] ;
if ( [ soundName isEqualToString : @ "default" ] )
soundName = UILocalNotificationDefaultSoundName ;
2017-10-18 09:58:24 +00:00
2017-10-19 14:09:50 +00:00
eventNotification . soundName = soundName ;
2017-10-18 09:58:24 +00:00
}
}
}
2017-12-15 08:26:44 +00:00
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@" , event . eventId ) ;
2017-10-19 14:09:50 +00:00
[ [ UIApplication sharedApplication ] scheduleLocalNotification : eventNotification ] ;
2017-12-15 08:26:44 +00:00
scheduledNotifications + + ;
}
else
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated notificationBody. Event id: %@" , event . eventId ) ;
2017-10-19 14:09:50 +00:00
}
2017-10-18 09:58:24 +00:00
}
2017-09-06 14:59:12 +00:00
}
2017-12-15 08:26:44 +00:00
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: Sent %tu local notifications for %tu events" , scheduledNotifications , eventsArray . count ) ;
2017-12-15 08:26:44 +00:00
2017-10-19 14:09:50 +00:00
[ eventsArray removeAllObjects ] ;
2017-08-21 10:28:05 +00:00
}
2017-10-18 09:58:24 +00:00
- ( nullable NSString * ) notificationBodyForEvent : ( MXEvent * ) event pushRule : ( MXPushRule * ) rule inAccount : ( MXKAccount * ) account
2017-08-21 10:28:05 +00:00
{
if ( ! event . content || ! event . content . count )
2017-12-15 08:26:44 +00:00
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] notificationBodyForEvent: empty event content" ) ;
2017-08-21 10:28:05 +00:00
return nil ;
2017-12-15 08:26:44 +00:00
}
2017-08-21 10:28:05 +00:00
2017-10-18 09:58:24 +00:00
MXRoom * room = [ account . mxSession roomWithRoomId : event . roomId ] ;
MXRoomState * roomState = room . state ;
2017-08-21 10:28:05 +00:00
NSString * notificationBody ;
NSString * eventSenderName = [ roomState memberName : event . sender ] ;
if ( event . eventType = = MXEventTypeRoomMessage || event . eventType = = MXEventTypeRoomEncrypted )
{
2017-10-12 12:46:20 +00:00
if ( room . isMentionsOnly )
2017-10-12 09:42:10 +00:00
{
2017-10-12 12:46:20 +00:00
// A local notification will be displayed only for highlighted notification .
BOOL isHighlighted = NO ;
// Check whether is there an highlight tweak on it
for ( MXPushRuleAction * ruleAction in rule . actions )
{
if ( ruleAction . actionType = = MXPushRuleActionTypeSetTweak )
{
if ( [ ruleAction . parameters [ @ "set_tweak" ] isEqualToString : @ "highlight" ] )
{
// Check the highlight tweak "value"
// If not present , highlight . Else check its value before highlighting
if ( nil = = ruleAction . parameters [ @ "value" ] || YES = = [ ruleAction . parameters [ @ "value" ] boolValue ] )
{
isHighlighted = YES ;
break ;
}
}
}
}
if ( ! isHighlighted )
{
// Ignore this notif .
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] notificationBodyForEvent: Ignore non highlighted notif in mentions only room" ) ;
2017-10-12 12:46:20 +00:00
return nil ;
}
2017-10-12 09:42:10 +00:00
}
2017-08-21 10:28:05 +00:00
NSString * msgType = event . content [ @ "msgtype" ] ;
NSString * content = event . content [ @ "body" ] ;
2017-09-08 13:32:55 +00:00
if ( event . isEncrypted && ! account . showDecryptedContentInNotifications )
{
// Hide the content
msgType = nil ;
}
2017-10-16 12:43:07 +00:00
NSString * roomDisplayName = room . summary . displayname ;
// Display the room name only if it is different than the sender name
if ( roomDisplayName . length && ! [ roomDisplayName isEqualToString : eventSenderName ] )
2017-08-21 10:28:05 +00:00
{
if ( [ msgType isEqualToString : @ "m.text" ] )
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "MSG_FROM_USER_IN_ROOM_WITH_CONTENT" , nil ) , eventSenderName , roomDisplayName , content ] ;
else if ( [ msgType isEqualToString : @ "m.emote" ] )
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "ACTION_FROM_USER_IN_ROOM" , nil ) , roomDisplayName , eventSenderName , content ] ;
else if ( [ msgType isEqualToString : @ "m.image" ] )
2017-08-29 08:50:22 +00:00
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "IMAGE_FROM_USER_IN_ROOM" , nil ) , eventSenderName , content , roomDisplayName ] ;
2017-08-21 10:28:05 +00:00
else
2017-09-08 13:32:55 +00:00
// Encrypted messages falls here
2017-08-21 10:28:05 +00:00
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "MSG_FROM_USER_IN_ROOM" , nil ) , eventSenderName , roomDisplayName ] ;
}
2015-10-20 08:19:21 +00:00
else
{
2017-08-21 10:28:05 +00:00
if ( [ msgType isEqualToString : @ "m.text" ] )
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "MSG_FROM_USER_WITH_CONTENT" , nil ) , eventSenderName , content ] ;
else if ( [ msgType isEqualToString : @ "m.emote" ] )
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "ACTION_FROM_USER" , nil ) , eventSenderName , content ] ;
else if ( [ msgType isEqualToString : @ "m.image" ] )
2017-08-29 08:50:22 +00:00
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "IMAGE_FROM_USER" , nil ) , eventSenderName , content ] ;
2017-08-21 10:28:05 +00:00
else
2017-09-08 13:32:55 +00:00
// Encrypted messages falls here
2017-08-21 10:28:05 +00:00
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "MSG_FROM_USER" , nil ) , eventSenderName ] ;
2015-02-19 17:23:24 +00:00
}
2015-02-13 15:16:27 +00:00
}
2017-08-21 10:28:05 +00:00
else if ( event . eventType = = MXEventTypeCallInvite )
{
NSString * sdp = event . content [ @ "offer" ] [ @ "sdp" ] ;
BOOL isVideoCall = [ sdp rangeOfString : @ "m=video" ] . location ! = NSNotFound ;
if ( ! isVideoCall )
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "VOICE_CALL_FROM_USER" , nil ) , eventSenderName ] ;
else
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "VIDEO_CALL_FROM_USER" , nil ) , eventSenderName ] ;
}
else if ( event . eventType = = MXEventTypeRoomMember )
{
2017-10-18 17:18:14 +00:00
NSString * roomDisplayName = room . summary . displayname ;
2017-08-21 10:28:05 +00:00
2017-10-18 17:18:14 +00:00
if ( roomDisplayName . length && ! [ roomDisplayName isEqualToString : eventSenderName ] )
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "USER_INVITE_TO_NAMED_ROOM" , nil ) , eventSenderName , roomDisplayName ] ;
2017-08-21 10:28:05 +00:00
else
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "USER_INVITE_TO_CHAT" , nil ) , eventSenderName ] ;
}
2017-07-14 14:41:25 +00:00
2017-08-21 10:28:05 +00:00
return notificationBody ;
2016-09-12 08:33:10 +00:00
}
2017-12-29 12:04:57 +00:00
/ * *
2017-12-29 12:22:29 +00:00
Display "limited" notifications for events the app was not able to get data
2017-12-29 12:04:57 +00:00
( because of / sync failure ) .
2017-12-29 12:22:29 +00:00
In this situation , we are only able to display "You received a message in %@" .
2017-12-29 12:04:57 +00:00
@ param mxSession the matrix session where the / sync failed .
@ param events the list of events id we did not get data .
* /
2017-12-29 12:22:29 +00:00
- ( void ) handleLimitedLocalNotifications : ( MXSession * ) mxSession events : ( NSArray < NSString * > * ) events
2017-12-29 09:31:16 +00:00
{
NSString * userId = mxSession . matrixRestClient . credentials . userId ;
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForFailedSync: %@" , userId ) ;
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForFailedSync: eventsToNotify: %@" , eventsToNotify [ @ ( mxSession . hash ) ] ) ;
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForFailedSync: incomingPushEventIds: %@" , self . incomingPushEventIds [ @ ( mxSession . hash ) ] ) ;
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForFailedSync: events: %@" , events ) ;
2017-12-29 14:20:12 +00:00
if ( ! events . count )
2017-12-29 09:31:16 +00:00
{
return ;
}
for ( NSString * eventId in events )
{
2017-12-29 15:43:20 +00:00
// Ignore event already notified to the user
if ( [ self displayedLocalNotificationForEvent : eventId andUser : userId type : nil ] )
2017-12-29 09:31:16 +00:00
{
2017-12-29 15:43:20 +00:00
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@" , eventId ) ;
2017-12-29 09:31:16 +00:00
continue ;
}
2017-12-29 12:04:57 +00:00
// Build notification user info
NSMutableDictionary * userInfo = [ NSMutableDictionary dictionaryWithDictionary : @ {
2017-12-29 15:43:20 +00:00
@ "type" : @ "limited" ,
2017-12-29 12:04:57 +00:00
@ "event_id" : eventId ,
@ "user_id" : userId
} ] ;
// Add the room_id so that user will open the room when tapping on the notif
NSDictionary * payload = incomingPushPayloads [ eventId ] ;
NSString * roomId = payload [ @ "room_id" ] ;
if ( roomId )
{
userInfo [ @ "room_id" ] = roomId ;
}
else
{
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForFailedSync: room_id is missing for event %@ in payload %@" , eventId , payload ) ;
}
2017-12-29 09:31:16 +00:00
2017-12-29 12:04:57 +00:00
UILocalNotification * localNotificationForFailedSync = [ [ UILocalNotification alloc ] init ] ;
localNotificationForFailedSync . userInfo = userInfo ;
2017-12-29 12:22:29 +00:00
localNotificationForFailedSync . alertBody = [ self limitedNotificationBodyForEvent : eventId inMatrixSession : mxSession ] ;
2017-12-29 09:31:16 +00:00
NSLog ( @ "[AppDelegate][Push] handleLocalNotificationsForFailedSync: Display notification for event %@" , eventId ) ;
[ [ UIApplication sharedApplication ] scheduleLocalNotification : localNotificationForFailedSync ] ;
}
}
2017-12-29 12:04:57 +00:00
/ * *
2017-12-29 12:40:55 +00:00
Build the body for the "limited" notification to display to the user .
2017-12-29 12:04:57 +00:00
@ param eventId the id of the event the app failed to get data .
@ param mxSession the matrix session where the / sync failed .
@ return the string to display in the local notification .
* /
2017-12-29 12:22:29 +00:00
- ( nullable NSString * ) limitedNotificationBodyForEvent : ( NSString * ) eventId inMatrixSession : ( MXSession * ) mxSession
2017-12-29 12:04:57 +00:00
{
NSString * notificationBody ;
NSString * roomDisplayName ;
NSDictionary * payload = incomingPushPayloads [ eventId ] ;
NSString * roomId = payload [ @ "room_id" ] ;
if ( roomId )
{
MXRoomSummary * roomSummary = [ mxSession roomSummaryWithRoomId : roomId ] ;
if ( roomSummary )
{
roomDisplayName = roomSummary . displayname ;
}
}
if ( roomDisplayName . length )
{
2018-01-03 10:06:39 +00:00
notificationBody = [ NSString stringWithFormat : NSLocalizedString ( @ "SINGLE_UNREAD_IN_ROOM" , nil ) , roomDisplayName ] ;
2017-12-29 12:04:57 +00:00
}
else
{
2018-01-03 10:06:39 +00:00
notificationBody = NSLocalizedString ( @ "SINGLE_UNREAD" , nil ) ;
2017-12-29 12:04:57 +00:00
}
return notificationBody ;
}
/ * *
2017-12-29 15:43:20 +00:00
Return the already displayed notification for an event .
2017-12-29 12:04:57 +00:00
@ param eventId the id of the event attached to the notification to find .
@ param userId the id of the user attached to the notification to find .
2017-12-29 15:43:20 +00:00
@ param type the type of notification . @ "full" or @ "limited" . nil for any type .
2017-12-29 12:04:57 +00:00
@ return the local notification if any .
* /
2018-01-02 14:46:43 +00:00
// TODO : This method does not work : [ [ UIApplication sharedApplication ] scheduledLocalNotifications ] is not reliable
2017-12-29 15:43:20 +00:00
- ( UILocalNotification * ) displayedLocalNotificationForEvent : ( NSString * ) eventId andUser : ( NSString * ) userId type : ( NSString * ) type
2017-12-29 09:31:16 +00:00
{
2018-01-02 13:15:50 +00:00
NSLog ( @ "[AppDelegate] displayedLocalNotificationForEvent: %@ andUser: %@. Current scheduledLocalNotifications: %@" , eventId , userId , [ [ UIApplication sharedApplication ] scheduledLocalNotifications ] ) ;
2017-12-29 12:40:55 +00:00
UILocalNotification * limitedLocalNotification ;
2017-12-29 09:31:16 +00:00
for ( UILocalNotification * localNotification in [ [ UIApplication sharedApplication ] scheduledLocalNotifications ] )
{
2018-01-02 13:15:50 +00:00
NSLog ( @ " - %@" , localNotification . userInfo ) ;
2017-12-29 15:43:20 +00:00
if ( [ localNotification . userInfo [ @ "event_id" ] isEqualToString : eventId ]
&& [ localNotification . userInfo [ @ "user_id" ] isEqualToString : userId ]
&& ( ! type || [ localNotification . userInfo [ @ "type" ] isEqualToString : type ] ) )
2017-12-29 09:31:16 +00:00
{
2017-12-29 12:40:55 +00:00
limitedLocalNotification = localNotification ;
2017-12-29 09:31:16 +00:00
break ;
}
}
2018-01-02 13:15:50 +00:00
NSLog ( @ "[AppDelegate] displayedLocalNotificationForEvent: found: %@" , limitedLocalNotification ) ;
2017-12-29 12:40:55 +00:00
return limitedLocalNotification ;
2017-12-29 09:31:16 +00:00
}
2016-03-03 08:15:44 +00:00
- ( void ) refreshApplicationIconBadgeNumber
{
2017-04-18 13:55:51 +00:00
// Consider the total number of missed discussions including the invites .
NSUInteger count = [ self . masterTabBarController missedDiscussionsCount ] ;
2017-04-18 14:20:55 +00:00
2016-04-26 12:45:19 +00:00
NSLog ( @ "[AppDelegate] refreshApplicationIconBadgeNumber: %tu" , count ) ;
[ UIApplication sharedApplication ] . applicationIconBadgeNumber = count ;
2016-03-03 08:15:44 +00:00
}
2015-11-17 23:15:52 +00:00
2016-04-12 08:23:48 +00:00
# pragma mark - Universal link
- ( BOOL ) handleUniversalLink : ( NSUserActivity * ) userActivity
{
NSURL * webURL = userActivity . webpageURL ;
NSLog ( @ "[AppDelegate] handleUniversalLink: %@" , webURL . absoluteString ) ;
2017-07-14 14:41:25 +00:00
2016-04-20 07:21:46 +00:00
// iOS Patch : fix vector . im urls before using it
2016-04-29 14:19:13 +00:00
webURL = [ Tools fixURLWithSeveralHashKeys : webURL ] ;
2017-07-14 14:41:25 +00:00
2016-04-22 15:54:57 +00:00
// Manage email validation link
if ( [ webURL . path isEqualToString : @ "/_matrix/identity/api/v1/validate/email/submitToken" ] )
{
// Validate the email on the passed identity server
NSString * identityServer = [ NSString stringWithFormat : @ "%@://%@" , webURL . scheme , webURL . host ] ;
MXRestClient * identityRestClient = [ [ MXRestClient alloc ] initWithHomeServer : identityServer andOnUnrecognizedCertificateBlock : nil ] ;
2017-07-14 14:41:25 +00:00
2016-04-22 15:54:57 +00:00
// Extract required parameters from the link
NSArray < NSString * > * pathParams ;
NSMutableDictionary * queryParams ;
[ self parseUniversalLinkFragment : webURL . absoluteString outPathParams : & pathParams outQueryParams : & queryParams ] ;
2017-07-14 14:41:25 +00:00
2017-02-20 17:34:41 +00:00
[ identityRestClient submit3PIDValidationToken : queryParams [ @ "token" ] medium : kMX3PIDMediumEmail clientSecret : queryParams [ @ "client_secret" ] sid : queryParams [ @ "sid" ] success : ^ {
2017-07-14 14:41:25 +00:00
2016-04-22 15:54:57 +00:00
NSLog ( @ "[AppDelegate] handleUniversalLink. Email successfully validated." ) ;
2017-07-14 14:41:25 +00:00
2016-04-22 15:54:57 +00:00
if ( queryParams [ @ "nextLink" ] )
{
// Continue the registration with the passed nextLink
NSLog ( @ "[AppDelegate] handleUniversalLink. Complete registration with nextLink" ) ;
NSURL * nextLink = [ NSURL URLWithString : queryParams [ @ "nextLink" ] ] ;
[ self handleUniversalLinkFragment : nextLink . fragment ] ;
}
else
{
// No nextLink in Vector world means validation for binding a new email
NSLog ( @ "[AppDelegate] handleUniversalLink. TODO: Complete email binding" ) ;
}
2017-07-14 14:41:25 +00:00
2016-04-22 15:54:57 +00:00
} failure : ^ ( NSError * error ) {
2017-07-14 14:41:25 +00:00
2016-10-21 15:10:02 +00:00
NSLog ( @ "[AppDelegate] handleUniversalLink. Error: submitToken failed" ) ;
2016-04-22 15:54:57 +00:00
[ self showErrorAsAlert : error ] ;
2017-07-14 14:41:25 +00:00
2016-04-22 15:54:57 +00:00
} ] ;
2017-07-14 14:41:25 +00:00
2016-04-22 15:54:57 +00:00
return YES ;
}
2017-07-14 14:41:25 +00:00
2016-04-12 12:19:38 +00:00
return [ self handleUniversalLinkFragment : webURL . fragment ] ;
}
- ( BOOL ) handleUniversalLinkFragment : ( NSString * ) fragment
{
BOOL continueUserActivity = NO ;
2016-04-12 13:30:02 +00:00
MXKAccountManager * accountManager = [ MXKAccountManager sharedManager ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 15:44:06 +00:00
NSLog ( @ "[AppDelegate] Universal link: handleUniversalLinkFragment: %@" , fragment ) ;
2017-07-14 14:41:25 +00:00
2016-04-12 15:44:06 +00:00
// The app manages only one universal link at a time
// Discard any pending one
[ self resetPendingUniversalLink ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
// Extract params
2016-04-12 08:23:48 +00:00
NSArray < NSString * > * pathParams ;
NSMutableDictionary * queryParams ;
2016-04-12 12:19:38 +00:00
[ self parseUniversalLinkFragment : fragment outPathParams : & pathParams outQueryParams : & queryParams ] ;
2017-07-14 14:41:25 +00:00
2016-04-20 07:21:46 +00:00
// Sanity check
if ( ! pathParams . count )
{
NSLog ( @ "[AppDelegate] Universal link: Error: No path parameters" ) ;
return NO ;
}
2017-07-14 14:41:25 +00:00
2016-08-29 09:57:01 +00:00
NSString * roomIdOrAlias ;
NSString * eventId ;
2017-07-25 14:08:24 +00:00
NSString * userId ;
2017-12-31 15:24:47 +00:00
NSString * groupId ;
2017-07-14 14:41:25 +00:00
2016-08-29 09:57:01 +00:00
// Check permalink to room or event
2016-04-12 08:23:48 +00:00
if ( [ pathParams [ 0 ] isEqualToString : @ "room" ] && pathParams . count >= 2 )
{
2016-08-29 09:57:01 +00:00
// The link is the form of "/room/[roomIdOrAlias]" or "/room/[roomIdOrAlias]/[eventId]"
roomIdOrAlias = pathParams [ 1 ] ;
2017-07-14 14:41:25 +00:00
2016-08-29 09:57:01 +00:00
// Is it a link to an event of a room ?
eventId = ( pathParams . count >= 3 ) ? pathParams [ 2 ] : nil ;
}
2017-12-31 15:24:47 +00:00
else if ( [ pathParams [ 0 ] isEqualToString : @ "group" ] && pathParams . count >= 2 )
{
// The link is the form of "/group/[groupId]"
groupId = pathParams [ 1 ] ;
}
2016-08-29 09:57:01 +00:00
else if ( ( [ pathParams [ 0 ] hasPrefix : @ "#" ] || [ pathParams [ 0 ] hasPrefix : @ "!" ] ) && pathParams . count >= 1 )
{
// The link is the form of "/#/[roomIdOrAlias]" or "/#/[roomIdOrAlias]/[eventId]"
// Such links come from matrix . to permalinks
roomIdOrAlias = pathParams [ 0 ] ;
eventId = ( pathParams . count >= 2 ) ? pathParams [ 1 ] : nil ;
}
2017-07-25 14:08:24 +00:00
// Check permalink to a user
else if ( [ pathParams [ 0 ] isEqualToString : @ "user" ] && pathParams . count = = 2 )
{
// The link is the form of "/user/userId"
userId = pathParams [ 1 ] ;
}
else if ( [ pathParams [ 0 ] hasPrefix : @ "@" ] && pathParams . count = = 1 )
{
// The link is the form of "/#/[userId]"
// Such links come from matrix . to permalinks
userId = pathParams [ 0 ] ;
}
2017-02-13 16:58:29 +00:00
// Check the conditions to keep the room alias information of a pending fragment .
if ( universalLinkFragmentPendingRoomAlias )
{
if ( ! roomIdOrAlias || ! universalLinkFragmentPendingRoomAlias [ roomIdOrAlias ] )
{
universalLinkFragmentPendingRoomAlias = nil ;
}
}
2016-08-29 09:57:01 +00:00
if ( roomIdOrAlias )
2017-02-13 16:58:29 +00:00
{
2016-04-12 13:30:02 +00:00
if ( accountManager . activeAccounts . count )
2016-04-12 08:23:48 +00:00
{
2016-04-12 13:30:02 +00:00
// Check there is an account that knows this room
MXKAccount * account = [ accountManager accountKnowingRoomWithRoomIdOrAlias : roomIdOrAlias ] ;
if ( account )
2016-04-12 08:23:48 +00:00
{
2016-04-12 13:30:02 +00:00
NSString * roomId = roomIdOrAlias ;
2017-07-14 14:41:25 +00:00
2016-04-12 13:30:02 +00:00
// Translate the alias into the room id
if ( [ roomIdOrAlias hasPrefix : @ "#" ] )
2016-04-12 08:23:48 +00:00
{
2016-04-12 13:30:02 +00:00
MXRoom * room = [ account . mxSession roomWithAlias : roomIdOrAlias ] ;
if ( room )
{
roomId = room . roomId ;
}
2016-04-12 08:23:48 +00:00
}
2017-07-14 14:41:25 +00:00
2016-04-12 13:30:02 +00:00
// Open the room page
[ self showRoom : roomId andEventId : eventId withMatrixSession : account . mxSession ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 13:30:02 +00:00
continueUserActivity = YES ;
2016-04-12 08:23:48 +00:00
}
2016-04-19 13:18:24 +00:00
else
2016-04-12 13:30:02 +00:00
{
2016-04-19 13:18:24 +00:00
// We will display something but we need to do some requests before .
// So , come back to the home VC and show its loading wheel while processing
[ self restoreInitialDisplay : ^ {
2017-03-23 16:48:05 +00:00
if ( [ _masterTabBarController . selectedViewController isKindOfClass : MXKViewController . class ] )
2016-04-19 13:18:24 +00:00
{
2017-03-23 16:48:05 +00:00
MXKViewController * homeViewController = ( MXKViewController * ) _masterTabBarController . selectedViewController ;
[ homeViewController startActivityIndicator ] ;
if ( [ roomIdOrAlias hasPrefix : @ "#" ] )
{
// The alias may be not part of user ' s rooms states
// Ask the HS to resolve the room alias into a room id and then retry
universalLinkFragmentPending = fragment ;
MXKAccount * account = accountManager . activeAccounts . firstObject ;
[ account . mxSession . matrixRestClient roomIDForRoomAlias : roomIdOrAlias success : ^ ( NSString * roomId ) {
2017-02-13 16:58:29 +00:00
2017-03-23 16:48:05 +00:00
// Note : the activity indicator will not disappear if the session is not ready
[ homeViewController stopActivityIndicator ] ;
2017-02-13 16:58:29 +00:00
2017-03-23 16:48:05 +00:00
// Check that ' fragment ' has not been cancelled
if ( [ universalLinkFragmentPending isEqualToString : fragment ] )
2016-04-19 13:18:24 +00:00
{
2017-03-23 16:48:05 +00:00
// Retry opening the link but with the returned room id
NSString * newUniversalLinkFragment =
[ fragment stringByReplacingOccurrencesOfString : [ roomIdOrAlias stringByAddingPercentEscapesUsingEncoding : NSUTF8StringEncoding ]
withString : [ roomId stringByAddingPercentEscapesUsingEncoding : NSUTF8StringEncoding ] ] ;
universalLinkFragmentPendingRoomAlias = @ { roomId : roomIdOrAlias } ;
[ self handleUniversalLinkFragment : newUniversalLinkFragment ] ;
2016-04-19 13:18:24 +00:00
}
2017-03-23 16:48:05 +00:00
} failure : ^ ( NSError * error ) {
NSLog ( @ "[AppDelegate] Universal link: Error: The home server failed to resolve the room alias (%@)" , roomIdOrAlias ) ;
} ] ;
}
else if ( [ roomIdOrAlias hasPrefix : @ "!" ] && ( ( MXKAccount * ) accountManager . activeAccounts . firstObject ) . mxSession . state ! = MXSessionStateRunning )
2016-04-19 13:18:24 +00:00
{
2017-03-23 16:48:05 +00:00
// The user does not know the room id but this may be because their session is not yet sync ' ed
// So , wait for the completion of the sync and then retry
// FIXME : Manange all user ' s accounts not only the first one
MXKAccount * account = accountManager . activeAccounts . firstObject ;
NSLog ( @ "[AppDelegate] Universal link: Need to wait for the session to be sync'ed and running" ) ;
universalLinkFragmentPending = fragment ;
universalLinkWaitingObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXSessionStateDidChangeNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * _Nonnull notif ) {
// Check that ' fragment ' has not been cancelled
if ( [ universalLinkFragmentPending isEqualToString : fragment ] )
{
// Check whether the concerned session is the associated one
if ( notif . object = = account . mxSession && account . mxSession . state = = MXSessionStateRunning )
{
NSLog ( @ "[AppDelegate] Universal link: The session is running. Retry the link" ) ;
[ self handleUniversalLinkFragment : fragment ] ;
}
}
} ] ;
2016-04-19 13:18:24 +00:00
}
else
{
2017-03-23 16:48:05 +00:00
NSLog ( @ "[AppDelegate] Universal link: The room (%@) is not known by any account (email invitation: %@). Display its preview to try to join it" , roomIdOrAlias , queryParams ? @ "YES" : @ "NO" ) ;
// FIXME : In case of multi - account , ask the user which one to use
MXKAccount * account = accountManager . activeAccounts . firstObject ;
RoomPreviewData * roomPreviewData ;
if ( queryParams )
{
2016-04-19 13:18:24 +00:00
// Note : the activity indicator will not disappear if the session is not ready
2017-03-23 16:48:05 +00:00
[ homeViewController stopActivityIndicator ] ;
2017-02-13 16:58:29 +00:00
2017-03-23 16:48:05 +00:00
roomPreviewData = [ [ RoomPreviewData alloc ] initWithRoomId : roomIdOrAlias emailInvitationParams : queryParams andSession : account . mxSession ] ;
2016-04-19 13:18:24 +00:00
[ self showRoomPreview : roomPreviewData ] ;
2017-03-23 16:48:05 +00:00
}
else
{
roomPreviewData = [ [ RoomPreviewData alloc ] initWithRoomId : roomIdOrAlias andSession : account . mxSession ] ;
// Is it a link to an event of a room ?
// If yes , the event will be displayed once the room is joined
roomPreviewData . eventId = ( pathParams . count >= 3 ) ? pathParams [ 2 ] : nil ;
// Try to get more information about the room before opening its preview
[ roomPreviewData peekInRoom : ^ ( BOOL succeeded ) {
// Note : the activity indicator will not disappear if the session is not ready
[ homeViewController stopActivityIndicator ] ;
// If no data is available for this room , we name it with the known room alias ( if any ) .
if ( ! succeeded && universalLinkFragmentPendingRoomAlias [ roomIdOrAlias ] )
{
roomPreviewData . roomName = universalLinkFragmentPendingRoomAlias [ roomIdOrAlias ] ;
}
universalLinkFragmentPendingRoomAlias = nil ;
[ self showRoomPreview : roomPreviewData ] ;
} ] ;
}
2016-04-12 15:44:06 +00:00
}
2017-07-14 14:41:25 +00:00
2016-04-12 13:30:02 +00:00
}
} ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 13:56:17 +00:00
// Let ' s say we are handling the case
continueUserActivity = YES ;
2016-04-12 13:30:02 +00:00
}
2016-04-12 12:19:38 +00:00
}
2016-04-12 08:23:48 +00:00
else
{
2016-04-12 13:56:17 +00:00
// There is no account . The app will display the AuthenticationVC .
// Wait for a successful login
NSLog ( @ "[AppDelegate] Universal link: The user is not logged in. Wait for a successful login" ) ;
2016-04-12 15:44:06 +00:00
universalLinkFragmentPending = fragment ;
2017-07-14 14:41:25 +00:00
2016-04-12 13:56:17 +00:00
// Register an observer in order to handle new account
2016-04-12 15:44:06 +00:00
universalLinkWaitingObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXKAccountManagerDidAddAccountNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
2017-07-14 14:41:25 +00:00
2016-04-12 15:44:06 +00:00
// Check that ' fragment ' has not been cancelled
if ( [ universalLinkFragmentPending isEqualToString : fragment ] )
{
NSLog ( @ "[AppDelegate] Universal link: The user is now logged in. Retry the link" ) ;
[ self handleUniversalLinkFragment : fragment ] ;
}
2016-04-12 13:56:17 +00:00
} ] ;
2016-04-12 08:23:48 +00:00
}
}
2017-07-25 14:08:24 +00:00
else if ( userId )
{
// Check there is an account that knows this user
MXUser * mxUser ;
MXKAccount * account = [ accountManager accountKnowingUserWithUserId : userId ] ;
if ( account )
{
mxUser = [ account . mxSession userWithUserId : userId ] ;
}
// Prepare the display name of this user
NSString * displayName ;
if ( mxUser )
{
displayName = ( mxUser . displayname . length > 0 ) ? mxUser . displayname : userId ;
}
else
{
displayName = userId ;
}
// Create the contact related to this member
MXKContact * contact = [ [ MXKContact alloc ] initMatrixContactWithDisplayName : displayName andMatrixID : userId ] ;
[ self showContact : contact ] ;
continueUserActivity = YES ;
}
2017-12-31 15:24:47 +00:00
else if ( groupId )
{
// @ FIXME : In case of multi - account , ask the user which one to use
MXKAccount * account = accountManager . activeAccounts . firstObject ;
if ( account )
{
MXGroup * group = [ account . mxSession groupWithGroupId : groupId ] ;
if ( ! group )
{
// Create a group instance to display its preview
group = [ [ MXGroup alloc ] initWithGroupId : groupId ] ;
}
// Display the group details
[ self showGroup : group withMatrixSession : account . mxSession ] ;
continueUserActivity = YES ;
}
else
{
// There is no account . The app will display the AuthenticationVC .
// Wait for a successful login
NSLog ( @ "[AppDelegate] Universal link: The user is not logged in. Wait for a successful login" ) ;
universalLinkFragmentPending = fragment ;
// Register an observer in order to handle new account
universalLinkWaitingObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXKAccountManagerDidAddAccountNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
// Check that ' fragment ' has not been cancelled
if ( [ universalLinkFragmentPending isEqualToString : fragment ] )
{
NSLog ( @ "[AppDelegate] Universal link: The user is now logged in. Retry the link" ) ;
[ self handleUniversalLinkFragment : fragment ] ;
}
} ] ;
}
}
2016-04-13 15:56:46 +00:00
else if ( [ pathParams [ 0 ] isEqualToString : @ "register" ] )
{
2016-04-22 16:42:14 +00:00
NSLog ( @ "[AppDelegate] Universal link with registration parameters" ) ;
2016-04-21 14:19:16 +00:00
continueUserActivity = YES ;
2016-04-22 16:42:14 +00:00
2017-03-23 16:48:05 +00:00
[ _masterTabBarController showAuthenticationScreenWithRegistrationParameters : queryParams ] ;
2016-04-13 15:56:46 +00:00
}
2016-04-12 08:23:48 +00:00
else
{
2016-04-13 15:56:46 +00:00
// Unknown command : Do nothing except coming back to the main screen
2016-04-12 13:30:02 +00:00
NSLog ( @ "[AppDelegate] Universal link: TODO: Do not know what to do with the link arguments: %@" , pathParams ) ;
2017-07-14 14:41:25 +00:00
2016-04-13 15:56:46 +00:00
[ self popToHomeViewControllerAnimated : NO completion : nil ] ;
2016-04-12 08:23:48 +00:00
}
2017-07-14 14:41:25 +00:00
2016-04-12 08:23:48 +00:00
return continueUserActivity ;
}
2016-04-12 15:44:06 +00:00
- ( void ) resetPendingUniversalLink
{
universalLinkFragmentPending = nil ;
if ( universalLinkWaitingObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : universalLinkWaitingObserver ] ;
universalLinkWaitingObserver = nil ;
}
}
2016-04-12 09:19:50 +00:00
/ * *
2016-04-12 13:30:02 +00:00
Extract params from the URL fragment part ( after ' # ' ) of a vector . im Universal link :
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
The fragment can contain a ' ? ' . So there are two kinds of parameters : path params and query params .
It is in the form of / [ pathParam1 ] / [ pathParam2 ] ? [ queryParam1Key ] = [ queryParam1Value ] & [ queryParam2Key ] = [ queryParam2Value ]
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
@ param fragment the fragment to parse .
@ param outPathParams the decoded path params .
@ param outQueryParams the decoded query params . If there is no query params , it will be nil .
* /
- ( void ) parseUniversalLinkFragment : ( NSString * ) fragment outPathParams : ( NSArray < NSString * > * * ) outPathParams outQueryParams : ( NSMutableDictionary * * ) outQueryParams
{
NSParameterAssert ( outPathParams && outQueryParams ) ;
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
NSArray < NSString * > * pathParams ;
NSMutableDictionary * queryParams ;
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
NSArray < NSString * > * fragments = [ fragment componentsSeparatedByString : @ "?" ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
// Extract path params
pathParams = [ fragments [ 0 ] componentsSeparatedByString : @ "/" ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
// Remove the first empty path param string
pathParams = [ pathParams filteredArrayUsingPredicate : [ NSPredicate predicateWithFormat : @ "length > 0" ] ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
// URL decode each path param
NSMutableArray < NSString * > * pathParams2 = [ NSMutableArray arrayWithArray : pathParams ] ;
for ( NSInteger i = 0 ; i < pathParams . count ; i + + )
{
pathParams2 [ i ] = [ pathParams2 [ i ] stringByReplacingPercentEscapesUsingEncoding : NSUTF8StringEncoding ] ;
}
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
NSString * key = [ [ keyValue componentsSeparatedByString : @ "=" ] objectAtIndex : 0 ] ;
2017-07-14 14:41:25 +00:00
2016-04-12 09:19:50 +00:00
// Get the parameter value
NSString * value = [ [ keyValue componentsSeparatedByString : @ "=" ] objectAtIndex : 1 ] ;
2016-04-13 15:10:35 +00:00
if ( value . length )
{
value = [ value stringByReplacingOccurrencesOfString : @ "+" withString : @ " " ] ;
value = [ value stringByReplacingPercentEscapesUsingEncoding : NSUTF8StringEncoding ] ;
2017-07-14 14:41:25 +00:00
2016-04-13 15:10:35 +00:00
queryParams [ key ] = value ;
}
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 ;
}
2015-04-30 14:19:12 +00:00
# pragma mark - Matrix sessions handling
2015-06-18 09:19:42 +00:00
- ( void ) initMatrixSessions
{
2017-02-20 17:34:41 +00:00
NSLog ( @ "[AppDelegate] initMatrixSessions" ) ;
2017-07-14 14:41:25 +00:00
2017-06-14 15:37:22 +00:00
MXSDKOptions * sdkOptions = [ MXSDKOptions sharedInstance ] ;
2017-02-20 17:34:41 +00:00
2017-03-23 10:49:58 +00:00
// Define the media cache version
2017-06-14 15:37:22 +00:00
sdkOptions . mediaCacheAppVersion = 0 ;
2017-03-23 10:49:58 +00:00
2016-11-16 12:40:04 +00:00
// Enable e2e encryption for newly created MXSession
2017-06-14 15:37:22 +00:00
sdkOptions . enableCryptoWhenStartingMXSession = YES ;
2017-07-14 14:41:25 +00:00
2015-12-11 12:51:48 +00:00
// Disable identicon use
2017-06-14 15:37:22 +00:00
sdkOptions . disableIdenticonUseForUserAvatar = YES ;
2017-07-14 14:41:25 +00:00
2017-01-16 16:26:53 +00:00
// Enable SDK stats upload to GA
2017-06-14 15:37:22 +00:00
sdkOptions . enableGoogleAnalytics = YES ;
2017-07-14 14:41:25 +00:00
2017-06-14 15:37:22 +00:00
// Use UIKit BackgroundTask for handling background tasks in the SDK
2017-06-16 12:34:52 +00:00
sdkOptions . backgroundModeHandler = [ [ MXUIKitBackgroundModeHandler alloc ] init ] ;
2017-08-30 15:37:14 +00:00
// Get modular widget events in rooms histories
[ [ MXKAppSettings standardAppSettings ] addSupportedEventTypes : @ [ kWidgetEventTypeString ] ] ;
2015-12-11 12:51:48 +00:00
2016-02-11 14:19:49 +00:00
// Disable long press on event in bubble cells
[ MXKRoomBubbleTableViewCell disableLongPressGestureOnEvent : YES ] ;
2015-08-21 18:00:39 +00:00
// Set first RoomDataSource class used in Vector
[ MXKRoomDataSourceManager registerRoomDataSourceClass : RoomDataSource . class ] ;
2015-05-21 16:15:45 +00:00
// Register matrix session state observer in order to handle multi - sessions .
2017-07-14 14:41:25 +00:00
matrixSessionStateObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXSessionStateDidChangeNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
2015-04-30 14:19:12 +00:00
MXSession * mxSession = ( MXSession * ) notif . object ;
// Check whether the concerned session is a new one
2015-06-18 09:19:42 +00:00
if ( mxSession . state = = MXSessionStateInitialised )
{
2015-11-20 13:47:38 +00:00
// Store this new session
2015-11-17 23:15:52 +00:00
[ self addMatrixSession : mxSession ] ;
2015-12-18 10:55:35 +00:00
2016-03-23 13:45:16 +00:00
// Set the VoIP call stack ( if supported ) .
id < MXCallStack > callStack ;
# ifdef MX_CALL _STACK _OPENWEBRTC
callStack = [ [ MXOpenWebRTCCallStack alloc ] init ] ;
# endif
# ifdef MX_CALL _STACK _ENDPOINT
callStack = [ [ MXEndpointCallStack alloc ] initWithMatrixId : mxSession . myUser . userId ] ;
2016-07-22 16:26:55 +00:00
# endif
2017-11-24 15:28:48 +00:00
# ifdef CALL_STACK _JINGLE
2016-07-22 16:26:55 +00:00
callStack = [ [ MXJingleCallStack alloc ] init ] ;
2016-03-23 13:45:16 +00:00
# endif
if ( callStack )
{
[ mxSession enableVoIPWithCallStack : callStack ] ;
2017-06-20 20:16:54 +00:00
2017-12-20 10:27:11 +00:00
// Let ' s call invite be valid for 1 minute
mxSession . callManager . inviteLifetime = 60000 ;
2017-06-20 20:16:54 +00:00
// Setup CallKit
if ( [ MXCallKitAdapter callKitAvailable ] )
{
BOOL isCallKitEnabled = [ MXKAppSettings standardAppSettings ] . isCallKitEnabled ;
[ self enableCallKit : isCallKitEnabled forCallManager : mxSession . callManager ] ;
// Register for changes performed by the user
[ [ MXKAppSettings standardAppSettings ] addObserver : self
forKeyPath : @ "enableCallKit"
options : NSKeyValueObservingOptionNew
context : NULL ] ;
}
2016-03-23 13:45:16 +00:00
}
2016-05-20 13:30:10 +00:00
else
{
// When there is no call stack , display alerts on call invites
[ self enableNoVoIPOnMatrixSession : mxSession ] ;
}
2017-07-14 14:41:25 +00:00
2015-12-22 07:43:55 +00:00
// Each room member will be considered as a potential contact .
[ MXKContactManager sharedManager ] . contactManagerMXRoomSource = MXKContactManagerMXRoomSourceAll ;
2017-08-30 15:37:14 +00:00
// Send read receipts for modular widgets events too
NSMutableArray < MXEventTypeString > * acknowledgableEventTypes = [ NSMutableArray arrayWithArray : mxSession . acknowledgableEventTypes ] ;
[ acknowledgableEventTypes addObject : kWidgetEventTypeString ] ;
mxSession . acknowledgableEventTypes = acknowledgableEventTypes ;
2015-06-18 13:20:33 +00:00
}
else if ( mxSession . state = = MXSessionStateStoreDataReady )
2015-06-18 09:19:42 +00:00
{
2017-09-07 17:28:38 +00:00
// A new call observer may be added here
[ self addMatrixCallObserver ] ;
2017-10-18 17:18:14 +00:00
// Enable local notifications
[ self enableLocalNotificationsFromMatrixSession : mxSession ] ;
2017-09-08 13:32:55 +00:00
// Look for the account related to this session .
2015-06-19 08:31:27 +00:00
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
2015-06-18 13:20:33 +00:00
for ( MXKAccount * account in mxAccounts )
2015-06-18 09:19:42 +00:00
{
2015-06-18 13:20:33 +00:00
if ( account . mxSession = = mxSession )
{
2017-09-08 13:32:55 +00:00
// Enable inApp notifications ( if they are allowed for this account ) .
2015-06-18 13:20:33 +00:00
[ self enableInAppNotificationsForAccount : account ] ;
break ;
}
2015-04-30 14:19:12 +00:00
}
2015-06-18 13:20:33 +00:00
}
else if ( mxSession . state = = MXSessionStateClosed )
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
[ self removeMatrixSession : mxSession ] ;
2015-05-21 16:15:45 +00:00
}
2017-09-06 14:59:12 +00:00
// Consider here the case where the app is running in background .
else if ( [ [ UIApplication sharedApplication ] applicationState ] = = UIApplicationStateBackground )
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] MXSession state changed while in background. mxSession.state: %tu - incomingPushEventIds: %@" , mxSession . state , self . incomingPushEventIds [ @ ( mxSession . hash ) ] ) ;
2017-09-06 14:59:12 +00:00
if ( mxSession . state = = MXSessionStateRunning )
{
// Pause the session in background task
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
for ( MXKAccount * account in mxAccounts )
{
if ( account . mxSession = = mxSession )
{
[ account pauseInBackgroundTask ] ;
2017-10-19 14:09:50 +00:00
// Trigger local notifcations ( Indeed the app finishs here an initial sync in background , the user has missed some notifcations )
[ self handleLocalNotificationsForAccount : account ] ;
// Update app icon badge number
[ self refreshApplicationIconBadgeNumber ] ;
2017-09-06 14:59:12 +00:00
break ;
}
}
}
else if ( mxSession . state = = MXSessionStatePaused )
{
2017-10-23 14:58:55 +00:00
// Check whether some push notifications are pending for this session .
if ( self . incomingPushEventIds [ @ ( mxSession . hash ) ] . count )
2017-09-06 14:59:12 +00:00
{
2017-12-29 09:31:16 +00:00
NSLog ( @ "[AppDelegate][Push] relaunch a background sync for %tu kMXSessionStateDidChangeNotification pending incoming pushes" , self . incomingPushEventIds [ @ ( mxSession . hash ) ] . count ) ;
2017-10-19 14:09:50 +00:00
[ self launchBackgroundSync ] ;
2017-09-06 14:59:12 +00:00
}
}
2017-12-29 09:31:16 +00:00
else if ( mxSession . state = = MXSessionStateInitialSyncFailed )
{
// Display failure sync notifications for pending events if any
if ( self . incomingPushEventIds [ @ ( mxSession . hash ) ] . count )
{
NSLog ( @ "[AppDelegate][Push] initial sync failed with %tu pending incoming pushes" , self . incomingPushEventIds [ @ ( mxSession . hash ) ] . count ) ;
2017-12-29 12:40:55 +00:00
// Trigger limited local notifications when the sync with HS fails
2017-12-29 12:22:29 +00:00
[ self handleLimitedLocalNotifications : mxSession events : self . incomingPushEventIds [ @ ( mxSession . hash ) ] ] ;
2017-12-29 09:31:16 +00:00
// Update app icon badge number
[ self refreshApplicationIconBadgeNumber ] ;
}
}
2017-09-06 14:59:12 +00:00
}
2017-11-16 13:46:47 +00:00
else if ( [ [ UIApplication sharedApplication ] applicationState ] = = UIApplicationStateActive )
{
if ( mxSession . state = = MXSessionStateRunning )
{
// Check if we need to display a key share dialog
[ self checkPendingRoomKeyRequests ] ;
}
}
2015-05-21 16:15:45 +00:00
2017-01-31 23:18:01 +00:00
[ self handleLaunchAnimation ] ;
2015-04-30 14:19:12 +00:00
} ] ;
// Register an observer in order to handle new account
2015-06-18 13:20:33 +00:00
addedAccountObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXKAccountManagerDidAddAccountNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
2015-04-30 14:19:12 +00:00
2016-03-23 13:45:16 +00:00
// Finalize the initialization of this new account
2015-06-18 13:20:33 +00:00
MXKAccount * account = notif . object ;
2015-06-18 09:19:42 +00:00
if ( account )
{
2017-03-17 15:46:44 +00:00
// Replace default room summary updater
EventFormatter * eventFormatter = [ [ EventFormatter alloc ] initWithMatrixSession : account . mxSession ] ;
eventFormatter . isForSubtitle = YES ;
account . mxSession . roomSummaryUpdateDelegate = eventFormatter ;
2017-07-14 14:41:25 +00:00
2016-03-21 13:49:15 +00:00
// Set the push gateway URL .
account . pushGatewayURL = [ [ NSUserDefaults standardUserDefaults ] objectForKey : @ "pushGatewayURL" ] ;
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
2017-09-19 14:06:44 +00:00
account . enablePushKitNotifications = YES ;
2015-06-19 08:31:27 +00:00
}
else
{
// Set up push notifications
[ self registerUserNotificationSettings ] ;
}
2015-04-30 14:19:12 +00:00
2015-06-18 13:20:33 +00:00
// Observe inApp notifications toggle change
[ account addObserver : self forKeyPath : @ "enableInAppNotifications" options : 0 context : nil ] ;
2015-04-30 14:19:12 +00:00
}
2017-01-17 22:13:30 +00:00
// Load the local contacts on first account creation .
if ( [ MXKAccountManager sharedManager ] . accounts . count = = 1 )
{
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self refreshLocalContacts ] ;
} ) ;
}
2015-05-11 21:11:23 +00:00
} ] ;
2015-06-18 13:20:33 +00:00
// Add observer to handle removed accounts
removedAccountObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXKAccountManagerDidRemoveAccountNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
// Remove inApp notifications toggle change
MXKAccount * account = notif . object ;
[ 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 )
{
[ self logout ] ;
}
} ] ;
2017-07-14 14:41:25 +00:00
2016-05-04 16:35:59 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXSessionIgnoredUsersDidChangeNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * _Nonnull notif ) {
2017-07-14 14:41:25 +00:00
2016-05-04 16:35:59 +00:00
NSLog ( @ "[AppDelegate] kMXSessionIgnoredUsersDidChangeNotification received. Reload the app" ) ;
2017-07-14 14:41:25 +00:00
2016-05-04 16:35:59 +00:00
// Reload entirely the app when a user has been ignored or unignored
[ [ AppDelegate theDelegate ] reloadMatrixSessions : YES ] ;
2017-07-14 14:41:25 +00:00
2016-05-04 16:35:59 +00:00
} ] ;
2015-06-18 13:20:33 +00:00
2016-08-12 14:50:27 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXSessionDidCorruptDataNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * _Nonnull notif ) {
NSLog ( @ "[AppDelegate] kMXSessionDidCorruptDataNotification received. Reload the app" ) ;
// Reload entirely the app when a session has corrupted its data
[ [ AppDelegate theDelegate ] reloadMatrixSessions : YES ] ;
} ] ;
2017-08-24 13:18:23 +00:00
// Add observer on settings changes .
2017-08-16 16:35:32 +00:00
[ [ MXKAppSettings standardAppSettings ] addObserver : self forKeyPath : @ "showAllEventsInRoomHistory" options : 0 context : nil ] ;
2015-04-30 14:19:12 +00:00
2015-07-21 09:30:37 +00:00
// Prepare account manager
MXKAccountManager * accountManager = [ MXKAccountManager sharedManager ] ;
// Use MXFileStore as MXStore to permanently store events .
accountManager . storeClass = [ MXFileStore class ] ;
2017-07-14 14:41:25 +00:00
2017-09-19 14:06:44 +00:00
// Disable APNS use .
if ( accountManager . apnsDeviceToken )
{
// We use now Pushkit , unregister for all remote notifications received via Apple Push Notification service .
[ [ UIApplication sharedApplication ] unregisterForRemoteNotifications ] ;
[ accountManager setApnsDeviceToken : nil ] ;
}
2016-03-23 13:45:16 +00:00
// Observers have been defined , we can start a matrix session for each enabled accounts .
2017-09-06 14:59:12 +00:00
NSLog ( @ "[AppDelegate] initMatrixSessions: prepareSessionForActiveAccounts (app state: %tu)" , [ [ UIApplication sharedApplication ] applicationState ] ) ;
[ accountManager prepareSessionForActiveAccounts ] ;
2015-06-19 08:31:27 +00:00
// Check whether we ' re already logged in
2017-10-04 11:04:21 +00:00
NSArray * mxAccounts = accountManager . activeAccounts ;
2015-06-18 09:19:42 +00:00
if ( mxAccounts . count )
{
2016-03-21 13:49:15 +00:00
for ( MXKAccount * account in mxAccounts )
{
2017-03-17 15:46:44 +00:00
// Replace default room summary updater
EventFormatter * eventFormatter = [ [ EventFormatter alloc ] initWithMatrixSession : account . mxSession ] ;
eventFormatter . isForSubtitle = YES ;
account . mxSession . roomSummaryUpdateDelegate = eventFormatter ;
2017-07-14 14:41:25 +00:00
2017-03-17 15:46:44 +00:00
// The push gateway url is now configurable .
// Set this url in the existing accounts when it is undefined .
2016-03-21 13:49:15 +00:00
if ( ! account . pushGatewayURL )
{
account . pushGatewayURL = [ [ NSUserDefaults standardUserDefaults ] objectForKey : @ "pushGatewayURL" ] ;
}
}
2015-06-19 08:31:27 +00:00
// Set up push notifications
2015-04-30 14:19:12 +00:00
[ self registerUserNotificationSettings ] ;
2015-06-19 08:31:27 +00:00
// Observe inApp notifications toggle change for each account
2015-06-18 09:19:42 +00:00
for ( MXKAccount * account in mxAccounts )
{
2015-06-18 13:20:33 +00:00
[ account addObserver : self forKeyPath : @ "enableInAppNotifications" options : 0 context : nil ] ;
2015-05-21 16:15:45 +00:00
}
2015-04-30 14:19:12 +00:00
}
}
2015-11-17 23:15:52 +00:00
- ( NSArray * ) mxSessions
{
return [ NSArray arrayWithArray : mxSessionArray ] ;
}
- ( void ) addMatrixSession : ( MXSession * ) mxSession
{
if ( mxSession )
{
2015-11-20 13:47:38 +00:00
// Report this session to contact manager
[ [ MXKContactManager sharedManager ] addMatrixSession : mxSession ] ;
2016-06-14 09:10:37 +00:00
// Update home data sources
2017-03-23 16:48:05 +00:00
[ _masterTabBarController addMatrixSession : mxSession ] ;
2017-08-04 11:05:07 +00:00
// Register the session to the widgets manager
[ [ WidgetManager sharedManager ] addMatrixSession : mxSession ] ;
2015-11-17 23:15:52 +00:00
[ mxSessionArray addObject : mxSession ] ;
2017-03-23 16:48:05 +00:00
// Do the one time check on device id
[ self checkDeviceId : mxSession ] ;
2017-10-23 14:58:55 +00:00
// Add an array to handle incoming push
self . incomingPushEventIds [ @ ( mxSession . hash ) ] = [ NSMutableArray array ] ;
2017-11-14 17:21:01 +00:00
// Enable listening of incoming key share requests
[ self enableRoomKeyRequestObserver : mxSession ] ;
2015-11-17 23:15:52 +00:00
}
}
- ( void ) removeMatrixSession : ( MXSession * ) mxSession
{
2015-11-20 13:47:38 +00:00
[ [ MXKContactManager sharedManager ] removeMatrixSession : mxSession ] ;
2015-12-09 17:12:42 +00:00
// Update home data sources
2017-03-23 16:48:05 +00:00
[ _masterTabBarController removeMatrixSession : mxSession ] ;
2016-05-20 12:31:23 +00:00
2017-08-04 11:05:07 +00:00
// Update the widgets manager
[ [ WidgetManager sharedManager ] removeMatrixSession : mxSession ] ;
2017-07-14 14:41:25 +00:00
2016-05-20 13:30:10 +00:00
// If any , disable the no VoIP support workaround
2016-05-20 12:31:23 +00:00
[ self disableNoVoIPOnMatrixSession : mxSession ] ;
2015-11-17 23:15:52 +00:00
2017-10-18 17:18:14 +00:00
// Disable local notifications from this session
[ self disableLocalNotificationsFromMatrixSession : mxSession ] ;
2017-11-14 17:21:01 +00:00
// Disable listening of incoming key share requests
[ self disableRoomKeyRequestObserver : mxSession ] ;
2017-10-18 17:18:14 +00:00
2015-11-17 23:15:52 +00:00
[ mxSessionArray removeObject : mxSession ] ;
2017-09-07 17:28:38 +00:00
if ( ! mxSessionArray . count && matrixCallObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : matrixCallObserver ] ;
matrixCallObserver = nil ;
}
2017-10-23 14:58:55 +00:00
[ self . incomingPushEventIds removeObjectForKey : @ ( mxSession . hash ) ] ;
2015-11-17 23:15:52 +00:00
}
2016-08-26 14:12:46 +00:00
- ( void ) markAllMessagesAsRead
{
2017-04-18 14:20:55 +00:00
for ( MXSession * session in mxSessionArray )
{
[ session markAllMessagesAsRead ] ;
}
2016-08-26 14:12:46 +00:00
}
2015-06-18 09:19:42 +00:00
- ( void ) reloadMatrixSessions : ( BOOL ) clearCache
{
2015-04-30 14:19:12 +00:00
// Reload all running matrix sessions
2015-06-19 08:31:27 +00:00
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
2015-06-18 09:19:42 +00:00
for ( MXKAccount * account in mxAccounts )
{
2015-06-19 08:31:27 +00:00
[ account reload : clearCache ] ;
2017-07-14 14:41:25 +00:00
2017-03-27 08:02:26 +00:00
// Replace default room summary updater
EventFormatter * eventFormatter = [ [ EventFormatter alloc ] initWithMatrixSession : account . mxSession ] ;
eventFormatter . isForSubtitle = YES ;
account . mxSession . roomSummaryUpdateDelegate = eventFormatter ;
2015-04-30 14:19:12 +00:00
}
// Force back to Recents list if room details is displayed ( Room details are not available until the end of initial sync )
2016-04-13 15:08:02 +00:00
[ self popToHomeViewControllerAnimated : NO completion : nil ] ;
2015-04-30 14:19:12 +00:00
2015-06-18 09:19:42 +00:00
if ( clearCache )
{
2015-04-30 14:19:12 +00:00
// clear the media cache
2016-12-01 13:55:29 +00:00
[ MXMediaManager clearCache ] ;
2015-04-30 14:19:12 +00:00
}
}
2014-10-08 16:55:26 +00:00
2015-06-18 09:19:42 +00:00
- ( void ) logout
{
2017-08-21 10:28:05 +00:00
self . pushRegistry = nil ;
2017-09-19 14:06:44 +00:00
isPushRegistered = NO ;
2015-04-17 13:45:32 +00:00
2014-10-17 23:04:05 +00:00
// Clear cache
2016-12-01 13:55:29 +00:00
[ MXMediaManager clearCache ] ;
2017-07-14 14:41:25 +00:00
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
[ [ MXKAccountManager sharedManager ] logout ] ;
2015-04-17 13:45:32 +00:00
// Return to authentication screen
2017-03-23 16:48:05 +00:00
[ _masterTabBarController showAuthenticationScreen ] ;
2015-04-17 13:45:32 +00:00
2017-01-17 22:13:30 +00:00
// Note : Keep App settings
2015-04-17 13:45:32 +00:00
2015-04-17 12:24:08 +00:00
// Reset the contact manager
2015-06-08 16:53:57 +00:00
[ [ MXKContactManager sharedManager ] reset ] ;
2014-10-08 16:55:26 +00:00
}
2014-10-03 17:26:39 +00:00
2015-11-17 23:15:52 +00:00
- ( void ) observeValueForKeyPath : ( NSString * ) keyPath ofObject : ( id ) object change : ( NSDictionary * ) change context : ( void * ) context
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
if ( [ @ "showAllEventsInRoomHistory" isEqualToString : keyPath ] )
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
// Flush and restore Matrix data
[ self reloadMatrixSessions : NO ] ;
2015-02-18 16:40:55 +00:00
}
2015-11-17 23:15:52 +00:00
else if ( [ @ "enableInAppNotifications" isEqualToString : keyPath ] && [ object isKindOfClass : [ MXKAccount class ] ] )
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
[ self enableInAppNotificationsForAccount : ( MXKAccount * ) object ] ;
2014-10-31 17:54:32 +00:00
}
2017-06-20 20:16:54 +00:00
else if ( object = = [ MXKAppSettings standardAppSettings ] && [ keyPath isEqualToString : @ "enableCallKit" ] )
{
BOOL isCallKitEnabled = [ MXKAppSettings standardAppSettings ] . isCallKitEnabled ;
MXCallManager * callManager = [ [ [ [ [ MXKAccountManager sharedManager ] activeAccounts ] firstObject ] mxSession ] callManager ] ;
[ self enableCallKit : isCallKitEnabled forCallManager : callManager ] ;
}
2015-11-17 23:15:52 +00:00
}
- ( void ) addMatrixCallObserver
{
if ( matrixCallObserver )
2014-10-08 12:14:12 +00:00
{
2017-09-07 17:28:38 +00:00
return ;
2014-10-03 17:26:39 +00:00
}
2014-10-08 12:14:12 +00:00
2017-09-07 17:28:38 +00:00
// Register call observer in order to handle incoming calls
2017-06-21 10:02:49 +00:00
matrixCallObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXCallManagerNewCall
object : nil
queue : [ NSOperationQueue mainQueue ]
usingBlock : ^ ( NSNotification * notif )
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
// Ignore the call if a call is already in progress
2017-08-11 12:18:10 +00:00
if ( ! currentCallViewController && ! _jitsiViewController )
2015-11-17 23:15:52 +00:00
{
MXCall * mxCall = ( MXCall * ) notif . object ;
2017-10-18 08:19:05 +00:00
BOOL isCallKitEnabled = [ MXCallKitAdapter callKitAvailable ] && [ MXKAppSettings standardAppSettings ] . isCallKitEnabled ;
2017-06-20 20:16:54 +00:00
2016-08-19 15:43:29 +00:00
// Prepare the call view controller
2017-06-20 20:16:54 +00:00
currentCallViewController = [ CallViewController callViewController : nil ] ;
2017-10-18 08:19:05 +00:00
currentCallViewController . playRingtone = ! isCallKitEnabled ;
2017-06-20 20:16:54 +00:00
currentCallViewController . mxCall = mxCall ;
2015-11-17 23:15:52 +00:00
currentCallViewController . delegate = self ;
2017-08-31 13:58:28 +00:00
2017-08-24 11:03:05 +00:00
UIApplicationState applicationState = UIApplication . sharedApplication . applicationState ;
2015-11-17 23:15:52 +00:00
2017-08-24 11:03:05 +00:00
// App has been woken by PushKit notification in the background
if ( applicationState = = UIApplicationStateBackground && mxCall . isIncoming )
{
// Create backgound task .
// Without CallKit this will allow us to play vibro until the call was ended
// With CallKit we ' ll inform the system when the call is ended to let the system terminate our app to save resources
id < MXBackgroundModeHandler > handler = [ MXSDKOptions sharedInstance ] . backgroundModeHandler ;
NSUInteger callTaskIdentifier = [ handler startBackgroundTaskWithName : nil completion : ^ { } ] ;
// Start listening for call state change notifications
__weak NSNotificationCenter * notificationCenter = [ NSNotificationCenter defaultCenter ] ;
__block id token = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXCallStateDidChange
object : mxCall
queue : nil
usingBlock : ^ ( NSNotification * _Nonnull note ) {
MXCall * call = ( MXCall * ) note . object ;
if ( call . state = = MXCallStateEnded )
{
// Set call vc to nil to let our app handle new incoming calls even it wasn ' t killed by the system
currentCallViewController = nil ;
[ notificationCenter removeObserver : token ] ;
[ handler endBackgrounTaskWithIdentifier : callTaskIdentifier ] ;
}
} ] ;
}
2017-10-18 08:19:05 +00:00
if ( mxCall . isIncoming && isCallKitEnabled )
2016-08-19 15:43:29 +00:00
{
2017-10-18 08:19:05 +00:00
// Let ' s CallKit display the system incoming call screen
// Show the callVC only after the user answered the call
__weak NSNotificationCenter * notificationCenter = [ NSNotificationCenter defaultCenter ] ;
__block id token = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXCallStateDidChange
object : mxCall
queue : nil
usingBlock : ^ ( NSNotification * _Nonnull note ) {
MXCall * call = ( MXCall * ) note . object ;
2017-08-31 13:51:05 +00:00
2017-10-27 13:35:43 +00:00
NSLog ( @ "[AppDelegate] call.state: %@" , call ) ;
2017-10-18 08:19:05 +00:00
if ( call . state = = MXCallStateCreateAnswer )
{
[ notificationCenter removeObserver : token ] ;
2017-10-27 13:35:43 +00:00
NSLog ( @ "[AppDelegate] presentCallViewController" ) ;
2017-10-18 08:19:05 +00:00
[ self presentCallViewController : NO completion : nil ] ;
}
} ] ;
2016-08-19 15:43:29 +00:00
}
else
{
2017-10-18 08:19:05 +00:00
[ self presentCallViewController : YES completion : nil ] ;
2016-08-19 15:43:29 +00:00
}
2015-11-17 23:15:52 +00:00
}
2014-11-04 16:26:33 +00:00
} ] ;
2014-10-03 17:26:39 +00:00
}
2017-01-31 23:18:01 +00:00
- ( void ) handleLaunchAnimation
{
MXSession * mainSession = self . mxSessions . firstObject ;
if ( mainSession )
{
BOOL isLaunching = NO ;
switch ( mainSession . state )
{
case MXSessionStateClosed :
case MXSessionStateInitialised :
isLaunching = YES ;
break ;
case MXSessionStateStoreDataReady :
case MXSessionStateSyncInProgress :
2017-05-12 13:20:56 +00:00
// Stay in launching during the first server sync if the store is empty .
isLaunching = ( mainSession . rooms . count = = 0 && launchAnimationContainerView ) ;
2017-01-31 23:18:01 +00:00
default :
break ;
}
if ( isLaunching )
{
UIWindow * window = [ [ UIApplication sharedApplication ] keyWindow ] ;
if ( ! launchAnimationContainerView && window )
{
launchAnimationContainerView = [ [ UIView alloc ] initWithFrame : window . bounds ] ;
2017-08-11 14:56:09 +00:00
launchAnimationContainerView . backgroundColor = kRiotPrimaryBgColor ;
2017-01-31 23:18:01 +00:00
launchAnimationContainerView . autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight ;
[ window addSubview : launchAnimationContainerView ] ;
// Add animation view
UIImageView * animationView = [ [ UIImageView alloc ] initWithFrame : CGRectMake ( 0 , 0 , 170 , 170 ) ] ;
animationView . image = [ UIImage animatedImageNamed : @ "animatedLogo-" duration : 2 ] ;
animationView . center = CGPointMake ( launchAnimationContainerView . center . x , 3 * launchAnimationContainerView . center . y / 4 ) ;
animationView . translatesAutoresizingMaskIntoConstraints = NO ;
[ launchAnimationContainerView addSubview : animationView ] ;
NSLayoutConstraint * widthConstraint = [ NSLayoutConstraint constraintWithItem : animationView
attribute : NSLayoutAttributeWidth
relatedBy : NSLayoutRelationEqual
toItem : nil
attribute : NSLayoutAttributeNotAnAttribute
multiplier : 1
constant : 170 ] ;
NSLayoutConstraint * heightConstraint = [ NSLayoutConstraint constraintWithItem : animationView
attribute : NSLayoutAttributeHeight
relatedBy : NSLayoutRelationEqual
toItem : nil
attribute : NSLayoutAttributeNotAnAttribute
multiplier : 1
constant : 170 ] ;
NSLayoutConstraint * centerXConstraint = [ NSLayoutConstraint constraintWithItem : animationView
attribute : NSLayoutAttributeCenterX
relatedBy : NSLayoutRelationEqual
toItem : launchAnimationContainerView
attribute : NSLayoutAttributeCenterX
multiplier : 1
constant : 0 ] ;
NSLayoutConstraint * centerYConstraint = [ NSLayoutConstraint constraintWithItem : animationView
attribute : NSLayoutAttributeCenterY
relatedBy : NSLayoutRelationEqual
toItem : launchAnimationContainerView
attribute : NSLayoutAttributeCenterY
multiplier : 3.0 / 4.0
constant : 0 ] ;
[ NSLayoutConstraint activateConstraints : @ [ widthConstraint , heightConstraint , centerXConstraint , centerYConstraint ] ] ;
launchAnimationStart = [ NSDate date ] ;
}
2017-02-21 15:08:10 +00:00
return ;
2017-01-31 23:18:01 +00:00
}
2017-02-21 15:08:10 +00:00
}
if ( launchAnimationContainerView )
{
NSTimeInterval durationMs = [ [ NSDate date ] timeIntervalSinceDate : launchAnimationStart ] * 1000 ;
NSLog ( @ "[AppDelegate] LaunchAnimation was shown for %.3fms" , durationMs ) ;
if ( [ MXSDKOptions sharedInstance ] . enableGoogleAnalytics )
2017-01-31 23:18:01 +00:00
{
2017-02-21 15:08:10 +00:00
id < GAITracker > tracker = [ [ GAI sharedInstance ] defaultTracker ] ;
[ tracker send : [ [ GAIDictionaryBuilder createTimingWithCategory : kMXGoogleAnalyticsStartupCategory
interval : @ ( ( int ) durationMs )
name : kMXGoogleAnalyticsStartupLaunchScreen
label : nil ] build ] ] ;
2017-01-31 23:18:01 +00:00
}
2017-02-21 15:08:10 +00:00
[ launchAnimationContainerView removeFromSuperview ] ;
launchAnimationContainerView = nil ;
2017-01-31 23:18:01 +00:00
}
}
2017-06-20 20:16:54 +00:00
- ( void ) enableCallKit : ( BOOL ) enable forCallManager : ( MXCallManager * ) callManager
{
if ( enable )
{
2017-10-19 08:03:23 +00:00
// Create adapter for Riot
MXCallKitConfiguration * callKitConfiguration = [ [ MXCallKitConfiguration alloc ] init ] ;
callKitConfiguration . iconName = @ "riot_icon_callkit" ;
MXCallKitAdapter * callKitAdapter = [ [ MXCallKitAdapter alloc ] initWithConfiguration : callKitConfiguration ] ;
2017-06-20 20:16:54 +00:00
id < MXCallAudioSessionConfigurator > audioSessionConfigurator ;
2017-11-24 15:28:48 +00:00
# ifdef CALL_STACK _JINGLE
2017-06-20 20:16:54 +00:00
audioSessionConfigurator = [ [ MXJingleCallAudioSessionConfigurator alloc ] init ] ;
# endif
callKitAdapter . audioSessionConfigurator = audioSessionConfigurator ;
callManager . callKitAdapter = callKitAdapter ;
}
else
{
callManager . callKitAdapter = nil ;
}
}
2017-10-18 17:18:14 +00:00
- ( void ) enableLocalNotificationsFromMatrixSession : ( MXSession * ) mxSession
{
// Prepare listener block .
MXOnNotification notificationListenerBlock = ^ ( MXEvent * event , MXRoomState * roomState , MXPushRule * rule ) {
// Ignore this event if the app is not running in background .
if ( [ [ UIApplication sharedApplication ] applicationState ] ! = UIApplicationStateBackground )
{
return ;
}
2017-10-20 12:23:08 +00:00
// Sanity check
if ( event . eventId && event . roomId && rule )
2017-10-18 17:18:14 +00:00
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] enableLocalNotificationsFromMatrixSession: got event %@ to notify" , event . eventId ) ;
2017-10-23 14:58:55 +00:00
// Check whether this event corresponds to a pending push for this session .
NSUInteger index = [ self . incomingPushEventIds [ @ ( mxSession . hash ) ] indexOfObject : event . eventId ] ;
2017-10-20 12:23:08 +00:00
if ( index ! = NSNotFound )
{
// Remove it from the pending list .
2017-10-23 14:58:55 +00:00
[ self . incomingPushEventIds [ @ ( mxSession . hash ) ] removeObjectAtIndex : index ] ;
2017-10-20 12:23:08 +00:00
}
// Add it to the list of the events to notify .
[ eventsToNotify [ @ ( mxSession . hash ) ] addObject : @ { @ "event_id" : event . eventId , @ "room_id" : event . roomId , @ "push_rule" : rule } ] ;
}
else
{
2017-12-18 08:47:48 +00:00
NSLog ( @ "[AppDelegate][Push] enableLocalNotificationsFromMatrixSession: WARNING: wrong event to notify %@ %@ %@" , event , event . roomId , rule ) ;
2017-10-18 17:18:14 +00:00
}
} ;
eventsToNotify [ @ ( mxSession . hash ) ] = [ NSMutableArray array ] ;
[ mxSession . notificationCenter listenToNotifications : notificationListenerBlock ] ;
notificationListenerBlocks [ @ ( mxSession . hash ) ] = notificationListenerBlock ;
}
- ( void ) disableLocalNotificationsFromMatrixSession : ( MXSession * ) mxSession
{
// Stop listening to notification of this session
[ mxSession . notificationCenter removeListener : notificationListenerBlocks [ @ ( mxSession . hash ) ] ] ;
[ notificationListenerBlocks removeObjectForKey : @ ( mxSession . hash ) ] ;
[ eventsToNotify removeObjectForKey : @ ( mxSession . hash ) ] ;
}
2017-03-23 16:48:05 +00:00
# pragma mark -
/ * *
Check the existence of device id .
* /
- ( void ) checkDeviceId : ( MXSession * ) mxSession
{
// In case of the app update for the e2e encryption , the app starts with
// no device id provided by the homeserver .
// Ask the user to login again in order to enable e2e . Ask it once
if ( ! isErrorNotificationSuspended && ! [ [ NSUserDefaults standardUserDefaults ] boolForKey : @ "deviceIdAtStartupChecked" ] )
{
[ [ NSUserDefaults standardUserDefaults ] setBool : YES forKey : @ "deviceIdAtStartupChecked" ] ;
[ [ NSUserDefaults standardUserDefaults ] synchronize ] ;
// Check if there is a device id
if ( ! mxSession . matrixRestClient . credentials . deviceId )
{
NSLog ( @ "WARNING: The user has no device. Prompt for login again" ) ;
NSString * msg = NSLocalizedStringFromTable ( @ "e2e_enabling_on_app_update" , @ "Vector" , nil ) ;
__weak typeof ( self ) weakSelf = self ;
2017-07-14 14:41:25 +00:00
[ _errorNotification dismissViewControllerAnimated : NO completion : nil ] ;
_errorNotification = [ UIAlertController alertControllerWithTitle : nil message : msg preferredStyle : UIAlertControllerStyleAlert ] ;
2017-03-23 16:48:05 +00:00
2017-07-14 14:41:25 +00:00
[ _errorNotification addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "later" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> _errorNotification = nil ;
}
} ] ] ;
2017-03-23 16:48:05 +00:00
2017-07-14 14:41:25 +00:00
[ _errorNotification addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "ok" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> _errorNotification = nil ;
[ self logout ] ;
}
} ] ] ;
2017-03-23 16:48:05 +00:00
// Prompt the user
2017-07-14 14:41:25 +00:00
[ _errorNotification mxk_setAccessibilityIdentifier : @ "AppDelegateErrorAlert" ] ;
2017-03-23 16:48:05 +00:00
[ self showNotificationAlert : _errorNotification ] ;
}
}
}
2015-11-17 23:15:52 +00:00
# pragma mark - Matrix Accounts handling
2015-01-30 08:05:15 +00:00
2015-06-18 13:20:33 +00:00
- ( void ) enableInAppNotificationsForAccount : ( MXKAccount * ) account
2015-06-18 09:19:42 +00:00
{
2015-06-18 13:20:33 +00:00
if ( account . mxSession )
2015-06-18 09:19:42 +00:00
{
2015-06-18 13:20:33 +00:00
if ( account . enableInAppNotifications )
2015-06-18 09:19:42 +00:00
{
2015-06-18 13:20:33 +00:00
// Build MXEvent -> NSString formatter
2015-08-21 18:00:39 +00:00
EventFormatter * eventFormatter = [ [ EventFormatter alloc ] initWithMatrixSession : account . mxSession ] ;
2015-06-18 13:20:33 +00:00
eventFormatter . isForSubtitle = YES ;
2015-06-19 08:31:27 +00:00
[ account listenToNotifications : ^ ( MXEvent * event , MXRoomState * roomState , MXPushRule * rule ) {
// Check conditions to display this notification
2015-11-17 23:15:52 +00:00
if ( ! [ self . visibleRoomId isEqualToString : event . roomId ]
&& ! self . window . rootViewController . presentedViewController )
2015-06-19 08:31:27 +00:00
{
MXKEventFormatterError error ;
NSString * messageText = [ eventFormatter stringFromEvent : event withRoomState : roomState error : & error ] ;
if ( messageText . length && ( error = = MXKEventFormatterErrorNone ) )
{
// Removing existing notification ( if any )
if ( self . mxInAppNotification )
{
2017-07-14 14:41:25 +00:00
[ self . mxInAppNotification dismissViewControllerAnimated : NO completion : nil ] ;
2015-06-19 08:31:27 +00:00
}
// Check whether tweak is required
for ( MXPushRuleAction * ruleAction in rule . actions )
{
if ( ruleAction . actionType = = MXPushRuleActionTypeSetTweak )
{
if ( [ [ ruleAction . parameters valueForKey : @ "set_tweak" ] isEqualToString : @ "sound" ] )
{
// Play system sound ( VoicemailReceived )
AudioServicesPlaySystemSound ( 1002 ) ;
}
}
}
2017-10-16 12:58:38 +00:00
MXRoomSummary * roomSummary = [ account . mxSession roomSummaryWithRoomId : event . roomId ] ;
2017-10-16 12:43:07 +00:00
2015-06-19 08:31:27 +00:00
__weak typeof ( self ) weakSelf = self ;
2017-10-16 12:58:38 +00:00
self . mxInAppNotification = [ UIAlertController alertControllerWithTitle : roomSummary . displayname
2017-07-14 14:41:25 +00:00
message : messageText
preferredStyle : UIAlertControllerStyleAlert ] ;
2015-06-19 08:31:27 +00:00
2017-07-14 14:41:25 +00:00
[ self . mxInAppNotification addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "cancel" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self . mxInAppNotification = nil ;
[ account updateNotificationListenerForRoomId : event . roomId ignore : YES ] ;
}
} ] ] ;
[ self . mxInAppNotification addAction : [ UIAlertAction actionWithTitle : NSLocalizedStringFromTable ( @ "view" , @ "Vector" , nil )
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self . mxInAppNotification = nil ;
// Show the room
[ self showRoom : event . roomId andEventId : nil withMatrixSession : account . mxSession ] ;
}
} ] ] ;
[ self . window . rootViewController presentViewController : self . mxInAppNotification animated : YES completion : nil ] ;
2015-06-19 08:31:27 +00:00
}
}
} ] ;
2015-06-18 13:20:33 +00:00
}
else
{
[ account removeNotificationListener ] ;
2015-04-30 14:19:12 +00:00
}
}
2015-06-18 09:19:42 +00:00
if ( self . mxInAppNotification )
{
2017-07-14 14:41:25 +00:00
[ self . mxInAppNotification dismissViewControllerAnimated : NO completion : nil ] ;
2015-04-30 14:19:12 +00:00
self . mxInAppNotification = nil ;
}
}
2015-06-18 09:19:42 +00:00
- ( void ) selectMatrixAccount : ( void ( ^ ) ( MXKAccount * selectedAccount ) ) onSelection
{
2015-06-19 08:31:27 +00:00
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . activeAccounts ;
2015-05-28 16:31:08 +00:00
2015-06-18 09:19:42 +00:00
if ( mxAccounts . count = = 1 )
{
if ( onSelection )
{
2015-05-28 16:31:08 +00:00
onSelection ( mxAccounts . firstObject ) ;
}
2015-06-18 09:19:42 +00:00
}
else if ( mxAccounts . count > 1 )
{
2017-07-14 14:41:25 +00:00
[ accountPicker dismissViewControllerAnimated : NO completion : nil ] ;
2015-05-28 16:31:08 +00:00
2017-07-14 14:41:25 +00:00
accountPicker = [ UIAlertController alertControllerWithTitle : [ NSBundle mxk_localizedStringForKey : @ "select_account" ] message : nil preferredStyle : UIAlertControllerStyleActionSheet ] ;
2015-05-28 16:31:08 +00:00
__weak typeof ( self ) weakSelf = self ;
2015-06-18 09:19:42 +00:00
for ( MXKAccount * account in mxAccounts )
{
2017-07-14 14:41:25 +00:00
[ accountPicker addAction : [ UIAlertAction actionWithTitle : account . mxCredentials . userId
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> accountPicker = nil ;
if ( onSelection )
{
onSelection ( account ) ;
}
}
} ] ] ;
2015-05-28 16:31:08 +00:00
}
2017-07-14 14:41:25 +00:00
[ accountPicker addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "cancel" ]
style : UIAlertActionStyleDefault
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
2016-04-07 15:38:19 +00:00
- ( void ) showRoom : ( NSString * ) roomId andEventId : ( NSString * ) eventId withMatrixSession : ( MXSession * ) mxSession
2015-08-26 17:04:30 +00:00
{
2015-12-09 17:12:42 +00:00
[ self restoreInitialDisplay : ^ {
2016-03-23 13:45:16 +00:00
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 )
2017-03-23 16:48:05 +00:00
[ _masterTabBarController selectRoomWithId : roomId andEventId : eventId inMatrixSession : mxSession ] ;
2016-03-23 13:45:16 +00:00
2015-12-09 17:12:42 +00:00
} ] ;
2015-08-26 17:04:30 +00:00
}
2016-04-15 07:43:56 +00:00
- ( void ) showRoomPreview : ( RoomPreviewData * ) roomPreviewData
{
[ self restoreInitialDisplay : ^ {
2017-03-23 16:48:05 +00:00
[ _masterTabBarController showRoomPreview : roomPreviewData ] ;
2016-04-15 07:43:56 +00:00
} ] ;
}
2015-11-17 23:15:52 +00:00
- ( void ) setVisibleRoomId : ( NSString * ) roomId
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
if ( roomId )
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
// Enable inApp notification for this room in all existing accounts .
NSArray * mxAccounts = [ MXKAccountManager sharedManager ] . accounts ;
for ( MXKAccount * account in mxAccounts )
{
[ account updateNotificationListenerForRoomId : roomId ignore : NO ] ;
}
2014-10-02 15:02:47 +00:00
}
2015-11-17 23:15:52 +00:00
_visibleRoomId = roomId ;
2014-10-02 15:02:47 +00:00
}
2016-11-02 13:32:02 +00:00
- ( void ) createDirectChatWithUserId : ( NSString * ) userId completion : ( void ( ^ ) ( void ) ) completion
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
// Handle here potential multiple accounts
2016-03-21 17:38:44 +00:00
[ self selectMatrixAccount : ^ ( MXKAccount * selectedAccount ) {
MXSession * mxSession = selectedAccount . mxSession ;
if ( mxSession )
{
2016-11-02 13:32:02 +00:00
// Create a new room by inviting the other user only if it is defined and not oneself
NSArray * invite = ( ( userId && ! [ mxSession . myUser . userId isEqualToString : userId ] ) ? @ [ userId ] : nil ) ;
2016-03-21 17:38:44 +00:00
2016-11-02 13:32:02 +00:00
[ mxSession createRoom : nil
visibility : kMXRoomDirectoryVisibilityPrivate
roomAlias : nil
topic : nil
invite : invite
invite3PID : nil
isDirect : ( invite . count ! = 0 )
2016-11-02 15:35:45 +00:00
preset : kMXRoomPresetTrustedPrivateChat
2016-11-02 13:32:02 +00:00
success : ^ ( MXRoom * room ) {
// Open created room
[ self showRoom : room . state . roomId andEventId : nil withMatrixSession : mxSession ] ;
if ( completion )
{
completion ( ) ;
2016-03-21 17:38:44 +00:00
}
2016-11-02 13:32:02 +00:00
}
failure : ^ ( NSError * error ) {
NSLog ( @ "[AppDelegate] Create direct chat failed" ) ;
// Alert user
[ self showErrorAsAlert : error ] ;
if ( completion )
{
completion ( ) ;
}
} ] ;
2016-03-21 17:38:44 +00:00
}
else if ( completion )
{
completion ( ) ;
}
} ] ;
2015-01-05 15:53:41 +00:00
}
2016-11-02 16:54:28 +00:00
- ( void ) startDirectChatWithUserId : ( NSString * ) userId completion : ( void ( ^ ) ( void ) ) completion
{
// Handle here potential multiple accounts
[ self selectMatrixAccount : ^ ( MXKAccount * selectedAccount ) {
MXSession * mxSession = selectedAccount . mxSession ;
if ( mxSession )
{
2016-11-03 17:12:08 +00:00
MXRoom * directRoom = [ mxSession directJoinedRoomWithUserId : userId ] ;
2016-11-02 16:54:28 +00:00
// if the room exists
2016-11-03 17:12:08 +00:00
if ( directRoom )
2016-11-02 16:54:28 +00:00
{
// open it
2016-11-03 17:12:08 +00:00
[ self showRoom : directRoom . roomId andEventId : nil withMatrixSession : mxSession ] ;
2016-11-02 16:54:28 +00:00
if ( completion )
{
completion ( ) ;
}
}
else
{
[ self createDirectChatWithUserId : userId completion : completion ] ;
}
}
else if ( completion )
{
completion ( ) ;
}
} ] ;
}
2017-01-17 22:13:30 +00:00
# pragma mark - Contacts handling
2017-07-25 14:08:24 +00:00
- ( void ) showContact : ( MXKContact * ) contact
{
[ self restoreInitialDisplay : ^ {
[ self . masterTabBarController selectContact : contact ] ;
} ] ;
}
2017-01-17 22:13:30 +00:00
- ( void ) refreshLocalContacts
{
// Check whether the application is allowed to access the local contacts .
if ( ABAddressBookGetAuthorizationStatus ( ) = = kABAuthorizationStatusAuthorized )
{
// Check the user permission for syncing local contacts . This permission was handled independently on previous application version .
if ( ! [ MXKAppSettings standardAppSettings ] . syncLocalContacts )
{
// Check whether it was not requested yet .
if ( ! [ MXKAppSettings standardAppSettings ] . syncLocalContactsPermissionRequested )
{
[ MXKAppSettings standardAppSettings ] . syncLocalContactsPermissionRequested = YES ;
UIViewController * viewController = self . window . rootViewController . presentedViewController ;
if ( ! viewController )
{
viewController = self . window . rootViewController ;
}
[ MXKContactManager requestUserConfirmationForLocalContactsSyncInViewController : viewController completionHandler : ^ ( BOOL granted ) {
if ( granted )
{
// Allow local contacts sync in order to discover matrix users .
[ MXKAppSettings standardAppSettings ] . syncLocalContacts = YES ;
}
} ] ;
}
}
2017-01-23 17:40:30 +00:00
// Refresh the local contacts list .
[ [ MXKContactManager sharedManager ] refreshLocalContacts ] ;
2017-01-17 22:13:30 +00:00
}
}
2017-12-31 15:24:47 +00:00
# pragma mark - Matrix Groups handling
- ( void ) showGroup : ( MXGroup * ) group withMatrixSession : ( MXSession * ) mxSession
{
[ self restoreInitialDisplay : ^ {
// Select group to display its details ( dispatch this action in order to let TabBarController end its refresh )
[ _masterTabBarController selectGroup : group inMatrixSession : mxSession ] ;
} ] ;
}
2015-05-11 21:11:23 +00:00
# pragma mark - MXKCallViewControllerDelegate
2016-08-19 15:43:29 +00:00
- ( void ) dismissCallViewController : ( MXKCallViewController * ) callViewController completion : ( void ( ^ ) ( ) ) completion
2015-06-18 09:19:42 +00:00
{
2016-11-24 14:09:04 +00:00
if ( currentCallViewController && callViewController = = currentCallViewController )
2015-06-18 09:19:42 +00:00
{
2017-09-04 14:26:41 +00:00
if ( callViewController . isBeingPresented )
2016-11-24 14:09:04 +00:00
{
// Here the presentation of the call view controller is in progress
// Postpone the dismiss
dispatch_after ( dispatch_time ( DISPATCH_TIME _NOW , ( int64_t ) ( 0.3 * NSEC_PER _SEC ) ) , dispatch_get _main _queue ( ) , ^ {
[ self dismissCallViewController : callViewController completion : completion ] ;
} ) ;
}
// Check whether the call view controller is actually presented
else if ( callViewController . presentingViewController )
2015-06-18 09:19:42 +00:00
{
2015-05-12 08:53:47 +00:00
BOOL callIsEnded = ( callViewController . mxCall . state = = MXCallStateEnded ) ;
NSLog ( @ "Call view controller is dismissed (%d)" , callIsEnded ) ;
[ callViewController dismissViewControllerAnimated : YES completion : ^ {
2015-06-18 09:19:42 +00:00
if ( ! callIsEnded )
{
2016-09-02 20:21:02 +00:00
NSString * btnTitle = [ NSString stringWithFormat : NSLocalizedStringFromTable ( @ "active_call_details" , @ "Vector" , nil ) , callViewController . callerNameLabel . text ] ;
[ self addCallStatusBar : btnTitle ] ;
2015-05-12 08:53:47 +00:00
}
2016-08-19 15:43:29 +00:00
if ( completion )
{
completion ( ) ;
}
2016-11-24 14:09:04 +00:00
2015-05-12 08:53:47 +00:00
} ] ;
2015-06-18 09:19:42 +00:00
if ( callIsEnded )
{
2015-05-12 08:53:47 +00:00
[ self removeCallStatusBar ] ;
// Release properly
2017-06-03 23:39:00 +00:00
[ currentCallViewController destroy ] ;
2015-05-12 08:53:47 +00:00
currentCallViewController = nil ;
}
2016-08-19 15:43:29 +00:00
}
2017-10-27 13:35:43 +00:00
else if ( _callStatusBarWindow )
2015-06-18 09:19:42 +00:00
{
2016-11-24 14:09:04 +00:00
// Here the call view controller was not presented .
NSLog ( @ "Call view controller was not presented" ) ;
// Workaround to manage the "back to call" banner : present temporarily the call screen .
// This will correctly manage the navigation bar layout .
2017-10-18 08:19:05 +00:00
[ self presentCallViewController : YES completion : ^ {
2016-11-24 14:09:04 +00:00
[ self dismissCallViewController : currentCallViewController completion : completion ] ;
} ] ;
2015-05-12 08:53:47 +00:00
}
2015-05-11 21:11:23 +00:00
}
}
2017-08-11 12:18:10 +00:00
# pragma mark - Jitsi call
- ( void ) displayJitsiViewControllerWithWidget : ( Widget * ) jitsiWidget andVideo : ( BOOL ) video
{
if ( ! _jitsiViewController && ! currentCallViewController )
{
_jitsiViewController = [ JitsiViewController jitsiViewController ] ;
2018-01-04 10:49:39 +00:00
[ _jitsiViewController openWidget : jitsiWidget withVideo : video success : ^ {
2017-08-11 12:18:10 +00:00
_jitsiViewController . delegate = self ;
[ self presentJitsiViewController : nil ] ;
2018-01-04 10:49:39 +00:00
} failure : ^ ( NSError * error ) {
2017-08-11 12:18:10 +00:00
_jitsiViewController = nil ;
2017-08-18 09:40:56 +00:00
2018-01-04 10:49:39 +00:00
NSError * theError = [ NSError errorWithDomain : @ ""
code : 0
userInfo : @ {
2017-08-18 09:40:56 +00:00
NSLocalizedDescriptionKey : NSLocalizedStringFromTable ( @ "call_jitsi_error" , @ "Vector" , nil )
} ] ;
2018-01-04 10:49:39 +00:00
[ self showErrorAsAlert : theError ] ;
} ] ;
2017-08-11 12:18:10 +00:00
}
else
{
2017-08-18 09:40:56 +00:00
NSError * error = [ NSError errorWithDomain : @ ""
code : 0
userInfo : @ {
NSLocalizedDescriptionKey : NSLocalizedStringFromTable ( @ "call_already_displayed" , @ "Vector" , nil )
} ] ;
[ self showErrorAsAlert : error ] ;
2017-08-11 12:18:10 +00:00
}
}
- ( void ) presentJitsiViewController : ( void ( ^ ) ( ) ) completion
{
[ self removeCallStatusBar ] ;
if ( _jitsiViewController )
{
if ( self . window . rootViewController . presentedViewController )
{
[ self . window . rootViewController . presentedViewController presentViewController : _jitsiViewController animated : YES completion : completion ] ;
}
else
{
[ self . window . rootViewController presentViewController : _jitsiViewController animated : YES completion : completion ] ;
}
}
}
- ( void ) jitsiViewController : ( JitsiViewController * ) jitsiViewController dismissViewJitsiController : ( void ( ^ ) ( ) ) completion
{
if ( jitsiViewController = = _jitsiViewController )
{
[ _jitsiViewController dismissViewControllerAnimated : YES completion : completion ] ;
_jitsiViewController = nil ;
2017-08-11 12:59:05 +00:00
[ self removeCallStatusBar ] ;
2017-08-11 12:18:10 +00:00
}
}
- ( void ) jitsiViewController : ( JitsiViewController * ) jitsiViewController goBackToApp : ( void ( ^ ) ( ) ) completion
{
if ( jitsiViewController = = _jitsiViewController )
{
[ _jitsiViewController dismissViewControllerAnimated : YES completion : ^ {
MXRoom * room = [ _jitsiViewController . widget . mxSession roomWithRoomId : _jitsiViewController . widget . roomId ] ;
2017-09-14 14:00:50 +00:00
NSString * btnTitle = [ NSString stringWithFormat : NSLocalizedStringFromTable ( @ "active_call_details" , @ "Vector" , nil ) , room . summary . displayname ] ;
2017-08-11 12:18:10 +00:00
[ self addCallStatusBar : btnTitle ] ;
if ( completion )
{
completion ( ) ;
}
} ] ;
}
}
2015-05-11 21:11:23 +00:00
# pragma mark - Call status handling
2016-09-02 20:21:02 +00:00
- ( void ) addCallStatusBar : ( NSString * ) buttonTitle
2015-06-18 09:19:42 +00:00
{
2015-05-11 21:11:23 +00:00
// Add a call status bar
2016-09-02 20:21:02 +00:00
CGSize topBarSize = CGSizeMake ( [ [ UIScreen mainScreen ] bounds ] . size . width , CALL_STATUS _BAR _HEIGHT ) ;
2015-05-11 21:11:23 +00:00
2016-09-02 20:21:02 +00:00
_callStatusBarWindow = [ [ UIWindow alloc ] initWithFrame : CGRectMake ( 0 , 0 , topBarSize . width , topBarSize . height ) ] ;
_callStatusBarWindow . windowLevel = UIWindowLevelStatusBar ;
2015-05-11 21:11:23 +00:00
// Create statusBarButton
2016-09-02 20:21:02 +00:00
_callStatusBarButton = [ UIButton buttonWithType : UIButtonTypeCustom ] ;
_callStatusBarButton . frame = CGRectMake ( 0 , 0 , topBarSize . width , topBarSize . height ) ;
2015-05-11 21:11:23 +00:00
2016-09-02 20:21:02 +00:00
[ _callStatusBarButton setTitle : buttonTitle forState : UIControlStateNormal ] ;
[ _callStatusBarButton setTitle : buttonTitle forState : UIControlStateHighlighted ] ;
2017-08-11 14:56:09 +00:00
_callStatusBarButton . titleLabel . textColor = kRiotPrimaryBgColor ;
2016-09-02 20:21:02 +00:00
if ( [ UIFont respondsToSelector : @ selector ( systemFontOfSize : weight : ) ] )
{
_callStatusBarButton . titleLabel . font = [ UIFont systemFontOfSize : 17 weight : UIFontWeightMedium ] ;
}
else
{
_callStatusBarButton . titleLabel . font = [ UIFont boldSystemFontOfSize : 17 ] ;
}
2015-05-11 21:11:23 +00:00
2017-03-08 15:14:41 +00:00
[ _callStatusBarButton setBackgroundColor : kRiotColorGreen ] ;
2017-08-11 12:18:10 +00:00
[ _callStatusBarButton addTarget : self action : @ selector ( onCallStatusBarButtonPressed ) forControlEvents : UIControlEventTouchUpInside ] ;
2015-05-11 21:11:23 +00:00
// Place button into the new window
2016-09-02 20:21:02 +00:00
[ _callStatusBarButton setTranslatesAutoresizingMaskIntoConstraints : NO ] ;
[ _callStatusBarWindow addSubview : _callStatusBarButton ] ;
// Force callStatusBarButton to fill the window ( to handle auto - layout in case of screen rotation )
NSLayoutConstraint * widthConstraint = [ NSLayoutConstraint constraintWithItem : _callStatusBarButton
2017-07-14 14:41:25 +00:00
attribute : NSLayoutAttributeWidth
relatedBy : NSLayoutRelationEqual
toItem : _callStatusBarWindow
attribute : NSLayoutAttributeWidth
multiplier : 1.0
constant : 0 ] ;
2016-09-02 20:21:02 +00:00
NSLayoutConstraint * heightConstraint = [ NSLayoutConstraint constraintWithItem : _callStatusBarButton
attribute : NSLayoutAttributeHeight
relatedBy : NSLayoutRelationEqual
toItem : _callStatusBarWindow
attribute : NSLayoutAttributeHeight
multiplier : 1.0
constant : 0 ] ;
[ NSLayoutConstraint activateConstraints : @ [ widthConstraint , heightConstraint ] ] ;
_callStatusBarWindow . hidden = NO ;
2015-05-11 21:11:23 +00:00
[ self statusBarDidChangeFrame ] ;
// We need to listen to the system status bar size change events to refresh the root controller frame .
// Else the navigation bar position will be wrong .
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( statusBarDidChangeFrame )
name : UIApplicationDidChangeStatusBarFrameNotification
object : nil ] ;
}
2015-06-18 09:19:42 +00:00
- ( void ) removeCallStatusBar
{
2016-09-02 20:21:02 +00:00
if ( _callStatusBarWindow )
2015-06-18 09:19:42 +00:00
{
2016-09-02 20:21:02 +00:00
// No more need to listen to system status bar changes
[ [ NSNotificationCenter defaultCenter ] removeObserver : self name : UIApplicationDidChangeStatusBarFrameNotification object : nil ] ;
2015-05-11 21:11:23 +00:00
// Hide & destroy it
2016-09-02 20:21:02 +00:00
_callStatusBarWindow . hidden = YES ;
[ _callStatusBarButton removeFromSuperview ] ;
_callStatusBarButton = nil ;
_callStatusBarWindow = nil ;
2015-05-11 21:11:23 +00:00
2016-09-02 20:21:02 +00:00
[ self statusBarDidChangeFrame ] ;
2015-05-11 21:11:23 +00:00
}
}
2017-08-11 12:18:10 +00:00
- ( void ) onCallStatusBarButtonPressed
2016-11-24 14:09:04 +00:00
{
2017-08-11 12:18:10 +00:00
if ( currentCallViewController )
{
2017-10-18 08:19:05 +00:00
[ self presentCallViewController : YES completion : nil ] ;
2017-08-11 12:18:10 +00:00
}
else if ( _jitsiViewController )
{
[ self presentJitsiViewController : nil ] ;
}
2016-11-24 14:09:04 +00:00
}
2017-10-18 08:19:05 +00:00
- ( void ) presentCallViewController : ( BOOL ) animated completion : ( void ( ^ ) ( ) ) completion
2015-06-18 09:19:42 +00:00
{
2015-05-11 21:11:23 +00:00
[ self removeCallStatusBar ] ;
2017-07-14 14:41:25 +00:00
2016-09-06 12:21:11 +00:00
if ( currentCallViewController )
{
2016-09-06 15:20:16 +00:00
if ( self . window . rootViewController . presentedViewController )
{
2017-10-18 08:19:05 +00:00
[ self . window . rootViewController . presentedViewController presentViewController : currentCallViewController animated : animated completion : completion ] ;
2016-09-06 15:20:16 +00:00
}
else
{
2017-10-18 08:19:05 +00:00
[ self . window . rootViewController presentViewController : currentCallViewController animated : animated completion : completion ] ;
2016-09-06 15:20:16 +00:00
}
2016-09-06 12:21:11 +00:00
}
2015-05-11 21:11:23 +00:00
}
2015-06-18 09:19:42 +00:00
- ( void ) statusBarDidChangeFrame
{
2015-05-11 21:11:23 +00:00
UIApplication * app = [ UIApplication sharedApplication ] ;
UIViewController * rootController = app . keyWindow . rootViewController ;
// Refresh the root view controller frame
2016-09-02 20:21:02 +00:00
CGRect rootControllerFrame = [ [ UIScreen mainScreen ] bounds ] ;
if ( _callStatusBarWindow )
2015-06-18 09:19:42 +00:00
{
2016-09-02 20:21:02 +00:00
UIInterfaceOrientation statusBarOrientation = [ UIApplication sharedApplication ] . statusBarOrientation ;
2015-05-11 21:11:23 +00:00
2016-09-02 20:21:02 +00:00
switch ( statusBarOrientation )
{
case UIInterfaceOrientationLandscapeLeft :
{
_callStatusBarWindow . frame = CGRectMake ( - rootControllerFrame . size . width / 2 , - CALL_STATUS _BAR _HEIGHT / 2 , rootControllerFrame . size . width , CALL_STATUS _BAR _HEIGHT ) ;
_callStatusBarWindow . transform = CGAffineTransformMake ( 0 , -1 , 1 , 0 , CALL_STATUS _BAR _HEIGHT / 2 , rootControllerFrame . size . width / 2 ) ;
break ;
}
case UIInterfaceOrientationLandscapeRight :
{
_callStatusBarWindow . frame = CGRectMake ( - rootControllerFrame . size . width / 2 , - CALL_STATUS _BAR _HEIGHT / 2 , rootControllerFrame . size . width , CALL_STATUS _BAR _HEIGHT ) ;
_callStatusBarWindow . transform = CGAffineTransformMake ( 0 , 1 , -1 , 0 , rootControllerFrame . size . height - CALL_STATUS _BAR _HEIGHT / 2 , rootControllerFrame . size . width / 2 ) ;
break ;
}
default :
{
_callStatusBarWindow . transform = CGAffineTransformIdentity ;
_callStatusBarWindow . frame = CGRectMake ( 0 , 0 , rootControllerFrame . size . width , CALL_STATUS _BAR _HEIGHT ) ;
break ;
}
}
// Apply the vertical offset due to call status bar
rootControllerFrame . origin . y = CALL_STATUS _BAR _HEIGHT ;
rootControllerFrame . size . height - = CALL_STATUS _BAR _HEIGHT ;
2015-05-11 21:11:23 +00:00
}
2016-09-02 20:21:02 +00:00
rootController . view . frame = rootControllerFrame ;
2016-09-06 15:20:16 +00:00
if ( rootController . presentedViewController )
{
rootController . presentedViewController . view . frame = rootControllerFrame ;
}
2015-05-11 21:11:23 +00:00
[ rootController . view setNeedsLayout ] ;
}
2015-11-17 23:15:52 +00:00
# pragma mark - SplitViewController delegate
2016-06-21 19:47:20 +00:00
- ( nullable UIViewController * ) splitViewController : ( UISplitViewController * ) splitViewController separateSecondaryViewControllerFromPrimaryViewController : ( UIViewController * ) primaryViewController
{
2017-03-28 15:26:50 +00:00
// Return the top view controller of the master navigation controller , if it is a navigation controller itself .
2017-03-23 16:48:05 +00:00
UIViewController * topViewController = _masterNavigationController . topViewController ;
2017-03-28 15:26:50 +00:00
if ( [ topViewController isKindOfClass : UINavigationController . class ] )
2016-06-21 19:47:20 +00:00
{
2017-03-28 15:26:50 +00:00
return topViewController ;
2016-06-21 19:47:20 +00:00
}
2017-04-21 14:28:57 +00:00
// Else return the default empty details view controller from the storyboard .
// Be sure that the primary is then visible too .
if ( splitViewController . displayMode = = UISplitViewControllerDisplayModePrimaryHidden )
2016-06-21 19:47:20 +00:00
{
2017-04-21 14:28:57 +00:00
splitViewController . preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible ;
2016-06-21 19:47:20 +00:00
}
2017-03-28 15:26:50 +00:00
UIStoryboard * storyboard = [ UIStoryboard storyboardWithName : @ "Main" bundle : [ NSBundle mainBundle ] ] ;
2017-08-14 13:57:23 +00:00
UIViewController * emptyDetailsViewController = [ storyboard instantiateViewControllerWithIdentifier : @ "EmptyDetailsViewControllerStoryboardId" ] ;
emptyDetailsViewController . view . backgroundColor = kRiotPrimaryBgColor ;
return emptyDetailsViewController ;
2016-06-21 19:47:20 +00:00
}
2015-11-17 23:15:52 +00:00
- ( BOOL ) splitViewController : ( UISplitViewController * ) splitViewController collapseSecondaryViewController : ( UIViewController * ) secondaryViewController ontoPrimaryViewController : ( UIViewController * ) primaryViewController
{
2017-12-12 15:03:26 +00:00
if ( ! self . masterTabBarController . currentRoomViewController && ! self . masterTabBarController . currentContactDetailViewController && ! self . masterTabBarController . currentGroupDetailViewController )
2015-11-17 23:15:52 +00:00
{
// Return YES to indicate that we have handled the collapse by doing nothing ; the secondary controller will be discarded .
return YES ;
}
else
{
return NO ;
}
}
- ( BOOL ) splitViewController : ( UISplitViewController * ) svc shouldHideViewController : ( UIViewController * ) vc inOrientation : ( UIInterfaceOrientation ) orientation
{
// oniPad devices , force to display the primary and the secondary viewcontroller
// to avoid empty room View Controller in portrait orientation
// else , the user cannot select a room
return NO ;
}
2016-05-19 15:22:29 +00:00
# pragma mark - Status Bar Tap handling
- ( void ) touchesBegan : ( NSSet * ) touches withEvent : ( UIEvent * ) event
{
[ super touchesBegan : touches withEvent : event ] ;
UITouch * touch = [ touches anyObject ] ;
CGPoint point = [ touch locationInView : self . window ] ;
CGRect statusBarFrame = [ UIApplication sharedApplication ] . statusBarFrame ;
if ( CGRectContainsPoint ( statusBarFrame , point ) )
{
[ [ NSNotificationCenter defaultCenter ] postNotificationName : kAppDelegateDidTapStatusBarNotification object : nil ] ;
}
}
2016-05-20 12:31:23 +00:00
# pragma mark - No call support
/ * *
Display a "Call not supported" alert when the session receives a call invitation .
2017-07-14 14:41:25 +00:00
2016-05-20 12:31:23 +00:00
@ param mxSession the session to spy
* /
- ( void ) enableNoVoIPOnMatrixSession : ( MXSession * ) mxSession
{
// Listen to call events
2016-05-20 13:45:29 +00:00
callEventsListeners [ @ ( mxSession . hash ) ] =
2016-05-20 12:31:23 +00:00
[ mxSession listenToEventsOfTypes : @ [
kMXEventTypeStringCallInvite ,
kMXEventTypeStringCallCandidates ,
kMXEventTypeStringCallAnswer ,
kMXEventTypeStringCallHangup
]
2017-07-14 14:41:25 +00:00
onEvent : ^ ( MXEvent * event , MXTimelineDirection direction , id customObject ) {
if ( MXTimelineDirectionForwards = = direction )
{
switch ( event . eventType )
{
case MXEventTypeCallInvite :
{
if ( noCallSupportAlert )
{
[ noCallSupportAlert dismissViewControllerAnimated : NO completion : nil ] ;
}
MXCallInviteEventContent * callInviteEventContent = [ MXCallInviteEventContent modelFromJSON : event . content ] ;
// Sanity and invite expiration checks
if ( ! callInviteEventContent || event . age >= callInviteEventContent . lifetime )
{
return ;
}
MXUser * caller = [ mxSession userWithUserId : event . sender ] ;
NSString * callerDisplayname = caller . displayname ;
if ( ! callerDisplayname . length )
{
callerDisplayname = event . sender ;
}
NSString * appDisplayName = [ [ [ NSBundle mainBundle ] infoDictionary ] objectForKey : @ "CFBundleDisplayName" ] ;
NSString * message = [ NSString stringWithFormat : NSLocalizedStringFromTable ( @ "no_voip" , @ "Vector" , nil ) , callerDisplayname , appDisplayName ] ;
noCallSupportAlert = [ UIAlertController alertControllerWithTitle : NSLocalizedStringFromTable ( @ "no_voip_title" , @ "Vector" , nil )
message : message
preferredStyle : UIAlertControllerStyleAlert ] ;
__weak typeof ( self ) weakSelf = self ;
[ noCallSupportAlert addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "ignore" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> noCallSupportAlert = nil ;
}
} ] ] ;
[ noCallSupportAlert addAction : [ UIAlertAction actionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "reject_call" ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
// Reject the call by sending the hangup event
NSDictionary * content = @ {
@ "call_id" : callInviteEventContent . callId ,
@ "version" : @ ( 0 )
} ;
[ mxSession . matrixRestClient sendEventToRoom : event . roomId eventType : kMXEventTypeStringCallHangup content : content success : nil failure : ^ ( NSError * error ) {
NSLog ( @ "[AppDelegate] enableNoVoIPOnMatrixSession: ERROR: Cannot send m.call.hangup event." ) ;
} ] ;
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> noCallSupportAlert = nil ;
}
} ] ] ;
[ self showNotificationAlert : noCallSupportAlert ] ;
break ;
}
case MXEventTypeCallAnswer :
case MXEventTypeCallHangup :
// The call has ended . The alert is no more needed .
if ( noCallSupportAlert )
{
[ noCallSupportAlert dismissViewControllerAnimated : YES completion : nil ] ;
noCallSupportAlert = nil ;
}
break ;
default :
break ;
}
}
} ] ;
2016-05-20 12:31:23 +00:00
}
- ( void ) disableNoVoIPOnMatrixSession : ( MXSession * ) mxSession
{
// Stop listening to the call events of this session
2016-05-20 13:45:29 +00:00
[ mxSession removeListener : callEventsListeners [ @ ( mxSession . hash ) ] ] ;
[ callEventsListeners removeObjectForKey : @ ( mxSession . hash ) ] ;
2016-05-20 12:31:23 +00:00
}
2017-11-14 17:21:01 +00:00
# pragma mark - Incoming room key requests handling
- ( void ) enableRoomKeyRequestObserver : ( MXSession * ) mxSession
{
roomKeyRequestObserver =
[ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXCryptoRoomKeyRequestNotification
object : mxSession . crypto
queue : [ NSOperationQueue mainQueue ]
usingBlock : ^ ( NSNotification * notif )
{
[ self checkPendingRoomKeyRequestsInSession : mxSession ] ;
} ] ;
roomKeyRequestCancellationObserver =
[ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXCryptoRoomKeyRequestCancellationNotification
object : mxSession . crypto
queue : [ NSOperationQueue mainQueue ]
usingBlock : ^ ( NSNotification * notif )
{
[ self checkPendingRoomKeyRequestsInSession : mxSession ] ;
} ] ;
}
- ( void ) disableRoomKeyRequestObserver : ( MXSession * ) mxSession
{
if ( roomKeyRequestObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : roomKeyRequestObserver ] ;
roomKeyRequestObserver = nil ;
}
if ( roomKeyRequestCancellationObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : roomKeyRequestCancellationObserver ] ;
roomKeyRequestCancellationObserver = nil ;
}
}
// Check if a key share dialog must be displayed for the given session
- ( void ) checkPendingRoomKeyRequestsInSession : ( MXSession * ) mxSession
{
if ( [ UIApplication sharedApplication ] . applicationState ! = UIApplicationStateActive )
{
NSLog ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession called while the app is not active. Ignore it." ) ;
return ;
}
[ mxSession . crypto pendingKeyRequests : ^ ( MXUsersDevicesMap < NSArray < MXIncomingRoomKeyRequest * > * > * pendingKeyRequests ) {
NSLog ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession: pendingKeyRequests.count: %@. Already displayed: %@" ,
@ ( pendingKeyRequests . count ) ,
roomKeyRequestViewController ? @ "YES" : @ "NO" ) ;
if ( roomKeyRequestViewController )
{
// Check if the current RoomKeyRequestViewController is still valid
MXSession * currentMXSession = roomKeyRequestViewController . mxSession ;
NSString * currentUser = roomKeyRequestViewController . device . userId ;
NSString * currentDevice = roomKeyRequestViewController . device . deviceId ;
NSArray < MXIncomingRoomKeyRequest * > * currentPendingRequest = [ pendingKeyRequests objectForDevice : currentDevice forUser : currentUser ] ;
if ( currentMXSession = = mxSession && currentPendingRequest . count = = 0 )
{
NSLog ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession: Cancel current dialog" ) ;
// The key request has been probably cancelled , remove the popup
[ roomKeyRequestViewController hide ] ;
roomKeyRequestViewController = nil ;
}
}
if ( ! roomKeyRequestViewController && pendingKeyRequests . count )
{
// Pick the first coming user / device pair
2017-12-05 14:47:39 +00:00
NSString * userId = pendingKeyRequests . userIds . firstObject ;
NSString * deviceId = [ pendingKeyRequests deviceIdsForUser : userId ] . firstObject ;
2017-11-14 17:21:01 +00:00
2017-12-05 14:47:39 +00:00
// Give the client a chance to refresh the device list
// Note : react - sdk does not do a force download
[ mxSession . crypto downloadKeys : @ [ userId ] success : ^ ( MXUsersDevicesMap < MXDeviceInfo * > * usersDevicesInfoMap ) {
2017-11-14 17:21:01 +00:00
2017-12-05 14:47:39 +00:00
MXDeviceInfo * deviceInfo = [ usersDevicesInfoMap objectForDevice : deviceId forUser : userId ] ;
if ( deviceInfo )
{
BOOL wasNewDevice = ( deviceInfo . verified = = MXDeviceUnknown ) ;
2017-11-14 17:21:01 +00:00
2017-12-05 14:47:39 +00:00
void ( ^ openDialog ) ( ) = ^ void ( )
{
NSLog ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession: Open dialog for %@" , deviceInfo ) ;
2017-11-14 17:21:01 +00:00
2017-12-05 14:47:39 +00:00
roomKeyRequestViewController = [ [ RoomKeyRequestViewController alloc ] initWithDeviceInfo : deviceInfo wasNewDevice : wasNewDevice andMatrixSession : mxSession onComplete : ^ {
2017-11-14 17:21:01 +00:00
2017-12-05 14:47:39 +00:00
roomKeyRequestViewController = nil ;
// Check next pending key request , if any
[ self checkPendingRoomKeyRequests ] ;
} ] ;
[ roomKeyRequestViewController show ] ;
} ;
2017-11-14 17:21:01 +00:00
2017-12-05 14:47:39 +00:00
// If the device was new before , it ' s not any more .
if ( wasNewDevice )
{
[ mxSession . crypto setDeviceVerification : MXDeviceUnverified forDevice : deviceId ofUser : userId success : openDialog failure : nil ] ;
}
else
{
openDialog ( ) ;
}
}
else
{
NSLog ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession: No details found for device %@:%@" , userId , deviceId ) ;
// Ignore this device to avoid to loop on it
[ mxSession . crypto ignoreAllPendingKeyRequestsFromUser : userId andDevice : deviceId onComplete : ^ {
// And check next requests
[ self checkPendingRoomKeyRequests ] ;
} ] ;
}
} failure : ^ ( NSError * error ) {
// Retry later
NSLog ( @ "[AppDelegate] checkPendingRoomKeyRequestsInSession: Failed to download device keys. Retry" ) ;
[ self checkPendingRoomKeyRequests ] ;
2017-11-14 17:21:01 +00:00
} ] ;
}
} ] ;
}
// Check all opened MXSessions for key share dialog
- ( void ) checkPendingRoomKeyRequests
{
for ( MXSession * mxSession in mxSessionArray )
{
[ self checkPendingRoomKeyRequestsInSession : mxSession ] ;
}
}
2014-10-02 15:02:47 +00:00
@ end