2014-10-02 15:02:47 +00:00
/ *
Copyright 2014 OpenMarket Ltd
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : // www . apache . org / licenses / LICENSE -2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
# import "AppDelegate.h"
2015-08-21 18:00:39 +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-12-09 17:12:42 +00:00
# import "HomeViewController.h"
2015-01-05 15:53:41 +00:00
# import "SettingsViewController.h"
2015-11-17 23:15:52 +00:00
# import "RoomViewController.h"
2015-04-14 08:55:18 +00:00
# import "RageShakeManager.h"
2014-10-02 15:02:47 +00:00
2015-08-07 08:42:47 +00:00
# import "NSBundle+MatrixKit.h"
2015-02-12 10:16:28 +00:00
# import "AFNetworkReachabilityManager.h"
2015-04-30 14:19:12 +00:00
# import < AudioToolbox / AudioToolbox . h >
2015-11-16 09:30:57 +00:00
// # define MX_CALL _STACK _OPENWEBRTC
2015-08-07 14:20:46 +00:00
# ifdef MX_CALL _STACK _OPENWEBRTC
2015-07-21 09:30:37 +00:00
# import < MatrixOpenWebRTCWrapper / MatrixOpenWebRTCWrapper . h >
2015-08-07 14:20:46 +00:00
# endif
# ifdef MX_CALL _STACK _ENDPOINT
# import < MatrixEndpointWrapper / MatrixEndpointWrapper . h >
# endif
2015-07-21 09:30:37 +00:00
2015-11-26 17:23:04 +00:00
# import "VectorDesignValues.h"
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
2015-11-17 23:15:52 +00:00
@ interface AppDelegate ( )
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 ) .
* /
2015-05-12 08:53:47 +00:00
MXKCallViewController * currentCallViewController ;
2015-05-11 21:11:23 +00:00
/ * *
Call status window displayed when user goes back to app during a call .
* /
UIWindow * callStatusBarWindow ;
UIButton * callStatusBarButton ;
2015-05-28 16:31:08 +00:00
/ * *
Account picker used in case of multiple account .
* /
MXKAlert * accountPicker ;
2015-11-17 23:15:52 +00:00
/ * *
Array of ` MXSession` instances .
* /
NSMutableArray * mxSessionArray ;
2016-04-06 09:28:12 +00:00
/ * *
The room id of the current handled remote notification ( if any )
* /
NSString * remoteNotificationRoomId ;
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 ;
/ * *
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 ;
2016-04-13 15:08:02 +00:00
/ * *
Completion block called when [ self popToHomeViewControllerAnimated : ] has been
completed .
* /
void ( ^ popToHomeViewControllerCompletion ) ( ) ;
2015-02-18 16:40:55 +00:00
}
2014-10-02 15:02:47 +00:00
2015-04-30 14:19:12 +00:00
@ property ( strong , nonatomic ) MXKAlert * mxInAppNotification ;
2014-10-02 15:02:47 +00:00
@ end
@ implementation AppDelegate
2014-10-08 12:14:12 +00:00
# pragma mark -
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 ;
}
}
} ] ;
}
_isOffline = isOffline ;
}
2015-02-12 10:16:28 +00:00
# pragma mark - UIApplicationDelegate
2014-10-02 15:02:47 +00:00
2015-06-18 09:19:42 +00:00
- ( BOOL ) application : ( UIApplication * ) application didFinishLaunchingWithOptions : ( NSDictionary * ) launchOptions
{
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
2016-02-03 11:21:45 +00:00
2015-12-21 09:35:48 +00:00
// Override point for customization after application launch .
2016-02-11 14:19:49 +00:00
// Define the navigation bar text color
2016-02-05 14:21:00 +00:00
[ [ UINavigationBar appearance ] setTintColor : kVectorColorGreen ] ;
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 ] ;
// To simplify navigation into the app , we retrieve here the navigation controller and the view controller related
// to the recents list in Recents Tab .
// Note : UISplitViewController is not supported on iPhone for iOS < 8.0
UIViewController * recents = self . window . rootViewController ;
2015-12-09 17:12:42 +00:00
_homeNavigationController = nil ;
2015-11-17 23:15:52 +00:00
if ( [ recents isKindOfClass : [ UISplitViewController class ] ] )
2014-10-03 17:26:39 +00:00
{
2015-11-17 23:15:52 +00:00
UISplitViewController * splitViewController = ( UISplitViewController * ) recents ;
splitViewController . delegate = self ;
2014-10-03 17:26:39 +00:00
2015-12-09 17:12:42 +00:00
_homeNavigationController = [ splitViewController . viewControllers objectAtIndex : 0 ] ;
2014-10-03 17:26:39 +00:00
2015-11-17 23:15:52 +00:00
UIViewController * detailsViewController = [ splitViewController . viewControllers lastObject ] ;
if ( [ detailsViewController isKindOfClass : [ UINavigationController class ] ] )
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
UINavigationController * navigationController = ( UINavigationController * ) detailsViewController ;
detailsViewController = navigationController . topViewController ;
}
// IOS >= 8
if ( [ splitViewController respondsToSelector : @ selector ( displayModeButtonItem ) ] )
{
detailsViewController . navigationItem . leftBarButtonItem = splitViewController . displayModeButtonItem ;
2014-12-16 17:30:49 +00:00
2015-11-17 23:15:52 +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
if ( [ splitViewController respondsToSelector : @ selector ( preferredDisplayMode ) ] && [ ( NSString * ) [ UIDevice currentDevice ] . model hasPrefix : @ "iPad" ] )
2015-06-18 09:19:42 +00:00
{
2015-11-17 23:15:52 +00:00
splitViewController . preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible ;
2014-12-16 17:30:49 +00:00
}
2015-06-18 09:19:42 +00:00
}
2015-11-17 23:15:52 +00:00
}
else if ( [ recents isKindOfClass : [ UINavigationController class ] ] )
{
2015-12-09 17:12:42 +00:00
_homeNavigationController = ( UINavigationController * ) recents ;
2015-11-17 23:15:52 +00:00
}
2015-12-09 17:12:42 +00:00
if ( _homeNavigationController )
2015-11-17 23:15:52 +00:00
{
2015-12-09 17:12:42 +00:00
for ( UIViewController * viewController in _homeNavigationController . viewControllers )
2015-06-18 09:19:42 +00:00
{
2015-12-09 17:12:42 +00:00
if ( [ viewController isKindOfClass : [ HomeViewController class ] ] )
2015-11-17 23:15:52 +00:00
{
2015-12-22 14:42:53 +00:00
_homeViewController = ( HomeViewController * ) viewController ;
2015-11-17 23:15:52 +00:00
}
2014-10-03 17:26:39 +00:00
}
2014-10-02 15:02:47 +00:00
}
2015-11-17 23:15:52 +00:00
// Sanity check
2015-12-22 14:42:53 +00:00
NSAssert ( _homeViewController , @ "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-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 ] ;
2015-01-30 08:05:15 +00:00
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 )
{
2014-10-31 17:54:32 +00:00
[ self . errorNotification dismiss : NO ] ;
self . errorNotification = nil ;
}
2015-05-28 16:31:08 +00:00
2015-06-18 09:19:42 +00:00
if ( accountPicker )
{
2015-05-28 16:31:08 +00:00
[ accountPicker dismiss : NO ] ;
accountPicker = 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 ] ;
2016-02-11 14:05:24 +00:00
self . isOffline = NO ;
2015-02-12 10:16:28 +00:00
2015-04-30 14:19:12 +00:00
// check if some media must be released to reduce the cache size
2015-04-02 11:50:11 +00:00
[ MXKMediaManager reduceCacheSizeToInsert : 0 ] ;
2015-04-30 14:19:12 +00:00
// Hide potential notification
2015-06-18 09:19:42 +00:00
if ( self . mxInAppNotification )
{
2015-04-30 14:19:12 +00:00
[ self . mxInAppNotification dismiss : NO ] ;
self . mxInAppNotification = nil ;
}
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 ;
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" ) ;
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
2016-03-03 08:15:44 +00:00
// cancel any background sync before resuming
// i . e . warn IOS that there is no new data with any received push .
[ self cancelBackgroundSync ] ;
2015-02-02 15:43:47 +00:00
2016-03-23 13:45:16 +00:00
// Open account session ( s ) if this is not already done ( see [ initMatrixSessions ] in case of background launch ) .
[ [ MXKAccountManager sharedManager ] prepareSessionForActiveAccounts ] ;
2015-02-02 15:43:47 +00:00
_isAppForeground = YES ;
2014-10-02 15:02:47 +00:00
}
2015-06-18 09:19:42 +00:00
- ( void ) applicationDidBecomeActive : ( UIApplication * ) application
{
2016-02-03 11:21:45 +00:00
NSLog ( @ "[AppDelegate] applicationDidBecomeActive" ) ;
2016-04-06 09:28:12 +00:00
remoteNotificationRoomId = nil ;
2015-11-30 16:22:39 +00:00
// Check if the app crashed last time
if ( [ MXLogger crashLog ] )
{
# ifndef DEBUG
// In distributed version , clear the cache to not annoy user more .
// In debug mode , the developer will be pleased to investigate what is wrong in the cache .
NSLog ( @ "[AppDelegate] Clear the cache due to app crash" ) ;
[ self reloadMatrixSessions : YES ] ;
# endif
// Ask the user to send a bug report
[ [ RageShakeManager sharedManager ] promptCrashReportInViewController : self . window . rootViewController ] ;
}
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 ;
}
2016-02-11 15:09:35 +00:00
[ [ 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 ] ;
} ] ;
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
// refresh the contacts list
[ MXKContactManager sharedManager ] . enableFullMatrixIdSyncOnLocalContactsDidLoad = NO ;
[ [ MXKContactManager sharedManager ] loadLocalContacts ] ;
_isAppForeground = YES ;
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-04-06 16:05:26 +00:00
- ( BOOL ) application : ( UIApplication * ) application continueUserActivity : ( NSUserActivity * ) userActivity restorationHandler : ( void ( ^ ) ( NSArray * _Nullable ) ) restorationHandler
{
2016-04-07 15:38:19 +00:00
BOOL continueUserActivity ;
2016-04-12 08:23:48 +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
}
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-04-05 16:05:50 +00:00
// Dismiss potential media picker
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-04-13 15:08:02 +00:00
[ self popToHomeViewControllerAnimated : NO completion : completion ] ;
2016-04-05 16:05:50 +00:00
} ] ;
}
else
{
2016-04-13 15:08:02 +00:00
[ self popToHomeViewControllerAnimated : NO completion : completion ] ;
2015-11-17 23:15:52 +00:00
}
}
- ( MXKAlert * ) showErrorAsAlert : ( NSError * ) error
{
// 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 ;
}
if ( self . errorNotification )
{
[ self . errorNotification dismiss : NO ] ;
}
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
}
self . errorNotification = [ [ MXKAlert alloc ] initWithTitle : title message : msg style : MXKAlertStyleAlert ] ;
2016-02-11 13:06:48 +00:00
self . errorNotification . cancelButtonIndex = [ self . errorNotification addActionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "ok" ] style : MXKAlertActionStyleDefault handler : ^ ( MXKAlert * alert ) {
2015-11-17 23:15:52 +00:00
[ AppDelegate theDelegate ] . errorNotification = nil ;
} ] ;
[ self . errorNotification showInViewController : self . window . rootViewController ] ;
// Switch in offline mode in case of network reachability error
if ( [ error . domain isEqualToString : NSURLErrorDomain ] && error . code = = NSURLErrorNotConnectedToInternet )
{
self . isOffline = YES ;
}
return self . errorNotification ;
}
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
{
2016-04-13 15:08:02 +00:00
// Force back to the main screen if this is the not the one that is displayed
if ( _homeViewController && _homeViewController ! = _homeNavigationController . visibleViewController )
2016-04-05 16:05:50 +00:00
{
2016-04-13 15:08:02 +00:00
// Listen to the homeNavigationController changes
// We need to be sure that homeViewController is back to the screen
popToHomeViewControllerCompletion = completion ;
_homeNavigationController . delegate = self ;
2016-04-05 16:05:50 +00:00
[ _homeNavigationController popToViewController : _homeViewController animated : animated ] ;
// 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 .
_homeNavigationController . navigationBarHidden = NO ;
// For unknown reason , the default settings of the navigation bar are not restored correctly by [ popToViewController : animated : ]
// when a ViewController has changed them ( see RoomViewController , RoomMemberDetailsViewController ) .
// Patch : restore default settings here .
[ _homeNavigationController . navigationBar setShadowImage : nil ] ;
[ _homeNavigationController . navigationBar setBackgroundImage : nil forBarMetrics : UIBarMetricsDefault ] ;
// Release the current selected room
[ _homeViewController closeSelectedRoom ] ;
}
2016-04-13 15:08:02 +00:00
else
{
2016-04-13 15:27:54 +00:00
// Dispatch the completion in order to let navigation stack refresh itself
// It is required to display the auth VC at startup
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
completion ( ) ;
} ) ;
2016-04-13 15:08:02 +00:00
}
}
# pragma mark - UINavigationController delegate
- ( void ) navigationController : ( UINavigationController * ) navigationController didShowViewController : ( UIViewController * ) viewController animated : ( BOOL ) animated
{
if ( viewController = = _homeViewController )
{
_homeNavigationController . delegate = nil ;
if ( popToHomeViewControllerCompletion )
{
2016-04-19 07:14:32 +00:00
void ( ^ popToHomeViewControllerCompletion2 ) ( ) = popToHomeViewControllerCompletion ;
popToHomeViewControllerCompletion = nil ;
// 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
}
2014-12-12 09:49:15 +00:00
# pragma mark - APNS methods
2015-06-18 09:19:42 +00:00
- ( void ) registerUserNotificationSettings
{
if ( ! isAPNSRegistered )
{
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
}
- ( void ) application : ( UIApplication * ) application didRegisterUserNotificationSettings : ( UIUserNotificationSettings * ) notificationSettings
{
[ application registerForRemoteNotifications ] ;
}
2015-06-18 09:19:42 +00:00
- ( void ) application : ( UIApplication * ) app didRegisterForRemoteNotificationsWithDeviceToken : ( NSData * ) deviceToken
{
2015-10-28 14:33:07 +00:00
NSUInteger len = ( ( deviceToken . length > 8 ) ? 8 : deviceToken . length / 2 ) ;
NSLog ( @ "[AppDelegate] Got APNS token! (%@ ...)" , [ deviceToken subdataWithRange : NSMakeRange ( 0 , len ) ] ) ;
2014-12-12 09:49:15 +00:00
2015-06-18 13:20:33 +00:00
MXKAccountManager * accountManager = [ MXKAccountManager sharedManager ] ;
[ accountManager setApnsDeviceToken : deviceToken ] ;
2014-12-12 09:49:15 +00:00
isAPNSRegistered = YES ;
}
2015-06-18 09:19:42 +00:00
- ( void ) application : ( UIApplication * ) app didFailToRegisterForRemoteNotificationsWithError : ( NSError * ) error
{
2015-02-24 12:49:05 +00:00
NSLog ( @ "[AppDelegate] Failed to register for APNS: %@" , error ) ;
2014-12-12 09:49:15 +00:00
}
2015-10-20 08:19:21 +00:00
- ( void ) cancelBackgroundSync
{
if ( _completionHandler )
{
_completionHandler ( UIBackgroundFetchResultNoData ) ;
_completionHandler = nil ;
}
}
2015-06-18 09:19:42 +00:00
- ( void ) application : ( UIApplication * ) application didReceiveRemoteNotification : ( NSDictionary * ) userInfo fetchCompletionHandler : ( void ( ^ ) ( UIBackgroundFetchResult ) ) completionHandler
{
2014-12-12 09:49:15 +00:00
# ifdef DEBUG
// log the full userInfo only in DEBUG
2016-03-23 13:45:16 +00:00
NSLog ( @ "[AppDelegate] didReceiveRemoteNotification: %@" , userInfo ) ;
# else
NSLog ( @ "[AppDelegate] didReceiveRemoteNotification" ) ;
2014-12-12 09:49:15 +00:00
# endif
2015-10-20 08:19:21 +00:00
// Look for the room id
NSString * roomId = [ userInfo objectForKey : @ "room_id" ] ;
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
// * * * * * * * * * * * * * *
// 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 )
{
UIApplicationState state = [ UIApplication sharedApplication ] . applicationState ;
2015-05-28 17:20:18 +00:00
2015-10-20 08:19:21 +00:00
// Jump to the concerned room only if the app is transitioning from the background
if ( state = = UIApplicationStateInactive )
2015-06-18 09:19:42 +00:00
{
2016-04-06 09:28:12 +00:00
// Check whether another remote notification is not already processed
if ( ! remoteNotificationRoomId )
{
remoteNotificationRoomId = roomId ;
NSLog ( @ "[AppDelegate] didReceiveRemoteNotification: open the roomViewController %@" , roomId ) ;
2016-04-07 15:38:19 +00:00
[ self showRoom : roomId andEventId : nil withMatrixSession : dedicatedAccount . mxSession ] ;
2016-04-06 09:28:12 +00:00
}
else
{
NSLog ( @ "[AppDelegate] didReceiveRemoteNotification: busy" ) ;
}
2015-10-20 08:19:21 +00:00
}
else if ( ! _completionHandler && ( state = = UIApplicationStateBackground ) )
2015-06-18 09:19:42 +00:00
{
2015-10-20 08:19:21 +00:00
_completionHandler = completionHandler ;
2015-11-30 16:22:39 +00:00
2016-01-07 13:00:37 +00:00
NSLog ( @ "[AppDelegate] : starts a background sync" ) ;
2015-10-20 08:19:21 +00:00
2016-01-07 13:00:37 +00:00
[ dedicatedAccount backgroundSync : 20000 success : ^ {
2016-04-06 09:28:12 +00:00
NSLog ( @ "[AppDelegate]: the background sync succeeds" ) ;
2015-10-20 08:19:21 +00:00
if ( _completionHandler )
2015-06-18 09:19:42 +00:00
{
2015-10-20 08:19:21 +00:00
_completionHandler ( UIBackgroundFetchResultNewData ) ;
_completionHandler = nil ;
2015-05-28 17:20:18 +00:00
}
2015-10-20 08:19:21 +00:00
} failure : ^ ( NSError * error ) {
2016-04-06 09:28:12 +00:00
NSLog ( @ "[AppDelegate]: the background sync fails" ) ;
2015-10-20 08:19:21 +00:00
if ( _completionHandler )
{
_completionHandler ( UIBackgroundFetchResultNoData ) ;
_completionHandler = nil ;
}
} ] ;
// wait that the background sync is done
return ;
2015-05-28 17:20:18 +00:00
}
2015-10-20 08:19:21 +00:00
}
else
{
2016-04-06 09:28:12 +00:00
NSLog ( @ "[AppDelegate]: didReceiveRemoteNotification : no linked session / account has been found." ) ;
2015-02-19 17:23:24 +00:00
}
2015-02-13 15:16:27 +00:00
}
2015-10-20 08:19:21 +00:00
completionHandler ( UIBackgroundFetchResultNoData ) ;
2014-12-12 09:49:15 +00:00
}
2016-03-03 08:15:44 +00:00
- ( void ) refreshApplicationIconBadgeNumber
{
NSLog ( @ "[AppDelegate] refreshApplicationIconBadgeNumber" ) ;
[ UIApplication sharedApplication ] . applicationIconBadgeNumber = [ MXKRoomDataSourceManager notificationCount ] ;
}
2015-11-17 23:15:52 +00:00
2016-04-12 08:23:48 +00:00
# pragma mark - Universal link
2016-04-18 15:53:30 +00:00
- ( BOOL ) isUniversalLink : ( NSURL * ) url
{
BOOL isUniversalLink ;
if ( [ url . host isEqualToString : @ "vector.im" ]
&& NSNotFound ! = [ @ [ @ "/app" , @ "/staging" , @ "/beta" , @ "/develop" ] indexOfObject : url . path ] )
{
isUniversalLink = YES ;
}
return isUniversalLink ;
}
2016-04-12 08:23:48 +00:00
- ( BOOL ) handleUniversalLink : ( NSUserActivity * ) userActivity
{
NSURL * webURL = userActivity . webpageURL ;
NSLog ( @ "[AppDelegate] handleUniversalLink: %@" , webURL . absoluteString ) ;
2016-04-20 07:21:46 +00:00
// iOS Patch : fix vector . im urls before using it
webURL = [ AppDelegate fixURLWithSeveralHashKeys : webURL ] ;
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 ] ;
2016-04-12 12:19:38 +00:00
2016-04-12 15:44:06 +00:00
NSLog ( @ "[AppDelegate] Universal link: handleUniversalLinkFragment: %@" , fragment ) ;
// The app manages only one universal link at a time
// Discard any pending one
[ self resetPendingUniversalLink ] ;
2016-04-12 12:19:38 +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 ] ;
2016-04-12 08:23:48 +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 ;
}
2016-04-12 08:23:48 +00:00
// Check the action to do
if ( [ pathParams [ 0 ] isEqualToString : @ "room" ] && pathParams . count >= 2 )
{
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
// The link is the form of "/room/[roomIdOrAlias]" or "/room/[roomIdOrAlias]/[eventId]"
NSString * roomIdOrAlias = pathParams [ 1 ] ;
2016-04-12 08:23:48 +00:00
2016-04-12 13:30:02 +00:00
// Is it a link to an event of a room ?
NSString * eventId = ( pathParams . count >= 3 ) ? pathParams [ 2 ] : nil ;
// 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 ;
// 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
}
2016-04-12 13:30:02 +00:00
// Open the room page
[ self showRoom : roomId andEventId : eventId withMatrixSession : account . mxSession ] ;
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 : ^ {
[ _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 ) {
// Note : the activity indicator will not disappear if the session is not ready
[ _homeViewController stopActivityIndicator ] ;
// Check that ' fragment ' has not been cancelled
if ( [ universalLinkFragmentPending isEqualToString : fragment ] )
{
// Retry opening the link but with the returned room id
NSString * newUniversalLinkFragment =
[ fragment stringByReplacingOccurrencesOfString : [ roomIdOrAlias stringByAddingPercentEscapesUsingEncoding : NSUTF8StringEncoding ]
withString : [ roomId stringByAddingPercentEscapesUsingEncoding : NSUTF8StringEncoding ] ] ;
[ self handleUniversalLinkFragment : newUniversalLinkFragment ] ;
}
} 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-12 15:44:06 +00:00
{
2016-04-19 13:18:24 +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-12 15:44:06 +00:00
}
2016-04-19 13:18:24 +00:00
else
{
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" ) ;
2016-04-12 08:23:48 +00:00
2016-04-19 13:18:24 +00:00
// FIXME : In case of multi - account , ask the user which one to use
MXKAccount * account = accountManager . activeAccounts . firstObject ;
2016-04-12 13:30:02 +00:00
2016-04-19 13:18:24 +00:00
RoomPreviewData * roomPreviewData ;
if ( queryParams )
{
// Note : the activity indicator will not disappear if the session is not ready
[ _homeViewController stopActivityIndicator ] ;
2016-04-12 13:30:02 +00:00
2016-04-19 13:18:24 +00:00
roomPreviewData = [ [ RoomPreviewData alloc ] initWithRoomId : roomIdOrAlias emailInvitationParams : queryParams andSession : account . mxSession ] ;
[ self showRoomPreview : roomPreviewData ] ;
}
else
{
roomPreviewData = [ [ RoomPreviewData alloc ] initWithRoomId : roomIdOrAlias andSession : account . mxSession ] ;
2016-04-12 13:30:02 +00:00
2016-04-19 13:18:24 +00:00
// Try to get more information about the room before opening its preview
[ roomPreviewData fetchPreviewData : ^ ( BOOL successed ) {
2016-04-12 13:30:02 +00:00
2016-04-19 13:18:24 +00:00
// Note : the activity indicator will not disappear if the session is not ready
[ _homeViewController stopActivityIndicator ] ;
[ self showRoomPreview : roomPreviewData ] ;
} ] ;
2016-04-12 15:44:06 +00:00
}
2016-04-12 13:30:02 +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 ;
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 ) {
2016-04-12 13:56:17 +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
}
}
2016-04-13 15:56:46 +00:00
else if ( [ pathParams [ 0 ] isEqualToString : @ "register" ] )
{
2016-04-21 14:19:16 +00:00
NSLog ( @ "[AppDelegate] Universal link: Registration next_link parameters: %@" , queryParams ) ;
continueUserActivity = YES ;
2016-04-13 15:56:46 +00:00
// This is the nextLink in the registration process
2016-04-21 14:19:16 +00:00
if ( _homeViewController . authViewController )
{
NSLog ( @ "[AppDelegate] Universal link: Forward next_link parameter to the existing AuthViewController" ) ;
[ _homeViewController . authViewController registerWithNextLinkParameters : queryParams ] ;
}
else
{
2016-04-21 15:49:52 +00:00
NSLog ( @ "[AppDelegate] Universal link: Logout current session to complete the registration in next_link" ) ;
// At least one account should be logged it
// Logout out before opening the Authentication screen
if ( accountManager . activeAccounts . count )
{
[ accountManager logout ] ;
}
// Return to authentication screen
[ _homeViewController showAuthenticationScreenWithNextLinkParameters : queryParams ] ;
2016-04-21 14:19:16 +00:00
}
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 ) ;
2016-04-13 15:56:46 +00:00
[ self popToHomeViewControllerAnimated : NO completion : nil ] ;
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 :
2016-04-12 15:44:06 +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 ]
@ 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 ) ;
NSArray < NSString * > * pathParams ;
NSMutableDictionary * queryParams ;
NSArray < NSString * > * fragments = [ fragment componentsSeparatedByString : @ "?" ] ;
// Extract path params
pathParams = [ fragments [ 0 ] componentsSeparatedByString : @ "/" ] ;
// Remove the first empty path param string
pathParams = [ pathParams filteredArrayUsingPredicate : [ NSPredicate predicateWithFormat : @ "length > 0" ] ] ;
// 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 ;
// Extract query params if any
if ( fragments . count = = 2 )
{
queryParams = [ [ NSMutableDictionary alloc ] init ] ;
for ( NSString * keyValue in [ fragments [ 1 ] componentsSeparatedByString : @ "&" ] )
{
// Get the parameter name
NSString * key = [ [ keyValue componentsSeparatedByString : @ "=" ] objectAtIndex : 0 ] ;
// 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 ] ;
2016-04-12 09:19:50 +00:00
2016-04-13 15:10:35 +00:00
queryParams [ key ] = value ;
}
2016-04-12 09:19:50 +00:00
}
}
* outPathParams = pathParams ;
* outQueryParams = queryParams ;
}
2016-04-20 07:21:46 +00:00
+ ( NSURL * ) fixURLWithSeveralHashKeys : ( NSURL * ) url
{
2016-04-20 16:03:03 +00:00
NSURL * fixedURL = url ;
2016-04-20 07:21:46 +00:00
2016-04-20 16:03:03 +00:00
// The NSURL may have no fragment because it contains more that ' % 23 ' occurence
if ( ! url . fragment )
2016-04-20 07:21:46 +00:00
{
2016-04-20 16:03:03 +00:00
// Replacing the first ' % 23 ' occurence into a ' # ' makes NSURL works correctly
NSString * urlString = url . absoluteString ;
NSRange range = [ urlString rangeOfString : @ "%23" ] ;
if ( NSNotFound ! = range . location )
{
urlString = [ urlString stringByReplacingCharactersInRange : range withString : @ "#" ] ;
fixedURL = [ NSURL URLWithString : urlString ] ;
}
2016-04-20 07:21:46 +00:00
}
return fixedURL ;
}
2015-04-30 14:19:12 +00:00
# pragma mark - Matrix sessions handling
2015-06-18 09:19:42 +00:00
- ( void ) initMatrixSessions
{
2015-12-11 12:51:48 +00:00
// Disable identicon use
[ MXSDKOptions sharedInstance ] . disableIdenticonUseForUserAvatar = YES ;
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 .
2015-06-18 09:19:42 +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 ;
2015-05-11 22:26:00 +00:00
// Remove by default potential call observer on matrix session state change
2015-06-18 09:19:42 +00:00
if ( matrixCallObserver )
{
2015-05-11 22:26:00 +00:00
[ [ NSNotificationCenter defaultCenter ] removeObserver : matrixCallObserver ] ;
matrixCallObserver = nil ;
}
2015-04-30 14:19:12 +00:00
// 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 ] ;
# endif
if ( callStack )
{
[ mxSession enableVoIPWithCallStack : callStack ] ;
}
2015-12-22 07:43:55 +00:00
// Each room member will be considered as a potential contact .
[ MXKContactManager sharedManager ] . contactManagerMXRoomSource = MXKContactManagerMXRoomSourceAll ;
2015-06-18 13:20:33 +00:00
}
else if ( mxSession . state = = MXSessionStateStoreDataReady )
2015-06-18 09:19:42 +00:00
{
2015-06-18 13:20:33 +00:00
// Check whether the app user wants inApp notifications on new events for 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 )
{
[ 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
}
// Restore call observer only if all session are running
2015-11-17 23:15:52 +00:00
NSArray * mxSessions = self . mxSessions ;
2015-05-21 16:15:45 +00:00
BOOL shouldAddMatrixCallObserver = ( mxSessions . count ) ;
2015-06-18 09:19:42 +00:00
for ( mxSession in mxSessions )
{
if ( mxSession . state ! = MXSessionStateRunning )
{
2015-05-21 16:15:45 +00:00
shouldAddMatrixCallObserver = NO ;
break ;
}
}
2015-06-18 13:20:33 +00:00
2015-06-18 09:19:42 +00:00
if ( shouldAddMatrixCallObserver )
{
2015-05-11 22:26:00 +00:00
// A new call observer may be added here
[ self addMatrixCallObserver ] ;
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 )
{
2016-03-21 13:49:15 +00:00
// Set the push gateway URL .
account . pushGatewayURL = [ [ NSUserDefaults standardUserDefaults ] objectForKey : @ "pushGatewayURL" ] ;
2015-06-19 08:31:27 +00:00
if ( isAPNSRegistered )
{
// Enable push notifications by default on new added account
account . enablePushNotifications = YES ;
}
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
}
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" ] ;
// Logout the app when there is no available account
if ( ! [ MXKAccountManager sharedManager ] . accounts . count )
{
[ self logout ] ;
}
} ] ;
2015-04-30 14:19:12 +00:00
// Observe settings changes
[ [ MXKAppSettings standardAppSettings ] addObserver : self forKeyPath : @ "showAllEventsInRoomHistory" options : 0 context : nil ] ;
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 ] ;
2016-03-23 13:45:16 +00:00
// Observers have been defined , we can start a matrix session for each enabled accounts .
// except if the app is still in background .
if ( [ [ UIApplication sharedApplication ] applicationState ] ! = UIApplicationStateBackground )
2015-08-07 14:20:46 +00:00
{
2016-03-23 13:45:16 +00:00
[ accountManager prepareSessionForActiveAccounts ] ;
}
else
{
// The app is launched in background as a result of a remote notification .
// Presently we are not able to initialize the matrix session ( s ) in background . ( FIXME : initialize matrix session ( s ) in case of a background launch ) .
// Patch : the account session ( s ) will be opened when the app will enter foreground .
NSLog ( @ "[AppDelegate] initMatrixSessions: The application has been launched in background" ) ;
2015-08-07 14:20:46 +00:00
}
2015-06-19 08:31:27 +00:00
// Check whether we ' re already logged in
2015-07-21 09:30:37 +00:00
NSArray * mxAccounts = accountManager . accounts ;
2015-06-18 09:19:42 +00:00
if ( mxAccounts . count )
{
2016-03-21 13:49:15 +00:00
// The push gateway url is now configurable .
// Set this url in the existing accounts when it is undefined .
for ( MXKAccount * account in mxAccounts )
{
if ( ! account . pushGatewayURL )
{
// Set the push gateway URL .
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 ] ;
2015-11-17 23:15:52 +00:00
// Update recents data source ( The recents view controller will be updated by its data source )
if ( ! mxSessionArray . count )
{
// This is the first added session , list all the recents for the logged user
2015-12-22 14:42:53 +00:00
[ _homeViewController displayWithSession : mxSession ] ;
2015-11-17 23:15:52 +00:00
}
else
{
2015-12-22 14:42:53 +00:00
[ _homeViewController addMatrixSession : mxSession ] ;
2015-11-17 23:15:52 +00:00
}
[ mxSessionArray addObject : mxSession ] ;
}
}
- ( 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
2015-12-22 14:42:53 +00:00
[ _homeViewController removeMatrixSession : mxSession ] ;
2015-11-17 23:15:52 +00:00
[ mxSessionArray removeObject : mxSession ] ;
}
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 ] ;
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
[ MXKMediaManager clearCache ] ;
}
}
2014-10-08 16:55:26 +00:00
2015-06-18 09:19:42 +00:00
- ( void ) logout
{
2014-12-12 09:49:15 +00:00
[ [ UIApplication sharedApplication ] unregisterForRemoteNotifications ] ;
isAPNSRegistered = NO ;
2015-04-17 13:45:32 +00:00
2014-10-17 23:04:05 +00:00
// Clear cache
2015-04-02 11:50:11 +00:00
[ MXKMediaManager clearCache ] ;
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
2016-04-05 13:31:52 +00:00
[ _homeViewController showAuthenticationScreen ] ;
2015-04-17 13:45:32 +00:00
2014-11-28 14:06:53 +00:00
// Reset App settings
2015-04-15 12:46:54 +00:00
[ [ MXKAppSettings standardAppSettings ] reset ] ;
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
}
2015-11-17 23:15:52 +00:00
}
- ( void ) addMatrixCallObserver
{
if ( matrixCallObserver )
2014-10-08 12:14:12 +00:00
{
2015-11-17 23:15:52 +00:00
[ [ NSNotificationCenter defaultCenter ] removeObserver : matrixCallObserver ] ;
2014-10-03 17:26:39 +00:00
}
2014-10-08 12:14:12 +00:00
2015-11-17 23:15:52 +00:00
// Register call observer in order to handle new opened session
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
if ( ! currentCallViewController )
{
MXCall * mxCall = ( MXCall * ) notif . object ;
currentCallViewController = [ MXKCallViewController callViewController : mxCall ] ;
currentCallViewController . delegate = self ;
// FIXME GFO Check whether present call from self . window . rootViewController is working
[ self . window . rootViewController presentViewController : currentCallViewController animated : YES completion : ^ {
currentCallViewController . isPresented = YES ;
} ] ;
// Hide system status bar
[ UIApplication sharedApplication ] . statusBarHidden = YES ;
}
2014-11-04 16:26:33 +00:00
} ] ;
2014-10-03 17:26:39 +00:00
}
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 )
{
[ self . mxInAppNotification dismiss : NO ] ;
}
// 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 ) ;
}
}
}
__weak typeof ( self ) weakSelf = self ;
self . mxInAppNotification = [ [ MXKAlert alloc ] initWithTitle : roomState . displayname
message : messageText
style : MXKAlertStyleAlert ] ;
2015-08-07 08:42:47 +00:00
self . mxInAppNotification . cancelButtonIndex = [ self . mxInAppNotification addActionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "cancel" ]
2015-06-19 08:31:27 +00:00
style : MXKAlertActionStyleDefault
handler : ^ ( MXKAlert * alert )
{
weakSelf . mxInAppNotification = nil ;
[ account updateNotificationListenerForRoomId : event . roomId ignore : YES ] ;
} ] ;
2015-08-12 07:32:45 +00:00
[ self . mxInAppNotification addActionWithTitle : NSLocalizedStringFromTable ( @ "view" , @ "Vector" , nil )
2015-06-19 08:31:27 +00:00
style : MXKAlertActionStyleDefault
handler : ^ ( MXKAlert * alert )
2015-06-18 13:20:33 +00:00
{
2015-06-19 08:31:27 +00:00
weakSelf . mxInAppNotification = nil ;
// Show the room
2016-04-07 15:38:19 +00:00
[ weakSelf showRoom : event . roomId andEventId : nil withMatrixSession : account . mxSession ] ;
2015-06-19 08:31:27 +00:00
} ] ;
2015-11-17 23:15:52 +00:00
[ self . mxInAppNotification showInViewController : self . window . rootViewController ] ;
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 )
{
2015-04-30 14:19:12 +00:00
[ self . mxInAppNotification dismiss : NO ] ;
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 )
{
if ( accountPicker )
{
2015-05-28 16:31:08 +00:00
[ accountPicker dismiss : NO ] ;
}
2015-08-07 08:42:47 +00:00
accountPicker = [ [ MXKAlert alloc ] initWithTitle : [ NSBundle mxk_localizedStringForKey : @ "select_account" ] message : nil style : MXKAlertStyleActionSheet ] ;
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 )
{
2016-03-21 17:38:44 +00:00
[ accountPicker addActionWithTitle : account . mxCredentials . userId style : MXKAlertActionStyleDefault handler : ^ ( MXKAlert * alert ) {
2015-05-28 16:31:08 +00:00
__strong __typeof ( weakSelf ) strongSelf = weakSelf ;
strongSelf -> accountPicker = nil ;
2015-06-18 09:19:42 +00:00
if ( onSelection )
{
2015-05-28 16:31:08 +00:00
onSelection ( account ) ;
}
} ] ;
}
2016-03-21 17:38:44 +00:00
accountPicker . cancelButtonIndex = [ accountPicker addActionWithTitle : [ NSBundle mxk_localizedStringForKey : @ "cancel" ] style : MXKAlertActionStyleDefault handler : ^ ( MXKAlert * alert ) {
2015-05-28 16:31:08 +00:00
__strong __typeof ( weakSelf ) strongSelf = weakSelf ;
strongSelf -> accountPicker = nil ;
2016-03-21 17:38:44 +00:00
if ( onSelection )
{
onSelection ( nil ) ;
}
2015-05-28 16:31:08 +00:00
} ] ;
2015-11-17 23:15:52 +00:00
accountPicker . sourceView = self . window . rootViewController . view ;
[ accountPicker showInViewController : self . window . rootViewController ] ;
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 )
2016-04-07 15:38:19 +00:00
[ _homeViewController 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 : ^ {
[ _homeViewController showRoomPreview : roomPreviewData ] ;
} ] ;
}
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-03-21 17:38:44 +00:00
- ( void ) startPrivateOneToOneRoomWithUserId : ( 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 )
{
MXRoom * mxRoom = [ mxSession privateOneToOneRoomWithUserId : userId ] ;
// if the room exists
if ( mxRoom )
{
// open it
2016-04-07 15:38:19 +00:00
[ self showRoom : mxRoom . state . roomId andEventId : nil withMatrixSession : mxSession ] ;
2016-03-21 17:38:44 +00:00
if ( completion )
{
completion ( ) ;
}
}
else
{
// create a new room
[ mxSession createRoom : nil
visibility : kMXRoomVisibilityPrivate
roomAlias : nil
topic : nil
success : ^ ( MXRoom * room ) {
// Invite the other user only if it is defined and not onself
if ( userId && ! [ mxSession . myUser . userId isEqualToString : userId ] )
{
// Add the user
[ room inviteUser : userId
success : ^ {
}
failure : ^ ( NSError * error ) {
NSLog ( @ "[AppDelegate] %@ invitation failed (roomId: %@)" , userId , room . state . roomId ) ;
// Alert user
[ self showErrorAsAlert : error ] ;
} ] ;
}
// Open created room
2016-04-07 15:38:19 +00:00
[ self showRoom : room . state . roomId andEventId : nil withMatrixSession : mxSession ] ;
2016-03-21 17:38:44 +00:00
if ( completion )
{
completion ( ) ;
}
}
failure : ^ ( NSError * error ) {
NSLog ( @ "[AppDelegate] Create room failed" ) ;
// Alert user
[ self showErrorAsAlert : error ] ;
if ( completion )
{
completion ( ) ;
}
} ] ;
}
}
else if ( completion )
{
completion ( ) ;
}
} ] ;
2015-01-05 15:53:41 +00:00
}
2015-05-11 21:11:23 +00:00
# pragma mark - MXKCallViewControllerDelegate
2015-06-18 09:19:42 +00:00
- ( void ) dismissCallViewController : ( MXKCallViewController * ) callViewController
{
if ( callViewController = = currentCallViewController )
{
2015-05-11 21:11:23 +00:00
2015-06-18 09:19:42 +00:00
if ( callViewController . isPresented )
{
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 : ^ {
callViewController . isPresented = NO ;
2015-06-18 09:19:42 +00:00
if ( ! callIsEnded )
{
2015-05-12 08:53:47 +00:00
[ self addCallStatusBar ] ;
}
} ] ;
2015-06-18 09:19:42 +00:00
if ( callIsEnded )
{
2015-05-12 08:53:47 +00:00
[ self removeCallStatusBar ] ;
// Restore system status bar
[ UIApplication sharedApplication ] . statusBarHidden = NO ;
// Release properly
currentCallViewController . mxCall . delegate = nil ;
currentCallViewController . delegate = nil ;
currentCallViewController = nil ;
}
2015-06-18 09:19:42 +00:00
} else
{
2015-05-12 08:53:47 +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 ] ;
} ) ;
}
2015-05-11 21:11:23 +00:00
}
}
2015-06-16 11:54:12 +00:00
# pragma mark - MXKContactDetailsViewControllerDelegate
2016-03-21 17:38:44 +00:00
- ( void ) contactDetailsViewController : ( MXKContactDetailsViewController * ) contactDetailsViewController startChatWithMatrixId : ( NSString * ) matrixId completion : ( void ( ^ ) ( void ) ) completion
2015-06-16 11:54:12 +00:00
{
2016-03-21 17:38:44 +00:00
[ self startPrivateOneToOneRoomWithUserId : matrixId completion : completion ] ;
2015-06-16 11:54:12 +00:00
}
# pragma mark - MXKRoomMemberDetailsViewControllerDelegate
2016-03-21 17:38:44 +00:00
- ( void ) roomMemberDetailsViewController : ( MXKRoomMemberDetailsViewController * ) roomMemberDetailsViewController startChatWithMemberId : ( NSString * ) matrixId completion : ( void ( ^ ) ( void ) ) completion
2015-06-16 11:54:12 +00:00
{
2016-03-21 17:38:44 +00:00
[ self startPrivateOneToOneRoomWithUserId : matrixId completion : completion ] ;
2015-06-16 11:54:12 +00:00
}
2015-05-11 21:11:23 +00:00
# pragma mark - Call status handling
2015-06-18 09:19:42 +00:00
- ( void ) addCallStatusBar
{
2015-05-11 21:11:23 +00:00
// Add a call status bar
CGSize topBarSize = CGSizeMake ( [ [ UIScreen mainScreen ] applicationFrame ] . size . width , 44 ) ;
callStatusBarWindow = [ [ UIWindow alloc ] initWithFrame : CGRectMake ( 0 , 0 , topBarSize . width , topBarSize . height ) ] ;
callStatusBarWindow . windowLevel = UIWindowLevelStatusBar ;
// Create statusBarButton
callStatusBarButton = [ UIButton buttonWithType : UIButtonTypeCustom ] ;
callStatusBarButton . frame = CGRectMake ( 0 , 0 , topBarSize . width , topBarSize . height ) ;
2015-08-12 07:32:45 +00:00
NSString * btnTitle = NSLocalizedStringFromTable ( @ "return_to_call" , @ "Vector" , nil ) ;
2015-05-11 21:11:23 +00:00
[ callStatusBarButton setTitle : btnTitle forState : UIControlStateNormal ] ;
[ callStatusBarButton setTitle : btnTitle forState : UIControlStateHighlighted ] ;
callStatusBarButton . titleLabel . textColor = [ UIColor whiteColor ] ;
[ callStatusBarButton setBackgroundColor : [ UIColor blueColor ] ] ;
[ callStatusBarButton addTarget : self action : @ selector ( returnToCallView ) forControlEvents : UIControlEventTouchUpInside ] ;
// Place button into the new window
[ callStatusBarWindow addSubview : callStatusBarButton ] ;
callStatusBarWindow . hidden = NO ;
[ 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
{
if ( callStatusBarWindow )
{
2015-05-11 21:11:23 +00:00
// Hide & destroy it
callStatusBarWindow . hidden = YES ;
[ self statusBarDidChangeFrame ] ;
[ callStatusBarButton removeFromSuperview ] ;
callStatusBarButton = nil ;
callStatusBarWindow = nil ;
// No more need to listen to system status bar changes
[ [ NSNotificationCenter defaultCenter ] removeObserver : self name : UIApplicationDidChangeStatusBarFrameNotification object : nil ] ;
}
}
2015-06-18 09:19:42 +00:00
- ( void ) returnToCallView
{
2015-05-11 21:11:23 +00:00
[ self removeCallStatusBar ] ;
2015-11-17 23:15:52 +00:00
// FIXME GFO check whether self . window . rootViewController may present the call
[ self . window . rootViewController presentViewController : currentCallViewController animated : YES completion : ^ {
2015-05-12 08:53:47 +00:00
currentCallViewController . isPresented = YES ;
} ] ;
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
CGRect frame = [ [ UIScreen mainScreen ] applicationFrame ] ;
2015-06-18 09:19:42 +00:00
if ( callStatusBarWindow )
{
2015-05-11 21:11:23 +00:00
// Substract the height of call status bar from the frame .
CGFloat callBarStatusHeight = callStatusBarWindow . frame . size . height ;
CGFloat delta = callBarStatusHeight - frame . origin . y ;
frame . origin . y = callBarStatusHeight ;
frame . size . height - = delta ;
}
rootController . view . frame = frame ;
[ rootController . view setNeedsLayout ] ;
}
2015-11-17 23:15:52 +00:00
# pragma mark - SplitViewController delegate
- ( BOOL ) splitViewController : ( UISplitViewController * ) splitViewController collapseSecondaryViewController : ( UIViewController * ) secondaryViewController ontoPrimaryViewController : ( UIViewController * ) primaryViewController
{
if ( [ secondaryViewController isKindOfClass : [ UINavigationController class ] ] && [ [ ( UINavigationController * ) secondaryViewController topViewController ] isKindOfClass : [ RoomViewController class ] ] && ( [ ( RoomViewController * ) [ ( UINavigationController * ) secondaryViewController topViewController ] roomDataSource ] = = nil ) )
{
// 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 ;
}
2014-10-02 15:02:47 +00:00
@ end