3 // SharedWebCredentialViewService
5 // Copyright (c) 2014 Apple Inc. All Rights Reserved.
8 #import "SWCViewController.h"
9 #import <UIKit/UIViewController_Private.h>
10 #import <UIKit/UIFont_Private.h>
12 #import <UIKit/UIAlertController_Private.h>
13 #import <UIKit/UITableViewCell_Private.h>
15 #include <bsm/libbsm.h>
16 #include <ipc/securityd_client.h>
17 #include "SharedWebCredential/swcagent_client.h"
19 const NSString* SWC_PASSWORD_KEY = @"spwd";
20 const NSString* SWC_ACCOUNT_KEY = @"acct";
21 const NSString* SWC_SERVER_KEY = @"srvr";
24 // SWCDictionaryAdditions
27 @interface NSDictionary (SWCDictionaryAdditions)
28 - (NSComparisonResult) compareCredentialDictionaryAscending:(NSDictionary *)other;
31 @implementation NSDictionary (SWCDictionaryAdditions)
32 - (NSComparisonResult)compareCredentialDictionaryAscending:(NSDictionary *)other
34 NSComparisonResult result;
35 NSString *str1 = [self objectForKey:SWC_ACCOUNT_KEY], *str2 = [other objectForKey:SWC_ACCOUNT_KEY];
36 if (!str1) str1 = @"";
37 if (!str2) str2 = @"";
39 // primary sort by account name
40 result = [str1 localizedCaseInsensitiveCompare:str2];
41 if (result == NSOrderedSame) {
42 // secondary sort by domain name
43 NSString *str3 = [self objectForKey:SWC_SERVER_KEY], *str4 = [other objectForKey:SWC_SERVER_KEY];
44 if (!str3) str3 = @"";
45 if (!str4) str4 = @"";
47 result = [str3 localizedCaseInsensitiveCompare:str4];
58 @interface SWCItemCell : UITableViewCell
63 UIView *_bottomLineSelected;
65 UIView *_topLineSelected;
67 BOOL _showTopSeparator;
70 - (id)initWithDictionary:(NSDictionary *)dict;
71 @property (nonatomic, readonly) id userInfo;
72 @property (nonatomic, assign) BOOL showSeparator;
75 @implementation SWCItemCell
77 @synthesize showSeparator = _showSeparator;
79 - (id)initWithDictionary:(NSDictionary *)dict
81 if ((self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil]))
85 self.selectionStyle = UITableViewCellSelectionStyleNone;
87 self.backgroundColor = [UIColor colorWithWhite:0.1 alpha:0.005];
89 self.textLabel.textColor = [UIColor blackColor];
90 self.textLabel.textAlignment = NSTextAlignmentLeft;
91 self.textLabel.AdjustsFontSizeToFitWidth = YES;
92 self.textLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
94 NSString *title = [dict objectForKey:SWC_ACCOUNT_KEY];
95 self.textLabel.text = title ? title : @"--";
97 self.detailTextLabel.textColor = [UIColor darkGrayColor];
98 self.detailTextLabel.textAlignment = NSTextAlignmentLeft;
99 self.detailTextLabel.AdjustsFontSizeToFitWidth = YES;
100 self.detailTextLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
102 NSString *subtitle = [dict objectForKey:SWC_SERVER_KEY];
103 self.detailTextLabel.text = subtitle ? subtitle : @"--";
105 self.backgroundView = [[UIView alloc] init];
106 self.backgroundView.backgroundColor = self.backgroundColor;
108 self.imageView.image = [self _checkmarkImage: NO];
109 self.imageView.hidden = YES;
116 - (void)setTicked: (BOOL) selected
118 _isTicked = selected;
121 - (void)layoutSubviews
125 CGFloat scale = [[UIScreen mainScreen] scale];
126 [_bottomLine setFrame:CGRectMake(0, self.frame.size.height - (1 / scale), self.frame.size.width, 1 / scale)];
129 if (_bottomLineSelected) {
130 CGFloat scale = [[UIScreen mainScreen] scale];
131 [_bottomLineSelected setFrame:CGRectMake(0, self.frame.size.height - (1 / scale), self.frame.size.width, 1 / scale)];
135 CGFloat scale = [[UIScreen mainScreen] scale];
136 [_topLine setFrame:CGRectMake(0, 0, self.frame.size.width, 1 / scale)];
139 if (_topLineSelected) {
140 CGFloat scale = [[UIScreen mainScreen] scale];
141 [_topLineSelected setFrame:CGRectMake(0, 0, self.frame.size.width, 1 / scale)];
146 self.imageView.hidden = NO;
148 self.imageView.hidden = YES;
151 [super layoutSubviews];
155 - (void)setShowSeparator:(BOOL)showSeparator {
156 if (_showSeparator != showSeparator) {
157 _showSeparator = showSeparator;
159 if (_showSeparator) {
161 CGRect rectZero = CGRectMake(0, 0, 0, 0);
162 _bottomLine = [[UIView alloc] initWithFrame:rectZero];
163 _bottomLine.backgroundColor = [UIColor colorWithWhite:.5 alpha:.5];
164 [self.backgroundView addSubview:_bottomLine];
166 if (!_bottomLineSelected) {
167 CGRect rectZero = CGRectMake(0, 0, 0, 0);
168 _bottomLineSelected = [[UIView alloc] initWithFrame:rectZero];
169 _bottomLineSelected.backgroundColor = [UIColor colorWithWhite:.5 alpha:.5];
170 [self.selectedBackgroundView addSubview: _bottomLineSelected];
175 [_bottomLine removeFromSuperview];
178 if (_bottomLineSelected) {
179 [_bottomLineSelected removeFromSuperview];
180 _bottomLineSelected = nil;
186 - (void)setShowTopSeparator:(BOOL)showTopSeparator {
187 if (_showTopSeparator != showTopSeparator) {
188 _showTopSeparator = showTopSeparator;
190 if (_showTopSeparator) {
192 CGRect rectZero = CGRectMake(0, 0, 0, 0);
193 _topLine = [[UIView alloc] initWithFrame:rectZero];
194 _topLine.backgroundColor = [UIColor colorWithWhite:.5 alpha:.5];
195 [self.backgroundView addSubview:_topLine];
197 if (!_topLineSelected) {
198 CGRect rectZero = CGRectMake(0, 0, 0, 0);
199 _topLineSelected = [[UIView alloc] initWithFrame:rectZero];
200 _topLineSelected.backgroundColor = [UIColor colorWithWhite:.5 alpha:.5];
201 [self.selectedBackgroundView addSubview: _topLineSelected];
206 [_topLine removeFromSuperview];
209 if (_topLineSelected) {
210 [_topLineSelected removeFromSuperview];
211 _topLineSelected = nil;
224 @interface SWCViewController ()
226 NSMutableArray *_credentials; // array of NSDictionary
228 UILabel *_middleLabel;
230 NSDictionary *_selectedDict;
231 NSIndexPath *_selectedCell;
236 @implementation SWCViewController
238 - (NSDictionary *)selectedItem
240 return _selectedDict;
243 - (void)setCredentials:(NSArray *)inArray
245 NSMutableArray *credentials = [[NSMutableArray alloc] initWithArray:inArray];
246 [credentials sortUsingSelector:@selector(compareCredentialDictionaryAscending:)];
247 _credentials = credentials;
254 [_table setUserInteractionEnabled:YES];
257 - (UITableView *)tableView
260 _table = [[UITableView alloc] init];
261 [_table setTranslatesAutoresizingMaskIntoConstraints:NO];
262 [_table setAutoresizingMask:UIViewAutoresizingNone];
263 [_table setBackgroundColor:[UIColor clearColor]];
264 [_table setSeparatorStyle:UITableViewCellSeparatorStyleNone];
268 return (UITableView *)_table;
274 UIView* view = [[UIView alloc] init];
276 UITableView* table = [self tableView];
277 [table setDelegate: self];
278 [table setDataSource: self];
280 [view addSubview: table];
282 CFErrorRef error = NULL;
283 audit_token_t auditToken = {};
284 memset(&auditToken, 0, sizeof(auditToken));
285 CFArrayRef credentialList = swca_copy_pairs(swca_copy_pairs_request_id, &auditToken, &error);
287 NSLog(@"Unable to get accounts: %@", [(__bridge NSError*)error localizedDescription]);
290 [self setCredentials:(__bridge NSArray*)credentialList];
291 if (credentialList) {
292 CFRelease(credentialList);
296 NSDictionary* views = NSDictionaryOfVariableBindings(table);
298 if ([_credentials count] > 2)
301 NSDictionary *metrics = @{@"height":@120.0};
302 [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[table]-|" options:0 metrics:metrics views:views]];
303 [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[table(height)]-|" options:0 metrics:metrics views:views]];
305 CGFloat scale = [[UIScreen mainScreen] scale];
306 table.layer.borderWidth = 1.0 / scale;
307 table.layer.borderColor = [UIColor colorWithWhite:.5 alpha:.5].CGColor;
309 [self setPreferredContentSize:CGSizeMake(0,140)];
311 } else if ([_credentials count] == 2) {
313 NSDictionary *metrics = @{@"height":@90.0};
314 [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[table]|" options:0 metrics:metrics views:views]];
315 [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[table(height)]-|" options:0 metrics:metrics views:views]];
317 [self setPreferredContentSize:CGSizeMake(0,90)];
318 [table setScrollEnabled: NO];
320 } else { // [_credentials count] == 1
322 NSDictionary *metrics = @{@"height":@45.0};
323 [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[table]|" options:0 metrics:metrics views:views]];
324 [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[table(height)]|" options:0 metrics:metrics views:views]];
326 [self setPreferredContentSize:CGSizeMake(0,45)];
327 [table setScrollEnabled: NO];
334 -(void)viewWillAppear:(BOOL)animated
337 // Select the first cell by default
339 NSDictionary *dict = [_credentials objectAtIndex: 0];
340 _selectedDict = dict;
341 _selectedCell = [NSIndexPath indexPathForItem:0 inSection: 0];
342 SWCItemCell *cell = (SWCItemCell *)[_table cellForRowAtIndexPath: _selectedCell];
343 [cell setTicked: YES];
344 [_table selectRowAtIndexPath: _selectedCell animated: NO scrollPosition: UITableViewScrollPositionTop];
346 CFErrorRef error = NULL;
347 audit_token_t auditToken = {};
348 memset(&auditToken, 0, sizeof(auditToken));
349 bool result = swca_set_selection(swca_set_selection_request_id,
350 &auditToken, (__bridge CFDictionaryRef)dict, &error);
352 NSLog(@"Unable to select item: %@", [(__bridge NSError*)error localizedDescription]);
355 [super viewWillAppear:animated];
360 // UITableView delegate methods
363 - (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
365 return [_credentials count];
368 - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
370 NSUInteger row = indexPath.row;
372 NSDictionary *dict = [_credentials objectAtIndex:row];
373 _selectedDict = dict;
375 CFErrorRef error = NULL;
376 audit_token_t auditToken = {};
377 memset(&auditToken, 0, sizeof(auditToken));
378 bool result = swca_set_selection(swca_set_selection_request_id,
379 &auditToken, (__bridge CFDictionaryRef)dict, &error);
381 NSLog(@"Unable to select item: %@", [(__bridge NSError*)error localizedDescription]);
384 _selectedCell = indexPath;
385 SWCItemCell *cell = (SWCItemCell *)[tableView cellForRowAtIndexPath: indexPath];
386 [cell setTicked: YES];
387 [cell layoutSubviews];
390 - (void) tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
392 SWCItemCell *cell = (SWCItemCell *)[tableView cellForRowAtIndexPath: indexPath];
393 [cell setTicked: NO];
394 [cell layoutSubviews];
398 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
401 NSDictionary *dict = [_credentials objectAtIndex:[indexPath row]];
402 SWCItemCell *cell = [[SWCItemCell alloc] initWithDictionary:dict];
404 // show separator on top cell if there's only or or two items
405 if ([_credentials count] <= 2) {
406 cell.showTopSeparator = YES;
408 cell.showSeparator = YES;
410 if (indexPath.row == 0)
412 cell.showTopSeparator = YES;
417 if (_selectedCell == indexPath)
419 [cell setTicked: YES];
421 [cell setTicked: NO];