1// 2// SWCViewController.m 3// SharedWebCredentialViewService 4// 5// Copyright (c) 2014 Apple Inc. All Rights Reserved. 6// 7 8#import "SWCViewController.h" 9#import <UIKit/UIViewController_Private.h> 10#import <UIKit/UIFont_Private.h> 11 12#import <UIKit/UIAlertController_Private.h> 13#import <UIKit/UITableViewCell_Private.h> 14 15#include <bsm/libbsm.h> 16#include <ipc/securityd_client.h> 17#include "SharedWebCredential/swcagent_client.h" 18 19const NSString* SWC_PASSWORD_KEY = @"spwd"; 20const NSString* SWC_ACCOUNT_KEY = @"acct"; 21const NSString* SWC_SERVER_KEY = @"srvr"; 22 23// 24// SWCDictionaryAdditions 25// 26 27@interface NSDictionary (SWCDictionaryAdditions) 28- (NSComparisonResult) compareCredentialDictionaryAscending:(NSDictionary *)other; 29@end 30 31@implementation NSDictionary (SWCDictionaryAdditions) 32- (NSComparisonResult)compareCredentialDictionaryAscending:(NSDictionary *)other 33{ 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 = @""; 38 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 = @""; 46 47 result = [str3 localizedCaseInsensitiveCompare:str4]; 48 } 49 50 return result; 51} 52@end 53 54 55// 56// SWCItemCell 57// 58@interface SWCItemCell : UITableViewCell 59{ 60 NSDictionary *_dict; 61 BOOL _isTicked; 62 UIView *_bottomLine; 63 UIView *_bottomLineSelected; 64 UIView *_topLine; 65 UIView *_topLineSelected; 66 BOOL _showSeparator; 67 BOOL _showTopSeparator; 68} 69 70- (id)initWithDictionary:(NSDictionary *)dict; 71@property (nonatomic, readonly) id userInfo; 72@property (nonatomic, assign) BOOL showSeparator; 73@end 74 75@implementation SWCItemCell 76 77@synthesize showSeparator = _showSeparator; 78 79- (id)initWithDictionary:(NSDictionary *)dict 80{ 81 if ((self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil])) 82 { 83 _dict = dict; 84 85 self.selectionStyle = UITableViewCellSelectionStyleNone; 86 87 self.backgroundColor = [UIColor colorWithWhite:0.1 alpha:0.005]; 88 89 self.textLabel.textColor = [UIColor blackColor]; 90 self.textLabel.textAlignment = NSTextAlignmentLeft; 91 self.textLabel.AdjustsFontSizeToFitWidth = YES; 92 self.textLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters; 93 94 NSString *title = [dict objectForKey:SWC_ACCOUNT_KEY]; 95 self.textLabel.text = title ? title : @"--"; 96 97 self.detailTextLabel.textColor = [UIColor darkGrayColor]; 98 self.detailTextLabel.textAlignment = NSTextAlignmentLeft; 99 self.detailTextLabel.AdjustsFontSizeToFitWidth = YES; 100 self.detailTextLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters; 101 102 NSString *subtitle = [dict objectForKey:SWC_SERVER_KEY]; 103 self.detailTextLabel.text = subtitle ? subtitle : @"--"; 104 105 self.backgroundView = [[UIView alloc] init]; 106 self.backgroundView.backgroundColor = self.backgroundColor; 107 108 self.imageView.image = [self _checkmarkImage: NO]; 109 self.imageView.hidden = YES; 110 111 } 112 113 return self; 114} 115 116- (void)setTicked: (BOOL) selected 117{ 118 _isTicked = selected; 119} 120 121- (void)layoutSubviews 122{ 123 124 if (_bottomLine) { 125 CGFloat scale = [[UIScreen mainScreen] scale]; 126 [_bottomLine setFrame:CGRectMake(0, self.frame.size.height - (1 / scale), self.frame.size.width, 1 / scale)]; 127 } 128 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)]; 132 } 133 134 if (_topLine) { 135 CGFloat scale = [[UIScreen mainScreen] scale]; 136 [_topLine setFrame:CGRectMake(0, 0, self.frame.size.width, 1 / scale)]; 137 } 138 139 if (_topLineSelected) { 140 CGFloat scale = [[UIScreen mainScreen] scale]; 141 [_topLineSelected setFrame:CGRectMake(0, 0, self.frame.size.width, 1 / scale)]; 142 } 143 144 if (_isTicked) 145 { 146 self.imageView.hidden = NO; 147 } else { 148 self.imageView.hidden = YES; 149 } 150 151 [super layoutSubviews]; 152 153} 154 155- (void)setShowSeparator:(BOOL)showSeparator { 156 if (_showSeparator != showSeparator) { 157 _showSeparator = showSeparator; 158 159 if (_showSeparator) { 160 if (!_bottomLine) { 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]; 165 } 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]; 171 } 172 173 } else { 174 if (_bottomLine) { 175 [_bottomLine removeFromSuperview]; 176 _bottomLine = nil; 177 } 178 if (_bottomLineSelected) { 179 [_bottomLineSelected removeFromSuperview]; 180 _bottomLineSelected = nil; 181 } 182 } 183 } 184} 185 186- (void)setShowTopSeparator:(BOOL)showTopSeparator { 187 if (_showTopSeparator != showTopSeparator) { 188 _showTopSeparator = showTopSeparator; 189 190 if (_showTopSeparator) { 191 if (!_topLine) { 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]; 196 } 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]; 202 } 203 204 } else { 205 if (_topLine) { 206 [_topLine removeFromSuperview]; 207 _topLine = nil; 208 } 209 if (_topLineSelected) { 210 [_topLineSelected removeFromSuperview]; 211 _topLineSelected = nil; 212 } 213 } 214 } 215} 216 217@end 218 219 220// 221// SWCViewController 222// 223 224@interface SWCViewController () 225{ 226 NSMutableArray *_credentials; // array of NSDictionary 227 UILabel *_topLabel; 228 UILabel *_middleLabel; 229 UITableView *_table; 230 NSDictionary *_selectedDict; 231 NSIndexPath *_selectedCell; 232} 233 234@end 235 236@implementation SWCViewController 237 238- (NSDictionary *)selectedItem 239{ 240 return _selectedDict; 241} 242 243- (void)setCredentials:(NSArray *)inArray 244{ 245 NSMutableArray *credentials = [[NSMutableArray alloc] initWithArray:inArray]; 246 [credentials sortUsingSelector:@selector(compareCredentialDictionaryAscending:)]; 247 _credentials = credentials; 248 if (_table) 249 [_table reloadData]; 250} 251 252- (void)_enableTable 253{ 254 [_table setUserInteractionEnabled:YES]; 255} 256 257- (UITableView *)tableView 258{ 259 if (_table == nil) { 260 _table = [[UITableView alloc] init]; 261 [_table setTranslatesAutoresizingMaskIntoConstraints:NO]; 262 [_table setAutoresizingMask:UIViewAutoresizingNone]; 263 [_table setBackgroundColor:[UIColor clearColor]]; 264 [_table setSeparatorStyle:UITableViewCellSeparatorStyleNone]; 265 } 266 [_table sizeToFit]; 267 268 return (UITableView *)_table; 269} 270 271-(void)loadView 272{ 273 274 UIView* view = [[UIView alloc] init]; 275 276 UITableView* table = [self tableView]; 277 [table setDelegate: self]; 278 [table setDataSource: self]; 279 280 [view addSubview: table]; 281 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); 286 if (error) { 287 NSLog(@"Unable to get accounts: %@", [(__bridge NSError*)error localizedDescription]); 288 } 289 290 [self setCredentials:(__bridge NSArray*)credentialList]; 291 if (credentialList) { 292 CFRelease(credentialList); 293 } 294 295 296 NSDictionary* views = NSDictionaryOfVariableBindings(table); 297 298 if ([_credentials count] > 2) 299 { 300 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]]; 304 305 CGFloat scale = [[UIScreen mainScreen] scale]; 306 table.layer.borderWidth = 1.0 / scale; 307 table.layer.borderColor = [UIColor colorWithWhite:.5 alpha:.5].CGColor; 308 309 [self setPreferredContentSize:CGSizeMake(0,140)]; 310 311 } else if ([_credentials count] == 2) { 312 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]]; 316 317 [self setPreferredContentSize:CGSizeMake(0,90)]; 318 [table setScrollEnabled: NO]; 319 320 } else { // [_credentials count] == 1 321 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]]; 325 326 [self setPreferredContentSize:CGSizeMake(0,45)]; 327 [table setScrollEnabled: NO]; 328 329 } 330 331 [self setView:view]; 332} 333 334-(void)viewWillAppear:(BOOL)animated 335{ 336 337 // Select the first cell by default 338 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]; 345 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); 351 if (!result) { 352 NSLog(@"Unable to select item: %@", [(__bridge NSError*)error localizedDescription]); 353 } 354 355 [super viewWillAppear:animated]; 356} 357 358 359// 360// UITableView delegate methods 361// 362 363- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section 364{ 365 return [_credentials count]; 366} 367 368- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 369{ 370 NSUInteger row = indexPath.row; 371 372 NSDictionary *dict = [_credentials objectAtIndex:row]; 373 _selectedDict = dict; 374 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); 380 if (!result) { 381 NSLog(@"Unable to select item: %@", [(__bridge NSError*)error localizedDescription]); 382 } 383 384 _selectedCell = indexPath; 385 SWCItemCell *cell = (SWCItemCell *)[tableView cellForRowAtIndexPath: indexPath]; 386 [cell setTicked: YES]; 387 [cell layoutSubviews]; 388} 389 390- (void) tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath 391{ 392 SWCItemCell *cell = (SWCItemCell *)[tableView cellForRowAtIndexPath: indexPath]; 393 [cell setTicked: NO]; 394 [cell layoutSubviews]; 395 396} 397 398- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 399{ 400 401 NSDictionary *dict = [_credentials objectAtIndex:[indexPath row]]; 402 SWCItemCell *cell = [[SWCItemCell alloc] initWithDictionary:dict]; 403 404 // show separator on top cell if there's only or or two items 405 if ([_credentials count] <= 2) { 406 cell.showTopSeparator = YES; 407 } else { 408 cell.showSeparator = YES; 409 410 if (indexPath.row == 0) 411 { 412 cell.showTopSeparator = YES; 413 } 414 415 } 416 417 if (_selectedCell == indexPath) 418 { 419 [cell setTicked: YES]; 420 } else { 421 [cell setTicked: NO]; 422 } 423 424 return cell; 425} 426 427 428@end 429