OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #if !defined(__has_feature) || !__has_feature(objc_arc) | 5 #if !defined(__has_feature) || !__has_feature(objc_arc) |
6 #error "This file requires ARC support." | 6 #error "This file requires ARC support." |
7 #endif | 7 #endif |
8 | 8 |
9 #import "remoting/ios/app/pin_entry_view.h" | 9 #import "remoting/ios/app/pin_entry_view.h" |
10 | 10 |
11 #import "ios/third_party/material_components_ios/src/components/Buttons/src/Mate
rialButtons.h" | 11 #import "ios/third_party/material_components_ios/src/components/Buttons/src/Mate
rialButtons.h" |
12 #import "remoting/ios/app/remoting_theme.h" | 12 #import "remoting/ios/app/remoting_theme.h" |
13 | 13 |
14 static const CGFloat kMargin = 5.f; | 14 static const CGFloat kMargin = 5.f; |
15 static const CGFloat kPadding = 6.f; | 15 static const CGFloat kPadding = 8.f; |
16 static const CGFloat kLineSpace = 12.f; | 16 static const CGFloat kLineSpace = 12.f; |
17 | 17 |
18 @interface PinEntryView () { | 18 static const int kMinPinLength = 6; |
| 19 |
| 20 @interface PinEntryView ()<UITextFieldDelegate> { |
19 UISwitch* _pairingSwitch; | 21 UISwitch* _pairingSwitch; |
20 UILabel* _pairingLabel; | 22 UILabel* _pairingLabel; |
21 MDCFloatingButton* _pinButton; | 23 MDCFloatingButton* _pinButton; |
22 UITextField* _pinEntry; | 24 UITextField* _pinEntry; |
23 } | 25 } |
24 @end | 26 @end |
25 | 27 |
26 @implementation PinEntryView | 28 @implementation PinEntryView |
27 | 29 |
28 @synthesize delegate = _delegate; | 30 @synthesize delegate = _delegate; |
29 | 31 |
30 - (id)initWithFrame:(CGRect)frame { | 32 - (id)initWithFrame:(CGRect)frame { |
31 self = [super initWithFrame:frame]; | 33 self = [super initWithFrame:frame]; |
32 if (self) { | 34 if (self) { |
33 self.backgroundColor = [UIColor clearColor]; | 35 self.backgroundColor = [UIColor clearColor]; |
34 | 36 |
35 _pairingSwitch = [[UISwitch alloc] init]; | 37 _pairingSwitch = [[UISwitch alloc] init]; |
36 _pairingSwitch.tintColor = | 38 _pairingSwitch.tintColor = |
37 [UIColor colorWithRed:1.f green:1.f blue:1.f alpha:0.5]; | 39 [UIColor colorWithRed:1.f green:1.f blue:1.f alpha:0.5]; |
38 _pairingSwitch.transform = CGAffineTransformMakeScale(0.5, 0.5); | 40 _pairingSwitch.transform = CGAffineTransformMakeScale(0.5, 0.5); |
| 41 _pairingSwitch.translatesAutoresizingMaskIntoConstraints = NO; |
39 [self addSubview:_pairingSwitch]; | 42 [self addSubview:_pairingSwitch]; |
40 | 43 |
41 _pairingLabel = [[UILabel alloc] init]; | 44 _pairingLabel = [[UILabel alloc] init]; |
42 _pairingLabel.textColor = | 45 _pairingLabel.textColor = |
43 [UIColor colorWithRed:1.f green:1.f blue:1.f alpha:0.5]; | 46 [UIColor colorWithRed:1.f green:1.f blue:1.f alpha:0.5]; |
44 _pairingLabel.font = [UIFont systemFontOfSize:12.f]; | 47 _pairingLabel.font = [UIFont systemFontOfSize:12.f]; |
45 _pairingLabel.text = @"Remember my PIN on this device."; | 48 _pairingLabel.text = @"Remember my PIN on this device."; |
| 49 _pairingLabel.translatesAutoresizingMaskIntoConstraints = NO; |
46 [self addSubview:_pairingLabel]; | 50 [self addSubview:_pairingLabel]; |
47 | 51 |
48 _pinButton = | 52 _pinButton = |
49 [MDCFloatingButton floatingButtonWithShape:MDCFloatingButtonShapeMini]; | 53 [MDCFloatingButton floatingButtonWithShape:MDCFloatingButtonShapeMini]; |
50 [_pinButton setImage:RemotingTheme.arrowIcon forState:UIControlStateNormal]; | 54 [_pinButton setImage:RemotingTheme.arrowIcon forState:UIControlStateNormal]; |
51 [_pinButton addTarget:self | 55 [_pinButton addTarget:self |
52 action:@selector(didTapPinEntry:) | 56 action:@selector(didTapPinEntry:) |
53 forControlEvents:UIControlEventTouchUpInside]; | 57 forControlEvents:UIControlEventTouchUpInside]; |
54 _pinButton.translatesAutoresizingMaskIntoConstraints = NO; | 58 _pinButton.translatesAutoresizingMaskIntoConstraints = NO; |
| 59 _pinButton.enabled = NO; |
| 60 _pinButton.translatesAutoresizingMaskIntoConstraints = NO; |
55 [self addSubview:_pinButton]; | 61 [self addSubview:_pinButton]; |
56 | 62 |
57 _pinEntry = [[UITextField alloc] init]; | 63 _pinEntry = [[UITextField alloc] init]; |
58 _pinEntry.textColor = [UIColor whiteColor]; | 64 _pinEntry.textColor = [UIColor whiteColor]; |
59 _pinEntry.secureTextEntry = YES; | 65 _pinEntry.secureTextEntry = YES; |
60 _pinEntry.keyboardType = UIKeyboardTypeNumberPad; | 66 _pinEntry.keyboardType = UIKeyboardTypeNumberPad; |
61 // TODO(nicholss): L18N this. | 67 // TODO(nicholss): L18N this. |
62 _pinEntry.attributedPlaceholder = [[NSAttributedString alloc] | 68 _pinEntry.attributedPlaceholder = [[NSAttributedString alloc] |
63 initWithString:@"Enter PIN" | 69 initWithString:@"Enter PIN" |
64 attributes:@{ | 70 attributes:@{ |
65 NSForegroundColorAttributeName : | 71 NSForegroundColorAttributeName : |
66 [UIColor colorWithRed:1.f green:1.f blue:1.f alpha:0.5] | 72 [UIColor colorWithRed:1.f green:1.f blue:1.f alpha:0.5] |
67 }]; | 73 }]; |
| 74 _pinEntry.translatesAutoresizingMaskIntoConstraints = NO; |
| 75 _pinEntry.delegate = self; |
68 [self addSubview:_pinEntry]; | 76 [self addSubview:_pinEntry]; |
| 77 |
| 78 [self |
| 79 initializeLayoutConstraintsWithViews:NSDictionaryOfVariableBindings( |
| 80 _pairingSwitch, _pairingLabel, |
| 81 _pinButton, _pinEntry)]; |
69 } | 82 } |
70 return self; | 83 return self; |
71 } | 84 } |
72 | 85 |
| 86 - (void)initializeLayoutConstraintsWithViews:(NSDictionary*)views { |
| 87 // Metrics to use in visual format strings. |
| 88 NSDictionary* layoutMetrics = @{ |
| 89 @"margin" : @(kMargin), |
| 90 @"padding" : @(kPadding), |
| 91 @"lineSpace" : @(kLineSpace), |
| 92 }; |
| 93 |
| 94 [self addConstraints: |
| 95 [NSLayoutConstraint |
| 96 constraintsWithVisualFormat: |
| 97 @"H:|-[_pinEntry]-(padding)-[_pinButton]-|" |
| 98 options:NSLayoutFormatAlignAllCenterY |
| 99 metrics:layoutMetrics |
| 100 views:views]]; |
| 101 |
| 102 [self addConstraints: |
| 103 [NSLayoutConstraint |
| 104 constraintsWithVisualFormat: |
| 105 @"H:|-[_pairingSwitch]-(padding)-[_pairingLabel]-|" |
| 106 options:NSLayoutFormatAlignAllCenterY |
| 107 metrics:layoutMetrics |
| 108 views:views]]; |
| 109 |
| 110 [self addConstraints:[NSLayoutConstraint |
| 111 constraintsWithVisualFormat: |
| 112 @"V:|-[_pinButton]-(lineSpace)-[_pairingSwitch]" |
| 113 options:0 |
| 114 metrics:layoutMetrics |
| 115 views:views]]; |
| 116 [self setNeedsUpdateConstraints]; |
| 117 } |
| 118 |
73 #pragma mark - UIView | 119 #pragma mark - UIView |
74 | 120 |
75 - (BOOL)canBecomeFirstResponder { | 121 - (BOOL)canBecomeFirstResponder { |
76 return [_pinEntry canBecomeFirstResponder]; | 122 return [_pinEntry canBecomeFirstResponder]; |
77 } | 123 } |
78 | 124 |
79 - (BOOL)becomeFirstResponder { | 125 - (BOOL)becomeFirstResponder { |
80 return [_pinEntry becomeFirstResponder]; | 126 return [_pinEntry becomeFirstResponder]; |
81 } | 127 } |
82 | 128 |
83 - (BOOL)endEditing:(BOOL)force { | 129 - (BOOL)endEditing:(BOOL)force { |
84 return [_pinEntry endEditing:force]; | 130 return [_pinEntry endEditing:force]; |
85 } | 131 } |
86 | 132 |
87 - (void)layoutSubviews { | 133 #pragma mark - UITextFieldDelegate |
88 [super layoutSubviews]; | |
89 | 134 |
90 [_pinButton sizeToFit]; | 135 - (BOOL)textField:(UITextField*)textField |
91 CGFloat buttonSize = _pinButton.frame.size.width; // Assume circle. | 136 shouldChangeCharactersInRange:(NSRange)range |
| 137 replacementString:(NSString*)string { |
| 138 if (textField == _pinEntry) { |
| 139 NSUInteger length = _pinEntry.text.length - range.length + string.length; |
| 140 _pinButton.enabled = length >= kMinPinLength; |
| 141 } |
| 142 return YES; |
| 143 } |
92 | 144 |
93 _pinEntry.frame = | 145 - (BOOL)textFieldShouldReturn:(UITextField*)textField { |
94 CGRectMake(kMargin, 0.f, | 146 NSLog(@"textFieldShouldReturn"); |
95 self.frame.size.width - kPadding - kMargin * 2.f - buttonSize, | 147 if ([_pinButton isEnabled]) { |
96 buttonSize); | 148 [self didTapPinEntry:textField]; |
| 149 return YES; |
| 150 } |
| 151 return NO; |
| 152 } |
97 | 153 |
98 [_pinButton sizeToFit]; | 154 #pragma mark - Public |
99 _pinButton.frame = | |
100 CGRectMake(self.frame.size.width - kPadding - kMargin - buttonSize, 0.f, | |
101 buttonSize, buttonSize); | |
102 | 155 |
103 [_pairingSwitch sizeToFit]; | 156 - (void)clearPinEntry { |
104 _pairingSwitch.center = CGPointMake( | 157 _pinEntry.text = @""; |
105 kMargin + _pairingSwitch.frame.size.width / 2.f, | 158 _pinButton.enabled = NO; |
106 buttonSize + _pairingSwitch.frame.size.height / 2.f + kLineSpace); | |
107 | |
108 _pairingLabel.frame = | |
109 CGRectMake(kMargin + _pairingSwitch.frame.size.width + kPadding, | |
110 buttonSize + kLineSpace, 0.f, 0.f); | |
111 [_pairingLabel sizeToFit]; | |
112 } | 159 } |
113 | 160 |
114 #pragma mark - Private | 161 #pragma mark - Private |
115 | 162 |
116 - (void)didTapPinEntry:(id)sender { | 163 - (void)didTapPinEntry:(id)sender { |
117 [_delegate didProvidePin:_pinEntry.text createPairing:_pairingSwitch.isOn]; | 164 [_delegate didProvidePin:_pinEntry.text createPairing:_pairingSwitch.isOn]; |
118 [_pinEntry endEditing:YES]; | 165 [_pinEntry endEditing:YES]; |
119 } | 166 } |
120 | 167 |
121 @end | 168 @end |
OLD | NEW |