CollectionViewController.m revision e2d1c3c0c150e511b0df83a3c49451ae773ea93a
0N/A/*
3643N/A * The contents of this file are subject to the terms of the Common Development and
0N/A * Distribution License (the License). You may not use this file except in compliance with the
0N/A * License.
0N/A *
0N/A * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
0N/A * specific language governing permission and limitations under the License.
0N/A *
0N/A * When distributing Covered Software, include this CDDL Header Notice in each file and include
0N/A * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
0N/A * Header, with the fields enclosed by brackets [] replaced by your own identifying
0N/A * information: "Portions copyright [year] [name of copyright owner]".
0N/A *
0N/A * Copyright 2016 ForgeRock AS.
0N/A *
0N/A * Portions Copyright 2014 Nathaniel McCallum, Red Hat
0N/A */
0N/A
1472N/A#import "CollectionViewController.h"
1472N/A
1472N/A#import "FRAQRScanViewController.h"
0N/A
0N/A#import "BlockActionSheet.h"
0N/A#import "FRAOathMechanismCell.h"
1879N/A#import "FRAIdentityDatabase.h"
1879N/A#import "FRAOathMechanism.h"
1879N/A
1879N/A@interface CollectionViewController () <UICollectionViewDataSource, UICollectionViewDelegate, UIPopoverControllerDelegate>
1879N/A
1879N/A@property (nonatomic, strong) UIPopoverController* popover;
1879N/A
1879N/A- (FRAOathMechanism *)mechanismForTokenAtCell:(FRAOathMechanismCell*)cell;
1879N/A- (void)generateCodeForTokenAtCell:(FRAOathMechanismCell *)cell usingMechanism:(FRAOathMechanism *)mechanism;
1879N/A- (void)showEditActionSheetForTokenAtCell:(FRAOathMechanismCell *)cell withIndexPath:(NSIndexPath *)indexPath usingMechanism:(FRAOathMechanism *)mechanism;
1879N/A
1879N/A@end
1879N/A
1879N/A@implementation CollectionViewController {
1879N/A FRAIdentityDatabase* database;
1879N/A}
1879N/A
1879N/A- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
1879N/A return 1;
1879N/A}
1879N/A
2796N/A- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
2796N/A return [database identities].count;
2796N/A}
1879N/A
0N/A- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
0N/A switch ((int) collectionView.frame.size.width) {
0N/A case 1024: // iPad
3643N/A case 768: // iPad
3643N/A return CGSizeMake(328, 96);
3643N/A
0N/A case 568: // iPhone5 landscape
0N/A case 320: // iPhone* portrait
0N/A return CGSizeMake(269, 80);
3643N/A
0N/A case 480: // iPhone4 landscape
0N/A default:
0N/A return CGSizeMake(225, 64);
0N/A }
0N/A}
0N/A
0N/A- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
0N/A NSString* name = nil;
0N/A switch ((int) collectionView.frame.size.width) {
0N/A case 1024: // iPad
0N/A case 768: // iPad
0N/A name = @"iPad";
3643N/A break;
0N/A
0N/A case 568: // iPhone5 landscape
0N/A case 320: // iPhone* portrait
0N/A name = @"iPhone5";
0N/A break;
0N/A
0N/A case 480: // iPhone4 landscape
0N/A default:
0N/A name = @"iPhone4";
113N/A break;
0N/A }
113N/A
113N/A FRAOathMechanismCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:name forIndexPath:indexPath];
481N/A FRAIdentity* identity = (FRAIdentity*) [[database identities] objectAtIndex:indexPath.row];
481N/A NSArray* mechanisms = [database mechanismsWithOwner:identity];
481N/A return [cell bind:[mechanisms objectAtIndex:0]] ? cell : nil;
0N/A}
0N/A
0N/A- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
0N/A // If the device is smaller than an iPhone5,
0N/A // then reload the data to pick up the new cell size.
0N/A // This is unfortunate because it resets token UI state.
0N/A // However, this works until we get completely dynamic resizing.
0N/A if ([[UIScreen mainScreen] bounds].size.height < 568) {
0N/A [self.collectionView reloadData];
0N/A }
0N/A}
0N/A
3643N/A- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
3643N/A // Perform animation.
0N/A [collectionView deselectItemAtIndexPath:indexPath animated:YES];
0N/A
113N/A // Get the current cell and mechanism
113N/A FRAOathMechanismCell* cell = (FRAOathMechanismCell*)[collectionView cellForItemAtIndexPath:indexPath];
0N/A FRAOathMechanism* mechanism = [self mechanismForTokenAtCell:cell];
0N/A
0N/A if (self.navigationItem.rightBarButtonItem.style == UIBarButtonItemStylePlain) {
0N/A // If we are not in edit mode, generate the token.
0N/A [self generateCodeForTokenAtCell:cell usingMechanism:mechanism];
167N/A } else {
167N/A // If we are in edit mode, show the action sheet for deletion.
167N/A [self showEditActionSheetForTokenAtCell:cell withIndexPath:indexPath usingMechanism:mechanism];
0N/A }
167N/A}
3643N/A
0N/A- (FRAOathMechanism *)mechanismForTokenAtCell:(FRAOathMechanismCell*)cell {
113N/A if (cell == nil) {
113N/A return nil;
0N/A }
0N/A return [database mechanismWithId:cell.mechanismId];
2599N/A}
0N/A
0N/A- (void)generateCodeForTokenAtCell:(FRAOathMechanismCell *)cell usingMechanism:(FRAOathMechanism *)mechanism {
0N/A // Get the code and save the mechanism state.
0N/A FRAOathCode* oathCode = mechanism.code;
0N/A [database updateMechanism:mechanism];
0N/A
0N/A // Show the code.
0N/A cell.state = oathCode;
0N/A
0N/A // Copy the code to the clipboard.
0N/A NSString* code = oathCode.currentCode;
0N/A if (code != nil) {
0N/A [[UIPasteboard generalPasteboard] setString:code];
0N/A }
0N/A}
0N/A
0N/A- (void)showEditActionSheetForTokenAtCell:(FRAOathMechanismCell *)cell withIndexPath:(NSIndexPath *)indexPath usingMechanism:(FRAOathMechanism *)mechanism {
0N/A // Create the action sheet.
0N/A BlockActionSheet* as = [[BlockActionSheet alloc] init];
342N/A
0N/A // On iPads, the sheet points to the token.
0N/A // Otherwise, add a title to make the context clear.
0N/A if (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPad) {
0N/A as.title = [NSString stringWithFormat:@"%@\n%@", cell.issuer.text, cell.label.text];
0N/A }
0N/A // Add the remaining buttons.
0N/A as.destructiveButtonIndex = [as addButtonWithTitle:NSLocalizedString(@"Delete", nil)];
1988N/A as.cancelButtonIndex = [as addButtonWithTitle:NSLocalizedString(@"Cancel", nil)];
0N/A
0N/A [as showFromRect:cell.frame inView:self.collectionView animated:YES];
0N/A as.callback = ^(NSInteger offset) {
0N/A switch (offset) {
0N/A case 1: { // Delete
0N/A BlockActionSheet* as = [[BlockActionSheet alloc] init];
0N/A
0N/A as.title = NSLocalizedString(@"Are you sure?", nil);
0N/A if (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPad) {
0N/A as.title = [NSString stringWithFormat:@"%@\n\n%@\n%@", as.title, cell.issuer.text, cell.label.text];
0N/A }
0N/A as.destructiveButtonIndex = [as addButtonWithTitle:NSLocalizedString(@"Delete", nil)];
0N/A as.cancelButtonIndex = [as addButtonWithTitle:NSLocalizedString(@"Cancel", nil)];
0N/A [as showFromRect:cell.frame inView:self.collectionView animated:YES];
0N/A as.callback = ^(NSInteger offset) {
0N/A if (offset != 1) {
0N/A return;
0N/A }
0N/A [database removeMechanismWithId:mechanism.uid];
0N/A [self.collectionView deleteItemsAtIndexPaths:@[indexPath]];
0N/A };
0N/A
0N/A break;
0N/A }
0N/A }
0N/A };
0N/A}
0N/A
0N/A- (IBAction)editClicked:(id)sender {
2599N/A UIBarButtonItem* edit = sender;
2599N/A [self.collectionView reloadData];
0N/A
0N/A switch (edit.style) {
0N/A case UIBarButtonItemStylePlain:
0N/A edit.title = NSLocalizedString(@"Done", nil);
0N/A edit.style = UIBarButtonItemStyleDone;
0N/A break;
0N/A
0N/A default:
0N/A edit.title = NSLocalizedString(@"Edit", nil);
0N/A edit.style = UIBarButtonItemStylePlain;
0N/A break;
0N/A }
0N/A}
0N/A
0N/A- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
0N/A self.popover = nil;
0N/A [self.collectionView reloadData];
0N/A}
0N/A
0N/A- (void)viewDidLoad {
0N/A [super viewDidLoad];
0N/A
0N/A // Setup store.
0N/A database = [FRAIdentityDatabase singleton];
0N/A
0N/A // Setup collection view.
0N/A self.collectionView.allowsSelection = YES;
0N/A self.collectionView.allowsMultipleSelection = NO;
0N/A self.collectionView.delegate = self;
0N/A}
0N/A
0N/A- (void)viewWillAppear:(BOOL)animated {
0N/A [super viewWillAppear:animated];
0N/A [self.collectionView reloadData];
0N/A}
0N/A
0N/A@end
0N/A