Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(333)

Unified Diff: remoting/ios/app/client_connection_view_controller.mm

Issue 2971903002: Adding error handling to the connection flow. (Closed)
Patch Set: Trying out activateConstraints. Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « remoting/ios/app/client_connection_view_controller.h ('k') | remoting/ios/app/pin_entry_view.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: remoting/ios/app/client_connection_view_controller.mm
diff --git a/remoting/ios/app/client_connection_view_controller.mm b/remoting/ios/app/client_connection_view_controller.mm
index 888ee2c358ea998c6de2cf1d4f89f90508397ad5..8e4c8a57a73bdf2a44f47a123ca6a47c1cfbbe79 100644
--- a/remoting/ios/app/client_connection_view_controller.mm
+++ b/remoting/ios/app/client_connection_view_controller.mm
@@ -12,9 +12,11 @@
#import "ios/third_party/material_components_ios/src/components/ActivityIndicator/src/MDCActivityIndicator.h"
#import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
#import "ios/third_party/material_components_ios/src/components/NavigationBar/src/MaterialNavigationBar.h"
+#import "ios/third_party/material_components_ios/src/components/Snackbar/src/MaterialSnackbar.h"
#import "remoting/ios/app/host_view_controller.h"
#import "remoting/ios/app/pin_entry_view.h"
#import "remoting/ios/app/remoting_theme.h"
+#import "remoting/ios/app/session_reconnect_view.h"
#import "remoting/ios/domain/client_session_details.h"
#import "remoting/ios/domain/host_info.h"
#import "remoting/ios/facade/remoting_authentication.h"
@@ -31,7 +33,10 @@ static const CGFloat kActivityIndicatorRadius = 33.f;
static const CGFloat kPinEntryViewWidth = 240.f;
static const CGFloat kPinEntryViewHeight = 90.f;
-static const CGFloat kCenterShift = -80.f;
+static const CGFloat kReconnectViewWidth = 120.f;
+static const CGFloat kReconnectViewHeight = 90.f;
+
+static const CGFloat kTopPadding = 240.f;
static const CGFloat kPadding = 20.f;
static const CGFloat kMargin = 20.f;
@@ -39,14 +44,20 @@ static const CGFloat kBarHeight = 58.f;
static const CGFloat kKeyboardAnimationTime = 0.3;
-@interface ClientConnectionViewController ()<PinEntryDelegate> {
+@interface ClientConnectionViewController ()<PinEntryDelegate,
+ SessionReconnectViewDelegate> {
UIImageView* _iconView;
MDCActivityIndicator* _activityIndicator;
+ NSLayoutConstraint* _activityIndicatorTopConstraintFull;
+ NSLayoutConstraint* _activityIndicatorTopConstraintKeyboard;
UILabel* _statusLabel;
MDCNavigationBar* _navBar;
PinEntryView* _pinEntryView;
+ SessionReconnectView* _reconnectView;
NSString* _remoteHostName;
RemotingClient* _client;
+ SessionErrorCode _lastError;
+ HostInfo* _hostInfo;
}
@end
@@ -57,17 +68,7 @@ static const CGFloat kKeyboardAnimationTime = 0.3;
- (instancetype)initWithHostInfo:(HostInfo*)hostInfo {
self = [super init];
if (self) {
- _client = [[RemotingClient alloc] init];
-
- __weak RemotingClient* weakClient = _client;
- [RemotingService.instance.authentication
- callbackWithAccessToken:^(RemotingAuthenticationStatus status,
- NSString* userEmail, NSString* accessToken) {
- [weakClient connectToHost:hostInfo
- username:userEmail
- accessToken:accessToken];
- }];
-
+ _hostInfo = hostInfo;
_remoteHostName = hostInfo.hostName;
// TODO(yuweih): This logic may be reused by other views.
@@ -102,73 +103,161 @@ static const CGFloat kKeyboardAnimationTime = 0.3;
constraintEqualToAnchor:[self.view trailingAnchor]],
[[_navBar heightAnchor] constraintEqualToConstant:kBarHeight],
]];
+
+ [self attemptConnectionToHost];
}
return self;
}
#pragma mark - UIViewController
-- (void)loadView {
- [super loadView];
-
+- (void)viewDidLoad {
+ [super viewDidLoad];
self.view.backgroundColor = RemotingTheme.connectionViewBackgroundColor;
_activityIndicator = [[MDCActivityIndicator alloc] initWithFrame:CGRectZero];
+ _activityIndicator.radius = kActivityIndicatorRadius;
+ _activityIndicator.trackEnabled = YES;
+ _activityIndicator.strokeWidth = kActivityIndicatorStrokeWidth;
+ _activityIndicator.cycleColors = @[ UIColor.whiteColor ];
+ _activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:_activityIndicator];
_statusLabel = [[UILabel alloc] initWithFrame:CGRectZero];
+ _statusLabel.numberOfLines = 1;
+ _statusLabel.lineBreakMode = NSLineBreakByTruncatingTail;
+ _statusLabel.textColor = [UIColor whiteColor];
+ _statusLabel.textAlignment = NSTextAlignmentCenter;
+ _statusLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:_statusLabel];
_iconView = [[UIImageView alloc] initWithFrame:CGRectZero];
- [self.view addSubview:_iconView];
-
- _pinEntryView = [[PinEntryView alloc] init];
- [self.view addSubview:_pinEntryView];
- _pinEntryView.delegate = self;
-}
-
-- (void)viewDidLoad {
- [super viewDidLoad];
-
_iconView.contentMode = UIViewContentModeCenter;
_iconView.alpha = 0.87f;
_iconView.backgroundColor = RemotingTheme.onlineHostColor;
_iconView.layer.cornerRadius = kIconRadius;
_iconView.layer.masksToBounds = YES;
_iconView.image = RemotingTheme.desktopIcon;
+ _iconView.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.view addSubview:_iconView];
- _activityIndicator.radius = kActivityIndicatorRadius;
- _activityIndicator.trackEnabled = YES;
- _activityIndicator.strokeWidth = kActivityIndicatorStrokeWidth;
- _activityIndicator.cycleColors = @[ UIColor.whiteColor ];
-
- _statusLabel.numberOfLines = 1;
- _statusLabel.lineBreakMode = NSLineBreakByTruncatingTail;
- _statusLabel.textColor = [UIColor whiteColor];
- _statusLabel.textAlignment = NSTextAlignmentCenter;
+ _reconnectView = [[SessionReconnectView alloc] initWithFrame:CGRectZero];
+ _reconnectView.hidden = YES;
+ _reconnectView.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.view addSubview:_reconnectView];
+ _reconnectView.delegate = self;
+ _pinEntryView = [[PinEntryView alloc] init];
_pinEntryView.hidden = YES;
-}
-
-- (void)viewWillLayoutSubviews {
- [super viewWillLayoutSubviews];
+ _pinEntryView.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.view addSubview:_pinEntryView];
+ _pinEntryView.delegate = self;
- _iconView.frame = CGRectMake(0, 0, kIconRadius * 2, kIconRadius * 2.f);
- _iconView.center =
- CGPointMake(self.view.center.x, self.view.center.y + kCenterShift);
+ [self
+ initializeLayoutConstraintsWithViews:NSDictionaryOfVariableBindings(
+ _activityIndicator, _statusLabel,
+ _iconView, _reconnectView,
+ _pinEntryView)];
+}
+- (void)initializeLayoutConstraintsWithViews:(NSDictionary*)views {
+ // Metrics to use in visual format strings.
+ NSDictionary* layoutMetrics = @{
+ @"padding" : @(kPadding),
+ @"margin" : @(kMargin),
+ @"topPadding" : @(kTopPadding),
+ @"iconDiameter" : @(kIconRadius * 2),
+ @"pinEntryViewWidth" : @(kPinEntryViewWidth),
+ @"pinEntryViewHeight" : @(kPinEntryViewHeight),
+ @"reconnectViewWidth" : @(kReconnectViewWidth),
+ @"reconnectViewHeight" : @(kReconnectViewHeight),
+ };
[_activityIndicator sizeToFit];
- _activityIndicator.center = _iconView.center;
-
- _statusLabel.frame =
- CGRectMake(kMargin, _activityIndicator.center.y + kIconRadius + kPadding,
- self.view.frame.size.width - kMargin * 2.f,
- _statusLabel.font.pointSize * _statusLabel.numberOfLines);
-
- _pinEntryView.frame = CGRectMake(
- (self.view.frame.size.width - kPinEntryViewWidth) / 2.f,
- _statusLabel.frame.origin.y + _statusLabel.frame.size.height + kPadding,
- kPinEntryViewWidth, kPinEntryViewHeight);
+ NSString* f;
+
+ // Horizontal constraints:
+
+ [self.view addConstraints:
+ [NSLayoutConstraint
+ constraintsWithVisualFormat:@"H:[_iconView(iconDiameter)]"
+ options:0
+ metrics:layoutMetrics
+ views:views]];
+
+ [self.view addConstraints:[NSLayoutConstraint
+ constraintsWithVisualFormat:
+ @"H:|-margin-[_statusLabel]-margin-|"
+ options:0
+ metrics:layoutMetrics
+ views:views]];
+
+ [self.view addConstraints:[NSLayoutConstraint
+ constraintsWithVisualFormat:
+ @"H:[_pinEntryView(pinEntryViewWidth)]"
+ options:0
+ metrics:layoutMetrics
+ views:views]];
+
+ [self.view addConstraints:[NSLayoutConstraint
+ constraintsWithVisualFormat:
+ @"H:[_reconnectView(reconnectViewWidth)]"
+ options:0
+ metrics:layoutMetrics
+ views:views]];
+
+ // Anchors:
+
+ _activityIndicatorTopConstraintFull =
+ [_activityIndicator.topAnchor constraintEqualToAnchor:self.view.topAnchor
+ constant:kTopPadding];
+ _activityIndicatorTopConstraintFull.active = YES;
+
+ [_iconView.centerYAnchor
+ constraintEqualToAnchor:_activityIndicator.centerYAnchor]
+ .active = YES;
+
+ // Vertical constraints:
+
+ [self.view addConstraints:
+ [NSLayoutConstraint
+ constraintsWithVisualFormat:@"V:[_iconView(iconDiameter)]"
+ options:0
+ metrics:layoutMetrics
+ views:views]];
+
+ [self.view addConstraints:
+ [NSLayoutConstraint
+ constraintsWithVisualFormat:
+ @"V:[_activityIndicator]-(padding)-[_statusLabel]"
+ options:NSLayoutFormatAlignAllCenterX
+ metrics:layoutMetrics
+ views:views]];
+
+ [self.view addConstraints:
+ [NSLayoutConstraint
+ constraintsWithVisualFormat:
+ @"V:[_iconView]-(padding)-[_statusLabel]"
+ options:NSLayoutFormatAlignAllCenterX
+ metrics:layoutMetrics
+ views:views]];
+
+ f = @"V:[_statusLabel]-(padding)-[_pinEntryView(pinEntryViewHeight)]";
+ [self.view addConstraints:
+ [NSLayoutConstraint
+ constraintsWithVisualFormat:f
+ options:NSLayoutFormatAlignAllCenterX
+ metrics:layoutMetrics
+ views:views]];
+
+ f = @"V:[_statusLabel]-padding-[_reconnectView(reconnectViewHeight)]";
+ [self.view addConstraints:
+ [NSLayoutConstraint
+ constraintsWithVisualFormat:f
+ options:NSLayoutFormatAlignAllCenterX
+ metrics:layoutMetrics
+ views:views]];
+
+ [self.view setNeedsUpdateConstraints];
}
- (void)viewWillAppear:(BOOL)animated {
@@ -217,29 +306,30 @@ static const CGFloat kKeyboardAnimationTime = 0.3;
CGRectValue]
.size;
- [UIView
- animateWithDuration:kKeyboardAnimationTime
- animations:^{
- CGRect f = self.view.frame;
- CGFloat newHeight =
- self.view.frame.size.height - keyboardSize.height;
- CGFloat overlap =
- newHeight - (_pinEntryView.frame.origin.y +
- _pinEntryView.frame.size.height + kPadding);
- if (overlap < 0) {
- f.origin.y = overlap;
- // TODO(yuweih): This may push the navigation bar off screen.
- self.view.frame = f;
- }
- }];
+ CGFloat newHeight = self.view.frame.size.height - keyboardSize.height;
+ CGFloat overlap = newHeight - (_pinEntryView.frame.origin.y +
+ _pinEntryView.frame.size.height + kPadding);
+ if (overlap > 0) {
+ overlap = 0;
+ }
+ _activityIndicatorTopConstraintKeyboard.active = NO;
+ _activityIndicatorTopConstraintKeyboard = [_activityIndicator.topAnchor
+ constraintEqualToAnchor:self.view.topAnchor
+ constant:kTopPadding + overlap];
+ _activityIndicatorTopConstraintFull.active = NO;
+ _activityIndicatorTopConstraintKeyboard.active = YES;
+ [UIView animateWithDuration:kKeyboardAnimationTime
+ animations:^{
+ [self.view layoutIfNeeded];
+ }];
}
- (void)keyboardWillHide:(NSNotification*)notification {
+ _activityIndicatorTopConstraintKeyboard.active = NO;
+ _activityIndicatorTopConstraintFull.active = YES;
[UIView animateWithDuration:kKeyboardAnimationTime
animations:^{
- CGRect f = self.view.frame;
- f.origin.y = 0.f;
- self.view.frame = f;
+ [self.view layoutIfNeeded];
}];
}
@@ -258,13 +348,36 @@ static const CGFloat kKeyboardAnimationTime = 0.3;
[self showConnectedState];
break;
case ClientViewClosed:
- [self dismissViewControllerAnimated:YES completion:nil];
+ [self.navigationController popToRootViewControllerAnimated:YES];
+ break;
+ case ClientViewError:
+ [self showError];
break;
}
}
+#pragma mark - SessionReconnectViewDelegate
+
+- (void)didTapReconnect {
+ [self attemptConnectionToHost];
+}
+
#pragma mark - Private
+- (void)attemptConnectionToHost {
+ _client = [[RemotingClient alloc] init];
+ __weak RemotingClient* weakClient = _client;
+ __weak HostInfo* weakHostInfo = _hostInfo;
+ [RemotingService.instance.authentication
+ callbackWithAccessToken:^(RemotingAuthenticationStatus status,
+ NSString* userEmail, NSString* accessToken) {
+ [weakClient connectToHost:weakHostInfo
+ username:userEmail
+ accessToken:accessToken];
+ }];
+ [self setState:ClientViewConnecting];
+}
+
- (void)showConnectingState {
[_pinEntryView endEditing:YES];
_statusLabel.text =
@@ -274,6 +387,7 @@ static const CGFloat kKeyboardAnimationTime = 0.3;
_activityIndicator.indicatorMode = MDCActivityIndicatorModeIndeterminate;
_activityIndicator.hidden = NO;
_pinEntryView.hidden = YES;
+ _reconnectView.hidden = YES;
[_activityIndicator startAnimating];
}
@@ -282,6 +396,7 @@ static const CGFloat kKeyboardAnimationTime = 0.3;
[_activityIndicator stopAnimating];
_activityIndicator.hidden = YES;
_pinEntryView.hidden = NO;
+ _reconnectView.hidden = YES;
// TODO(yuweih): This may be called before viewDidAppear and miss the keyboard
// callback.
@@ -299,6 +414,7 @@ static const CGFloat kKeyboardAnimationTime = 0.3;
_activityIndicator.cycleColors = @[ [UIColor greenColor] ];
[_activityIndicator startAnimating];
_activityIndicator.progress = 1.0;
+ _reconnectView.hidden = YES;
HostViewController* hostViewController =
[[HostViewController alloc] initWithClient:_client];
@@ -312,6 +428,77 @@ static const CGFloat kKeyboardAnimationTime = 0.3;
[self.navigationController setViewControllers:controllers animated:NO];
}
+- (void)showError {
+ _statusLabel.text =
+ [NSString stringWithFormat:@"Error connecting to %@", _remoteHostName];
+ _activityIndicator.progress = 0.0;
+ _pinEntryView.hidden = YES;
+ _activityIndicator.hidden = NO;
+ _activityIndicator.indicatorMode = MDCActivityIndicatorModeDeterminate;
+ _activityIndicator.cycleColors = @[ [UIColor redColor] ];
+ [_activityIndicator startAnimating];
+ _activityIndicator.progress = 1.0;
+ _reconnectView.hidden = NO;
+
+ MDCSnackbarMessage* message = nil;
+ switch (_lastError) {
+ case SessionErrorOk:
+ // Do nothing.
+ break;
+ case SessionErrorPeerIsOffline:
+ message = [MDCSnackbarMessage
+ messageWithText:@"Error: SessionErrorPeerIsOffline."];
+ break;
+ case SessionErrorSessionRejected:
+ message = [MDCSnackbarMessage
+ messageWithText:@"Error: SessionErrorSessionRejected."];
+ break;
+ case SessionErrorIncompatibleProtocol:
+ message = [MDCSnackbarMessage
+ messageWithText:@"Error: SessionErrorIncompatibleProtocol."];
+ break;
+ case SessionErrorAuthenticationFailed:
+ message = [MDCSnackbarMessage messageWithText:@"Error: Invalid Pin."];
+ [_pinEntryView clearPinEntry];
+ break;
+ case SessionErrorInvalidAccount:
+ message = [MDCSnackbarMessage
+ messageWithText:@"Error: SessionErrorInvalidAccount."];
+ break;
+ case SessionErrorChannelConnectionError:
+ message = [MDCSnackbarMessage
+ messageWithText:@"Error: SessionErrorChannelConnectionError."];
+ break;
+ case SessionErrorSignalingError:
+ message = [MDCSnackbarMessage
+ messageWithText:@"Error: SessionErrorSignalingError."];
+ break;
+ case SessionErrorSignalingTimeout:
+ message = [MDCSnackbarMessage
+ messageWithText:@"Error: SessionErrorSignalingTimeout."];
+ break;
+ case SessionErrorHostOverload:
+ message = [MDCSnackbarMessage
+ messageWithText:@"Error: SessionErrorHostOverload."];
+ break;
+ case SessionErrorMaxSessionLength:
+ message = [MDCSnackbarMessage
+ messageWithText:@"Error: SessionErrorMaxSessionLength."];
+ break;
+ case SessionErrorHostConfigurationError:
+ message = [MDCSnackbarMessage
+ messageWithText:@"Error: SessionErrorHostConfigurationError."];
+ break;
+ case SessionErrorUnknownError:
+ message = [MDCSnackbarMessage
+ messageWithText:@"Error: SessionErrorUnknownError."];
+ break;
+ }
+ if (message.text) {
+ [MDCSnackbarManager showMessage:message];
+ }
+}
+
- (void)didProvidePin:(NSString*)pin createPairing:(BOOL)createPairing {
// TODO(nicholss): There is an open question if createPairing is supported on
// iOS. Need to fingure this out.
@@ -347,7 +534,8 @@ static const CGFloat kKeyboardAnimationTime = 0.3;
state = ClientViewConnected;
break;
case SessionFailed:
- // TODO(nicholss): Implement an error screen.
+ state = ClientViewError;
+ break;
case SessionClosed:
state = ClientViewClosed;
break;
@@ -355,6 +543,7 @@ static const CGFloat kKeyboardAnimationTime = 0.3;
LOG(ERROR) << "Unknown State for Session, " << sessionDetails.state;
return;
}
+ _lastError = sessionDetails.error;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self setState:state];
}];
« no previous file with comments | « remoting/ios/app/client_connection_view_controller.h ('k') | remoting/ios/app/pin_entry_view.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698