From 4b76c6b24deb63006d45330ee18d6b2c28d800ec Mon Sep 17 00:00:00 2001 From: giomfo Date: Thu, 12 Feb 2015 11:16:28 +0100 Subject: [PATCH] Console: Improve offline mode: - remove loading wheel when network is unreachable. - color in red the navigation bar when the app is offline. Buf Fix SYIOS- 66 - Console: last outgoing message is stuck as local echo whereas the message has been delivered. --- matrixConsole/API/MatrixSDKHandler.h | 8 +- matrixConsole/API/MatrixSDKHandler.m | 262 ++++++++++++------ matrixConsole/AppDelegate.m | 11 +- matrixConsole/AppSettings.m | 2 +- .../AuthenticationViewController.m | 12 +- .../ViewController/ContactsViewController.m | 9 +- .../RageShakableTableViewController.h | 2 + .../RageShakableTableViewController.m | 53 ++++ .../RageShakableViewController.h | 2 + .../RageShakableViewController.m | 53 ++++ .../ViewController/RecentsViewController.m | 44 ++- .../ViewController/RoomViewController.m | 23 +- .../ViewController/SettingsViewController.m | 33 +-- 13 files changed, 352 insertions(+), 162 deletions(-) diff --git a/matrixConsole/API/MatrixSDKHandler.h b/matrixConsole/API/MatrixSDKHandler.h index 2e061112e..e31633ff5 100644 --- a/matrixConsole/API/MatrixSDKHandler.h +++ b/matrixConsole/API/MatrixSDKHandler.h @@ -22,7 +22,9 @@ typedef enum : NSUInteger { MatrixSDKHandlerStatusLoggedOut = 0, MatrixSDKHandlerStatusLogged, MatrixSDKHandlerStatusStoreDataReady, - MatrixSDKHandlerStatusServerSyncDone + MatrixSDKHandlerStatusServerSyncInProgress, + MatrixSDKHandlerStatusServerSyncDone, + MatrixSDKHandlerStatusPaused } MatrixSDKHandlerStatus; @interface MatrixSDKHandler : NSObject @@ -47,7 +49,7 @@ typedef enum : NSUInteger { @property (nonatomic) MXPresence userPresence; @property (nonatomic,readonly) MatrixSDKHandlerStatus status; -@property (nonatomic,readonly) BOOL isResumeDone; +@property (nonatomic,readonly) BOOL isActivityInProgress; // return the MX cache size in bytes @property (nonatomic,readonly) NSUInteger MXCacheSize; // return the sum of the caches (MX cache + media cache ...) in bytes @@ -66,7 +68,7 @@ typedef enum : NSUInteger { - (void)logout; // Flush and restore Matrix data -- (void)forceInitialSync:(BOOL)clearCache; +- (void)reload:(BOOL)clearCache; - (void)enableInAppNotifications:(BOOL)isEnabled; diff --git a/matrixConsole/API/MatrixSDKHandler.m b/matrixConsole/API/MatrixSDKHandler.m index 67846c58c..94b38ce5b 100644 --- a/matrixConsole/API/MatrixSDKHandler.m +++ b/matrixConsole/API/MatrixSDKHandler.m @@ -24,6 +24,8 @@ #import "MediaManager.h" +#import "AFNetworkReachabilityManager.h" + NSString *const kMatrixSDKHandlerUnsupportedEventDescriptionPrefix = @"Unsupported event: "; static MatrixSDKHandler *sharedHandler = nil; @@ -31,16 +33,19 @@ static MatrixSDKHandler *sharedHandler = nil; @interface MatrixSDKHandler () { // We will notify user only once on session failure BOOL notifyOpenSessionFailure; + NSTimer* initialServerSyncTimer; // Handle user's settings change id userUpdateListener; // Handle events notification id eventsListener; + // Reachability observer + id reachabilityObserver; } @property (strong, nonatomic) MXFileStore *mxFileStore; @property (nonatomic,readwrite) MatrixSDKHandlerStatus status; -@property (nonatomic,readwrite) BOOL isResumeDone; +@property (nonatomic,readwrite) BOOL isActivityInProgress; @property (strong, nonatomic) MXCAlert *mxNotification; @property (nonatomic) UIBackgroundTaskIdentifier bgTask; @@ -66,8 +71,7 @@ static MatrixSDKHandler *sharedHandler = nil; -(MatrixSDKHandler *)init { if (self = [super init]) { - _status = (self.accessToken != nil) ? MatrixSDKHandlerStatusLogged : MatrixSDKHandlerStatusLoggedOut; - _isResumeDone = NO; + self.status = (self.accessToken != nil) ? MatrixSDKHandlerStatusLogged : MatrixSDKHandlerStatusLoggedOut; _userPresence = MXPresenceUnknown; notifyOpenSessionFailure = YES; @@ -87,12 +91,29 @@ static MatrixSDKHandler *sharedHandler = nil; } _unnotifiedRooms = [[NSMutableArray alloc] init]; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil]; } return self; } +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [[NSNotificationCenter defaultCenter] removeObserver:reachabilityObserver]; + reachabilityObserver = nil; + + [initialServerSyncTimer invalidate]; + initialServerSyncTimer = nil; + + _processingQueue = nil; + + [self closeSession]; + self.mxSession = nil; + + if (self.mxNotification) { + [self.mxNotification dismiss:NO]; + self.mxNotification = nil; + } +} + - (void)openSession { MXCredentials *credentials = [[MXCredentials alloc] initWithHomeServer:self.homeServerURL userId:self.userId @@ -138,64 +159,101 @@ static MatrixSDKHandler *sharedHandler = nil; kMXEventTypeStringRoomMessage ]; } - - // Launch mxSession - [self.mxSession start:^{ - typeof(self) self = weakSelf; - self->_isResumeDone = YES; - self.status = MatrixSDKHandlerStatusServerSyncDone; - [self setUserPresence:MXPresenceOnline andStatusMessage:nil completion:nil]; - - // Register listener to update user's information - self->userUpdateListener = [self.mxSession.myUser listenToUserUpdate:^(MXEvent *event) { - // Consider only events related to user's presence - if (event.eventType == MXEventTypePresence) { - MXPresence presence = [MXTools presence:event.content[@"presence"]]; - if (self.userPresence != presence) { - // Handle user presence on multiple devices (keep the more pertinent) - if (self.userPresence == MXPresenceOnline) { - if (presence == MXPresenceUnavailable || presence == MXPresenceOffline) { - // Force the local presence to overwrite the user presence on server side - [self setUserPresence:self->_userPresence andStatusMessage:nil completion:nil]; - return; - } - } else if (self.userPresence == MXPresenceUnavailable) { - if (presence == MXPresenceOffline) { - // Force the local presence to overwrite the user presence on server side - [self setUserPresence:self->_userPresence andStatusMessage:nil completion:nil]; - return; - } - } - self.userPresence = presence; - } - } - }]; - - // Check whether the app user wants notifications on new events - if ([[AppSettings sharedSettings] enableInAppNotifications]) { - [self enableInAppNotifications:YES]; - } - } failure:^(NSError *error) { - typeof(self) self = weakSelf; - NSLog(@"Initial Sync failed: %@", error); - if (self->notifyOpenSessionFailure) { - //Alert user only once - self->notifyOpenSessionFailure = NO; - [[AppDelegate theDelegate] showErrorAsAlert:error]; - } - - // Postpone a new attempt in 10 sec - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [self openSession]; - }); - }]; + + // Complete session registration by launching live stream + [self launchInitialServerSync]; } failure:^(NSError *error) { // This cannot happen. Loading of MXFileStore cannot fail. + typeof(self) self = weakSelf; + self.mxSession = nil; }]; } } +- (void)launchInitialServerSync { + // Complete the session registration when store data is ready. + + // Cancel potential reachability observer and pending action + [[NSNotificationCenter defaultCenter] removeObserver:reachabilityObserver]; + reachabilityObserver = nil; + [initialServerSyncTimer invalidate]; + initialServerSyncTimer = nil; + + // Sanity check + if (self.status != MatrixSDKHandlerStatusStoreDataReady) { + NSLog(@"Initial server sync is applicable only when store data is ready to complete session initialisation"); + return; + } + + // Launch mxSession + self.status = MatrixSDKHandlerStatusServerSyncInProgress; + [self.mxSession start:^{ + self.status = MatrixSDKHandlerStatusServerSyncDone; + [self setUserPresence:MXPresenceOnline andStatusMessage:nil completion:nil]; + + // Register listener to update user's information + userUpdateListener = [self.mxSession.myUser listenToUserUpdate:^(MXEvent *event) { + // Consider only events related to user's presence + if (event.eventType == MXEventTypePresence) { + MXPresence presence = [MXTools presence:event.content[@"presence"]]; + if (self.userPresence != presence) { + // Handle user presence on multiple devices (keep the more pertinent) + if (self.userPresence == MXPresenceOnline) { + if (presence == MXPresenceUnavailable || presence == MXPresenceOffline) { + // Force the local presence to overwrite the user presence on server side + [self setUserPresence:_userPresence andStatusMessage:nil completion:nil]; + return; + } + } else if (self.userPresence == MXPresenceUnavailable) { + if (presence == MXPresenceOffline) { + // Force the local presence to overwrite the user presence on server side + [self setUserPresence:_userPresence andStatusMessage:nil completion:nil]; + return; + } + } + self.userPresence = presence; + } + } + }]; + + // Check whether the app user wants notifications on new events + if ([[AppSettings sharedSettings] enableInAppNotifications]) { + [self enableInAppNotifications:YES]; + } + } failure:^(NSError *error) { + NSLog(@"Initial Sync failed: %@", error); + if (notifyOpenSessionFailure) { + //Alert user only once + notifyOpenSessionFailure = NO; + [[AppDelegate theDelegate] showErrorAsAlert:error]; + } + + // Return in previous state + self.status = MatrixSDKHandlerStatusStoreDataReady; + + // Check network reachability + if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorNotConnectedToInternet) { + // Add observer to launch a new attempt according to reachability. + reachabilityObserver = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingReachabilityDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { + NSNumber *staturItem = note.userInfo[AFNetworkingReachabilityNotificationStatusItem]; + if (staturItem) { + AFNetworkReachabilityStatus reachabilityStatus = staturItem.integerValue; + if (reachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi || reachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN) { + // New attempt + [self launchInitialServerSync]; + } + } + }]; + } else { + // Postpone a new attempt in 10 sec + initialServerSyncTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(launchInitialServerSync) userInfo:self repeats:NO]; + } + }]; +} + - (void)closeSession { + self.status = (self.accessToken != nil) ? MatrixSDKHandlerStatusLogged : MatrixSDKHandlerStatusLoggedOut; + if (eventsListener) { [self.mxSession removeListener:eventsListener]; eventsListener = nil; @@ -204,10 +262,12 @@ static MatrixSDKHandler *sharedHandler = nil; [self.mxSession.myUser removeListener:userUpdateListener]; userUpdateListener = nil; } + [self.mxSession close]; self.mxSession = nil; [self.mxRestClient close]; + if (self.homeServerURL) { self.mxRestClient = [[MXRestClient alloc] initWithHomeServer:self.homeServerURL]; if (self.identityServerURL) { @@ -217,25 +277,12 @@ static MatrixSDKHandler *sharedHandler = nil; self.mxRestClient = nil; } - _isResumeDone = NO; notifyOpenSessionFailure = YES; } -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - _processingQueue = nil; - - [self closeSession]; - self.mxSession = nil; - - if (self.mxNotification) { - [self.mxNotification dismiss:NO]; - self.mxNotification = nil; - } -} +#pragma mark - -- (void)onAppDidEnterBackground { +- (void)pauseInBackgroundTask { // Hide potential notification if (self.mxNotification) { [self.mxNotification dismiss:NO]; @@ -243,11 +290,7 @@ static MatrixSDKHandler *sharedHandler = nil; } _unnotifiedRooms = [[NSMutableArray alloc] init]; -} - -#pragma mark - - -- (void)pauseInBackgroundTask { + if (self.mxSession && self.status == MatrixSDKHandlerStatusServerSyncDone) { _bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ [[UIApplication sharedApplication] endBackgroundTask:_bgTask]; @@ -259,7 +302,7 @@ static MatrixSDKHandler *sharedHandler = nil; NSLog(@"pauseInBackgroundTask : %08lX starts", (unsigned long)_bgTask); // Pause SDK [self.mxSession pause]; - self.isResumeDone = NO; + self.status = MatrixSDKHandlerStatusPaused; // Update user presence __weak typeof(self) weakSelf = self; [self setUserPresence:MXPresenceUnavailable andStatusMessage:nil completion:^{ @@ -268,17 +311,26 @@ static MatrixSDKHandler *sharedHandler = nil; weakSelf.bgTask = UIBackgroundTaskInvalid; NSLog(@">>>>> background pause task finished"); }]; + } else { + // Cancel pending actions + [[NSNotificationCenter defaultCenter] removeObserver:reachabilityObserver]; + reachabilityObserver = nil; + [initialServerSyncTimer invalidate]; + initialServerSyncTimer = nil; } } - (void)resume { - if (self.mxSession && self.status == MatrixSDKHandlerStatusServerSyncDone) { - if (!self.isResumeDone) { + if (self.mxSession) { + if (self.status == MatrixSDKHandlerStatusPaused) { // Resume SDK and update user presence [self.mxSession resume:^{ [self setUserPresence:MXPresenceOnline andStatusMessage:nil completion:nil]; - self.isResumeDone = YES; + self.status = MatrixSDKHandlerStatusServerSyncDone; }]; + } else if (self.status == MatrixSDKHandlerStatusStoreDataReady) { + // The session initialisation was uncompleted, we try to complete it here. + [self launchInitialServerSync]; } if (_bgTask) { @@ -302,9 +354,8 @@ static MatrixSDKHandler *sharedHandler = nil; // Keep userLogin, homeServerUrl } -- (void)forceInitialSync:(BOOL)clearCache { - if (self.status == MatrixSDKHandlerStatusServerSyncDone || self.status == MatrixSDKHandlerStatusStoreDataReady) { - self.status = MatrixSDKHandlerStatusLogged; +- (void)reload:(BOOL)clearCache { + if (self.mxSession) { [self closeSession]; notifyOpenSessionFailure = NO; @@ -391,6 +442,47 @@ static MatrixSDKHandler *sharedHandler = nil; #pragma mark - Properties +- (void)setStatus:(MatrixSDKHandlerStatus)status { + // Remove potential reachability observer + if (reachabilityObserver) { + [[NSNotificationCenter defaultCenter] removeObserver:reachabilityObserver]; + reachabilityObserver = nil; + } + + // Update activity flag + switch (status) { + case MatrixSDKHandlerStatusLoggedOut: + case MatrixSDKHandlerStatusStoreDataReady: + case MatrixSDKHandlerStatusServerSyncDone: { + self.isActivityInProgress = NO; + break; + } + case MatrixSDKHandlerStatusLogged: + case MatrixSDKHandlerStatusServerSyncInProgress: { + self.isActivityInProgress = YES; + break; + } + case MatrixSDKHandlerStatusPaused: { + // Here the app is resuming, the activity is in progress except if the network is unreachable + AFNetworkReachabilityStatus reachabilityStatus = [AFNetworkReachabilityManager sharedManager].networkReachabilityStatus; + self.isActivityInProgress = (reachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi || reachabilityStatus ==AFNetworkReachabilityStatusReachableViaWWAN); + + // Add observer to update handler activity according to reachability. + reachabilityObserver = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingReachabilityDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { + NSNumber *staturItem = note.userInfo[AFNetworkingReachabilityNotificationStatusItem]; + if (staturItem) { + AFNetworkReachabilityStatus reachabilityStatus = staturItem.integerValue; + self.isActivityInProgress = (reachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi || reachabilityStatus ==AFNetworkReachabilityStatusReachableViaWWAN); + } + }]; + break; + } + } + + // Report status + _status = status; +} + - (NSString *)homeServerURL { return [[NSUserDefaults standardUserDefaults] objectForKey:@"homeserverurl"]; } @@ -478,7 +570,6 @@ static MatrixSDKHandler *sharedHandler = nil; [self openSession]; } else { [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"accesstoken"]; - self.status = MatrixSDKHandlerStatusLoggedOut; [self closeSession]; } [[NSUserDefaults standardUserDefaults] synchronize]; @@ -565,8 +656,7 @@ static MatrixSDKHandler *sharedHandler = nil; NSMutableArray* matrixIDs = [[NSMutableArray alloc] init]; MatrixSDKHandler *mxHandler = [MatrixSDKHandler sharedHandler]; - if ((mxHandler.status == MatrixSDKHandlerStatusStoreDataReady) || (mxHandler.status == MatrixSDKHandlerStatusServerSyncDone)) { - + if (mxHandler.mxSession) { NSArray *recentEvents = [NSMutableArray arrayWithArray:[mxHandler.mxSession recentsWithTypeIn:mxHandler.eventsFilterForMessages]]; for (MXEvent *mxEvent in recentEvents) { diff --git a/matrixConsole/AppDelegate.m b/matrixConsole/AppDelegate.m index 89c974e1f..c89af493d 100644 --- a/matrixConsole/AppDelegate.m +++ b/matrixConsole/AppDelegate.m @@ -23,6 +23,8 @@ #import "SettingsViewController.h" #import "ContactManager.h" +#import "AFNetworkReachabilityManager.h" + #define MAKE_STRING(x) #x #define MAKE_NS_STRING(x) @MAKE_STRING(x) @@ -70,7 +72,7 @@ return _build; } -#pragma mark - +#pragma mark - UIApplicationDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. @@ -141,6 +143,9 @@ // 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. + // Stop reachability monitoring + [[AFNetworkReachabilityManager sharedManager] stopMonitoring]; + // check if some media msut be released to reduce the cache size [MediaManager reduceCacheSizeToInsert:0]; // Suspend Matrix handler @@ -162,6 +167,10 @@ - (void)applicationDidBecomeActive:(UIApplication *)application { // 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. + + // Start monitoring reachability + [[AFNetworkReachabilityManager sharedManager] startMonitoring]; + // Resume Matrix handler [[MatrixSDKHandler sharedHandler] resume]; diff --git a/matrixConsole/AppSettings.m b/matrixConsole/AppSettings.m index 444948ad9..8a38d3e98 100644 --- a/matrixConsole/AppSettings.m +++ b/matrixConsole/AppSettings.m @@ -98,7 +98,7 @@ static AppSettings *sharedSettings = nil; - (void)setDisplayAllEvents:(BOOL)displayAllEvents { [[NSUserDefaults standardUserDefaults] setBool:displayAllEvents forKey:@"displayAllEvents"]; // Flush and restore Matrix data - [[MatrixSDKHandler sharedHandler] forceInitialSync:NO]; + [[MatrixSDKHandler sharedHandler] reload:NO]; } - (BOOL)hideRedactedInformation { diff --git a/matrixConsole/ViewController/AuthenticationViewController.m b/matrixConsole/ViewController/AuthenticationViewController.m index 593fbfaed..c4f2088ea 100644 --- a/matrixConsole/ViewController/AuthenticationViewController.m +++ b/matrixConsole/ViewController/AuthenticationViewController.m @@ -20,8 +20,6 @@ #import "AppDelegate.h" #import "MXCAlert.h" -#import "AFNetworkReachabilityManager.h" - @interface AuthenticationViewController () { // Current request in progress NSOperation *mxAuthFlowRequest; @@ -231,8 +229,7 @@ - (void)refreshSupportedAuthFlow { MatrixSDKHandler *mxHandler = [MatrixSDKHandler sharedHandler]; - // Stop reachability monitoring - [[AFNetworkReachabilityManager sharedManager] stopMonitoring]; + // Remove reachability observer [[NSNotificationCenter defaultCenter] removeObserver:self name:AFNetworkingReachabilityDidChangeNotification object:nil]; // Cancel protential request in progress @@ -289,13 +286,13 @@ } - (void)onFailureDuringFlowRefresh:(NSError*)error { - [_activityIndicator stopAnimating]; - if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == kCFURLErrorCancelled) { // Ignore this error return; } + [_activityIndicator stopAnimating]; + NSLog(@"GET auth flows failed: %@", error); // Alert user NSString *title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey]; @@ -321,9 +318,8 @@ if ([error.domain isEqualToString:NSURLErrorDomain]) { // Check network reachability if (error.code == NSURLErrorNotConnectedToInternet) { - // Start monitoring in order to launch a new request when network will be available + // Add reachability observer in order to launch a new request when network will be available [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReachabilityStatusChange:) name:AFNetworkingReachabilityDidChangeNotification object:nil]; - [[AFNetworkReachabilityManager sharedManager] startMonitoring]; } else if (error.code == kCFURLErrorTimedOut) { // Send a new request in 2 sec dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ diff --git a/matrixConsole/ViewController/ContactsViewController.m b/matrixConsole/ViewController/ContactsViewController.m index b1eb22018..5fe37f6a4 100644 --- a/matrixConsole/ViewController/ContactsViewController.m +++ b/matrixConsole/ViewController/ContactsViewController.m @@ -245,10 +245,9 @@ NSString *const kInvitationMessage = @"I'd like to chat with you with matrix. Pl } - (void)updateSectionedMatrixContacts { - // check if the user is already known + // Check whether mxSession is available in matrix handler MatrixSDKHandler *mxHandler = [MatrixSDKHandler sharedHandler]; - - if ((mxHandler.status != MatrixSDKHandlerStatusServerSyncDone) && (mxHandler.status != MatrixSDKHandlerStatusStoreDataReady)) { + if (!mxHandler.mxSession) { [self startActivityIndicator]; sectionedMatrixContacts = nil; } else { @@ -428,8 +427,8 @@ NSString *const kInvitationMessage = @"I'd like to chat with you with matrix. Pl MatrixSDKHandler* mxHandler = [MatrixSDKHandler sharedHandler]; - // display only if the matrix SDk is ready - if ((mxHandler.status == MatrixSDKHandlerStatusServerSyncDone) || (mxHandler.status == MatrixSDKHandlerStatusStoreDataReady)) { + // display only if the mxSession is available in matrix SDK handler + if (mxHandler.mxSession) { // only 1 matrix ID if (matrixIDs.count == 1) { NSString* matrixID = [matrixIDs objectAtIndex:0]; diff --git a/matrixConsole/ViewController/RageShakableTableViewController.h b/matrixConsole/ViewController/RageShakableTableViewController.h index 3a226211d..839b9f225 100644 --- a/matrixConsole/ViewController/RageShakableTableViewController.h +++ b/matrixConsole/ViewController/RageShakableTableViewController.h @@ -16,6 +16,8 @@ #import +#import "AFNetworkReachabilityManager.h" + @interface RageShakableTableViewController : UITableViewController @end diff --git a/matrixConsole/ViewController/RageShakableTableViewController.m b/matrixConsole/ViewController/RageShakableTableViewController.m index 0e5f76d51..b64cb3dc5 100644 --- a/matrixConsole/ViewController/RageShakableTableViewController.m +++ b/matrixConsole/ViewController/RageShakableTableViewController.m @@ -17,18 +17,71 @@ #import "RageShakableUIResponder.h" +@interface RageShakableTableViewController () { + id reachabilityObserver; +} +@end + @implementation RageShakableTableViewController - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; + [RageShakableUIResponder cancel:self]; + + if (self.navigationController) { + // The navigation bar tintColor depends on reachability status - Register reachability observer + reachabilityObserver = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingReachabilityDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) { + [self onReachabilityStatusChange]; + }]; + // Force update + [self onReachabilityStatusChange]; + } } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; + + [[NSNotificationCenter defaultCenter] removeObserver:reachabilityObserver]; [RageShakableUIResponder cancel:self]; } +#pragma mark - Reachability monitoring + +- (void)onReachabilityStatusChange { + // Retrieve the current reachability status + AFNetworkReachabilityManager *reachabilityManager = [AFNetworkReachabilityManager sharedManager]; + AFNetworkReachabilityStatus status = reachabilityManager.networkReachabilityStatus; + + // Retrieve the main navigation controller if the current view controller is embedded inside a split view controller. + UINavigationController *mainNavigationController = nil; + if (self.splitViewController) { + mainNavigationController = self.navigationController; + UIViewController *parentViewController = self.parentViewController; + while (parentViewController) { + if (parentViewController.navigationController) { + mainNavigationController = parentViewController.navigationController; + parentViewController = parentViewController.parentViewController; + } else { + break; + } + } + } + + // Update navigationBar tintColor + if (status == AFNetworkReachabilityStatusNotReachable) { + self.navigationController.navigationBar.barTintColor = [UIColor redColor]; + if (mainNavigationController) { + mainNavigationController.navigationBar.barTintColor = [UIColor redColor]; + } + } else if (status == AFNetworkReachabilityStatusReachableViaWiFi || status == AFNetworkReachabilityStatusReachableViaWWAN) { + self.navigationController.navigationBar.barTintColor = nil; + if (mainNavigationController) { + mainNavigationController.navigationBar.barTintColor = nil; + } + } +} + #pragma mark - rageshake : screenshot - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (motion == UIEventSubtypeMotionShake) { diff --git a/matrixConsole/ViewController/RageShakableViewController.h b/matrixConsole/ViewController/RageShakableViewController.h index 024d277a6..e822dc0ec 100644 --- a/matrixConsole/ViewController/RageShakableViewController.h +++ b/matrixConsole/ViewController/RageShakableViewController.h @@ -16,6 +16,8 @@ #import +#import "AFNetworkReachabilityManager.h" + @interface RageShakableViewController : UIViewController @end diff --git a/matrixConsole/ViewController/RageShakableViewController.m b/matrixConsole/ViewController/RageShakableViewController.m index 3ff1b3fea..2dd356435 100644 --- a/matrixConsole/ViewController/RageShakableViewController.m +++ b/matrixConsole/ViewController/RageShakableViewController.m @@ -17,18 +17,71 @@ #import "RageShakableUIResponder.h" +@interface RageShakableViewController () { + id reachabilityObserver; +} +@end + @implementation RageShakableViewController - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; + [RageShakableUIResponder cancel:self]; + + if (self.navigationController) { + // The navigation bar tintColor depends on reachability status - Register reachability observer + reachabilityObserver = [[NSNotificationCenter defaultCenter] addObserverForName:AFNetworkingReachabilityDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) { + [self onReachabilityStatusChange]; + }]; + // Force update + [self onReachabilityStatusChange]; + } } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; + + [[NSNotificationCenter defaultCenter] removeObserver:reachabilityObserver]; [RageShakableUIResponder cancel:self]; } +#pragma mark - Reachability monitoring + +- (void)onReachabilityStatusChange { + // Retrieve the current reachability status + AFNetworkReachabilityManager *reachabilityManager = [AFNetworkReachabilityManager sharedManager]; + AFNetworkReachabilityStatus status = reachabilityManager.networkReachabilityStatus; + + // Retrieve the main navigation controller if the current view controller is embedded inside a split view controller. + UINavigationController *mainNavigationController = nil; + if (self.splitViewController) { + mainNavigationController = self.navigationController; + UIViewController *parentViewController = self.parentViewController; + while (parentViewController) { + if (parentViewController.navigationController) { + mainNavigationController = parentViewController.navigationController; + parentViewController = parentViewController.parentViewController; + } else { + break; + } + } + } + + // Update navigationBar tintColor + if (status == AFNetworkReachabilityStatusNotReachable) { + self.navigationController.navigationBar.barTintColor = [UIColor redColor]; + if (mainNavigationController) { + mainNavigationController.navigationBar.barTintColor = [UIColor redColor]; + } + } else if (status == AFNetworkReachabilityStatusReachableViaWiFi || status == AFNetworkReachabilityStatusReachableViaWWAN) { + self.navigationController.navigationBar.barTintColor = nil; + if (mainNavigationController) { + mainNavigationController.navigationBar.barTintColor = nil; + } + } +} + #pragma mark - rageshake : screenshot - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (motion == UIEventSubtypeMotionShake) { diff --git a/matrixConsole/ViewController/RecentsViewController.m b/matrixConsole/ViewController/RecentsViewController.m index 619664325..a62fe0a6a 100644 --- a/matrixConsole/ViewController/RecentsViewController.m +++ b/matrixConsole/ViewController/RecentsViewController.m @@ -40,7 +40,7 @@ NSDateFormatter *dateFormatter; RoomViewController *currentRoomViewController; - BOOL isVisible; + BOOL shouldHideActivityIndicator; } @property (strong, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator; @@ -116,14 +116,22 @@ - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; + MatrixSDKHandler *mxHandler = [MatrixSDKHandler sharedHandler]; + [mxHandler addObserver:self forKeyPath:@"isActivityInProgress" options:0 context:nil]; + // Refresh display + shouldHideActivityIndicator = NO; + if (mxHandler.isActivityInProgress) { + [self startActivityIndicator]; + } [self configureView]; - [[MatrixSDKHandler sharedHandler] addObserver:self forKeyPath:@"isResumeDone" options:0 context:nil]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; + [[MatrixSDKHandler sharedHandler] removeObserver:self forKeyPath:@"isActivityInProgress"]; + // Leave potential editing mode [self setEditing:NO]; // Leave potential search session @@ -132,16 +140,14 @@ } // Hide activity indicator [self stopActivityIndicator]; + shouldHideActivityIndicator = YES; _preSelectedRoomId = nil; - [[MatrixSDKHandler sharedHandler] removeObserver:self forKeyPath:@"isResumeDone"]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; - isVisible = YES; - // Release potential Room ViewController if none is visible (Note: check on room visibility is required to handle correctly splitViewController) if ([AppDelegate theDelegate].masterTabBarController.visibleRoomId == nil && currentRoomViewController) { currentRoomViewController.roomId = nil; @@ -149,12 +155,6 @@ } } -- (void)viewDidDisappear:(BOOL)animated { - [super viewDidDisappear:animated]; - - isVisible = NO; -} - #pragma mark - - (void)setPreSelectedRoomId:(NSString *)roomId { @@ -216,12 +216,11 @@ - (void)configureView { MatrixSDKHandler *mxHandler = [MatrixSDKHandler sharedHandler]; - [self startActivityIndicator]; [[NSNotificationCenter defaultCenter] removeObserver:self name:kRecentRoomUpdatedByBackPagination object:nil]; if (mxHandler.mxSession) { // Check matrix handler status - if (mxHandler.status == MatrixSDKHandlerStatusStoreDataReady) { + if (mxHandler.status == MatrixSDKHandlerStatusStoreDataReady || mxHandler.status == MatrixSDKHandlerStatusServerSyncInProgress) { // Server sync is not complete yet if (!recents) { // Retrieve recents from local storage (some data may not be up-to-date) @@ -339,15 +338,13 @@ }]; } // else nothing to do - } else { + } else if (mxHandler.status != MatrixSDKHandlerStatusPaused) { + // Here status is MatrixSDKHandlerStatusLoggedOut or MatrixSDKHandlerStatusLogged - Reset recents recents = nil; } // Reload table [self.tableView reloadData]; - if ([mxHandler isResumeDone]) { - [self stopActivityIndicator]; - } // Check whether a room is preselected if (_preSelectedRoomId) { @@ -355,7 +352,6 @@ } } else { if (mxHandler.status == MatrixSDKHandlerStatusLoggedOut) { - [self stopActivityIndicator]; // Update title unreadCount = 0; [self updateTitleView]; @@ -462,16 +458,12 @@ if ([@"status" isEqualToString:keyPath]) { dispatch_async(dispatch_get_main_queue(), ^{ [self configureView]; - // Hide the activity indicator when Recents is not visible - if (!isVisible) { - [self stopActivityIndicator]; - } }); - } else if ([@"isResumeDone" isEqualToString:keyPath]) { - if ([[MatrixSDKHandler sharedHandler] isResumeDone]) { - [self stopActivityIndicator]; - } else { + } else if ([@"isActivityInProgress" isEqualToString:keyPath]) { + if (!shouldHideActivityIndicator && [MatrixSDKHandler sharedHandler].isActivityInProgress) { [self startActivityIndicator]; + } else { + [self stopActivityIndicator]; } } } diff --git a/matrixConsole/ViewController/RoomViewController.m b/matrixConsole/ViewController/RoomViewController.m index 20b2732cb..1dd4c43b5 100644 --- a/matrixConsole/ViewController/RoomViewController.m +++ b/matrixConsole/ViewController/RoomViewController.m @@ -230,8 +230,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; _redactionListener = nil; [[AppSettings sharedSettings] removeObserver:self forKeyPath:@"hideRedactedInformation"]; [[AppSettings sharedSettings] removeObserver:self forKeyPath:@"hideUnsupportedEvents"]; - [[MatrixSDKHandler sharedHandler] removeObserver:self forKeyPath:@"status"]; - [[MatrixSDKHandler sharedHandler] removeObserver:self forKeyPath:@"isResumeDone"]; + [[MatrixSDKHandler sharedHandler] removeObserver:self forKeyPath:@"isActivityInProgress"]; } self.mxRoom = nil; @@ -606,8 +605,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; _redactionListener = nil; [[AppSettings sharedSettings] removeObserver:self forKeyPath:@"hideRedactedInformation"]; [[AppSettings sharedSettings] removeObserver:self forKeyPath:@"hideUnsupportedEvents"]; - [[MatrixSDKHandler sharedHandler] removeObserver:self forKeyPath:@"status"]; - [[MatrixSDKHandler sharedHandler] removeObserver:self forKeyPath:@"isResumeDone"]; + [[MatrixSDKHandler sharedHandler] removeObserver:self forKeyPath:@"isActivityInProgress"]; } currentTypingUsers = nil; if (typingNotifListener) { @@ -658,8 +656,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; [[AppSettings sharedSettings] addObserver:self forKeyPath:@"hideRedactedInformation" options:0 context:nil]; [[AppSettings sharedSettings] addObserver:self forKeyPath:@"hideUnsupportedEvents" options:0 context:nil]; - [mxHandler addObserver:self forKeyPath:@"status" options:0 context:nil]; - [mxHandler addObserver:self forKeyPath:@"isResumeDone" options:0 context:nil]; + [mxHandler addObserver:self forKeyPath:@"isActivityInProgress" options:0 context:nil]; // Register a listener to handle messages _messagesListener = [self.mxRoom listenToEventsOfTypes:mxHandler.eventsFilterForMessages onEvent:^(MXEvent *event, MXEventDirection direction, MXRoomState *roomState) { // Handle first live events @@ -906,7 +903,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; - (void)stopActivityIndicator { // Check whether all conditions are satisfied before stopping loading wheel MatrixSDKHandler *mxHandler = [MatrixSDKHandler sharedHandler]; - if (mxHandler.status == MatrixSDKHandlerStatusServerSyncDone && mxHandler.isResumeDone && !isBackPaginationInProgress && !isJoinRequestInProgress) { + if (!mxHandler.isActivityInProgress && !isBackPaginationInProgress && !isJoinRequestInProgress) { [_activityIndicator stopAnimating]; } } @@ -925,17 +922,11 @@ NSString *const kCmdResetUserPowerLevel = @"/deop"; #pragma mark - KVO - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if ([@"status" isEqualToString:keyPath]) { - if ([MatrixSDKHandler sharedHandler].status == MatrixSDKHandlerStatusServerSyncDone) { - [self stopActivityIndicator]; - } else { + if ([@"isActivityInProgress" isEqualToString:keyPath]) { + if ([MatrixSDKHandler sharedHandler].isActivityInProgress) { [self startActivityIndicator]; - } - } else if ([@"isResumeDone" isEqualToString:keyPath]) { - if ([[MatrixSDKHandler sharedHandler] isResumeDone]) { - [self stopActivityIndicator]; } else { - [self startActivityIndicator]; + [self stopActivityIndicator]; } } else if ((object == inputAccessoryView.superview) && ([@"frame" isEqualToString:keyPath] || [@"center" isEqualToString:keyPath])) { diff --git a/matrixConsole/ViewController/SettingsViewController.m b/matrixConsole/ViewController/SettingsViewController.m index a2ebe7866..0e39165b0 100644 --- a/matrixConsole/ViewController/SettingsViewController.m +++ b/matrixConsole/ViewController/SettingsViewController.m @@ -172,15 +172,22 @@ NSString* const kCommandsDescriptionText = @"The following commands are availabl selectedCountryCode = countryCode = [[AppSettings sharedSettings] countryCode]; + MatrixSDKHandler *mxHandler = [MatrixSDKHandler sharedHandler]; + [mxHandler addObserver:self forKeyPath:@"isActivityInProgress" options:0 context:nil]; + // Refresh display - [self startActivityIndicator]; + if (mxHandler.isActivityInProgress) { + [self startActivityIndicator]; + } [self configureView]; - [[MatrixSDKHandler sharedHandler] addObserver:self forKeyPath:@"isResumeDone" options:0 context:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAPNSHandlerHasBeenUpdated) name:kAPNSHandlerHasBeenUpdated object:nil]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; + + [[MatrixSDKHandler sharedHandler] removeObserver:self forKeyPath:@"isActivityInProgress"]; + [self stopActivityIndicator]; // if country has been updated // update the contact phonenumbers @@ -195,7 +202,6 @@ NSString* const kCommandsDescriptionText = @"The following commands are availabl } countryCode = [[AppSettings sharedSettings] countryCode]; - [[MatrixSDKHandler sharedHandler] removeObserver:self forKeyPath:@"isResumeDone"]; [[NSNotificationCenter defaultCenter] removeObserver:self name:kAPNSHandlerHasBeenUpdated object:nil]; } @@ -328,8 +334,6 @@ NSString* const kCommandsDescriptionText = @"The following commands are availabl currentDisplayName = mxHandler.mxSession.myUser.displayname; self.userDisplayName.text = currentDisplayName; - [self stopActivityIndicator]; - // Register listener to update user's information userUpdateListener = [mxHandler.mxSession.myUser listenToUserUpdate:^(MXEvent *event) { // Update displayName @@ -346,7 +350,7 @@ NSString* const kCommandsDescriptionText = @"The following commands are availabl // TODO display user's presence }]; } - } else if (mxHandler.status == MatrixSDKHandlerStatusStoreDataReady) { + } else if (mxHandler.status == MatrixSDKHandlerStatusStoreDataReady || mxHandler.status == MatrixSDKHandlerStatusServerSyncInProgress) { // Set local user's information (the data may not be up-to-date) [self updateUserPicture:mxHandler.mxSession.myUser.avatarUrl force:NO]; currentDisplayName = mxHandler.mxSession.myUser.displayname; @@ -355,9 +359,6 @@ NSString* const kCommandsDescriptionText = @"The following commands are availabl [self reset]; } - if ([mxHandler isResumeDone]) { - [self stopActivityIndicator]; - } // Restore user's interactions _userPictureButton.enabled = YES; _userDisplayName.enabled = YES; @@ -438,8 +439,8 @@ NSString* const kCommandsDescriptionText = @"The following commands are availabl // Backup is complete isSavingInProgress = NO; - // Stop animation (except if the app is resuming) - if ([[MatrixSDKHandler sharedHandler] isResumeDone]) { + // Stop activity indicator except if matrix sdk handler is working + if (!mxHandler.isActivityInProgress) { [self stopActivityIndicator]; } } @@ -627,11 +628,11 @@ NSString* const kCommandsDescriptionText = @"The following commands are availabl dispatch_async(dispatch_get_main_queue(), ^{ [self configureView]; }); - } else if ([@"isResumeDone" isEqualToString:keyPath]) { - if ([[MatrixSDKHandler sharedHandler] isResumeDone] && !isSavingInProgress) { - [self stopActivityIndicator]; - } else { + } else if ([@"isActivityInProgress" isEqualToString:keyPath]) { + if ([MatrixSDKHandler sharedHandler].isActivityInProgress || isSavingInProgress) { [self startActivityIndicator]; + } else { + [self stopActivityIndicator]; } } } @@ -1112,7 +1113,7 @@ NSString* const kCommandsDescriptionText = @"The following commands are availabl // tap on clear application cache if ((indexPath.section == SETTINGS_SECTION_ROOMS_INDEX) && (indexPath.row == SETTINGS_SECTION_ROOMS_CLEAR_CACHE_INDEX)) { // clear caches - [[MatrixSDKHandler sharedHandler] forceInitialSync:YES]; + [[MatrixSDKHandler sharedHandler] reload:YES]; } else if (indexPath.section == SETTINGS_SECTION_CONTACTS_INDEX) {