| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- /*
- * Copyright 2021 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #import "SignInViewController.h"
- @import GoogleSignIn;
- #import "AuthInspectorViewController.h"
- #import "DataPickerState.h"
- #import "DataPickerViewController.h"
- static NSString *const kSignInViewTitle = @"Sign-In Sample";
- static NSString *const kPlaceholderUserName = @"<Name>";
- static NSString *const kPlaceholderEmailAddress = @"<Email>";
- static NSString *const kPlaceholderAvatarImageName = @"PlaceholderAvatar.png";
- // Labels for the cells that have in-cell control elements.
- static NSString *const kButtonWidthCellLabel = @"Width";
- // Labels for the cells that drill down to data pickers.
- static NSString *const kColorSchemeCellLabel = @"Color scheme";
- static NSString *const kStyleCellLabel = @"Style";
- // Accessibility Identifiers.
- static NSString *const kCredentialsButtonAccessibilityIdentifier = @"Credentials";
- // DO NOT USE THIS CLIENT ID. IT WILL NOT WORK FOR YOUR APP.
- // Please use the client ID created for you by Google.
- static NSString * const kClientID =
- @"589453917038-qaoga89fitj2ukrsq27ko56fimmojac6.apps.googleusercontent.com";
- @implementation SignInViewController {
- // This is an array of arrays, each one corresponding to the cell
- // labels for its respective section.
- NSArray *_sectionCellLabels;
- // These sets contain the labels corresponding to cells that have various types (each cell either
- // drills down to another table view or contains a slider).
- NSArray *_drillDownCells;
- NSArray *_sliderCells;
- // States storing the current set of selected elements for each data picker.
- DataPickerState *_colorSchemeState;
- DataPickerState *_styleState;
- // Map that keeps track of which cell corresponds to which DataPickerState.
- NSDictionary *_drilldownCellState;
- // Configuration options for GIDSignIn.
- GIDConfiguration *_configuration;
- }
- #pragma mark - View lifecycle
- - (void)setUp {
- _sectionCellLabels = @[
- @[ kColorSchemeCellLabel, kStyleCellLabel, kButtonWidthCellLabel ]
- ];
- // Groupings of cell types.
- _drillDownCells = @[
- kColorSchemeCellLabel,
- kStyleCellLabel
- ];
- _sliderCells = @[ kButtonWidthCellLabel ];
- // Initialize data picker states.
- NSString *dictionaryPath =
- [[NSBundle mainBundle] pathForResource:@"DataPickerDictionary"
- ofType:@"plist"];
- NSDictionary *configOptionsDict = [NSDictionary dictionaryWithContentsOfFile:dictionaryPath];
- NSDictionary *colorSchemeDict = [configOptionsDict objectForKey:kColorSchemeCellLabel];
- NSDictionary *styleDict = [configOptionsDict objectForKey:kStyleCellLabel];
- _colorSchemeState = [[DataPickerState alloc] initWithDictionary:colorSchemeDict];
- _styleState = [[DataPickerState alloc] initWithDictionary:styleDict];
- _drilldownCellState = @{
- kColorSchemeCellLabel : _colorSchemeState,
- kStyleCellLabel : _styleState
- };
- // Make sure the GIDSignInButton class is linked in because references from
- // xib file doesn't count.
- [GIDSignInButton class];
- _configuration = [[GIDConfiguration alloc] initWithClientID:kClientID];
- }
- - (id)initWithNibName:(NSString *)nibNameOrNil
- bundle:(NSBundle *)nibBundleOrNil {
- self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
- if (self) {
- [self setUp];
- self.title = kSignInViewTitle;
- }
- return self;
- }
- - (id)initWithCoder:(NSCoder *)aDecoder {
- self = [super initWithCoder:aDecoder];
- if (self) {
- [self setUp];
- self.title = kSignInViewTitle;
- }
- return self;
- }
- - (void)viewDidLoad {
- [super viewDidLoad];
- self.credentialsButton.accessibilityIdentifier = kCredentialsButtonAccessibilityIdentifier;
- }
- - (void)viewWillAppear:(BOOL)animated {
- [self adoptUserSettings];
- [self reportAuthStatus];
- [self updateButtons];
- [self.tableView reloadData];
- [super viewWillAppear:animated];
- }
- #pragma mark - Helper methods
- // Updates the GIDSignIn shared instance and the GIDSignInButton
- // to reflect the configuration settings that the user set
- - (void)adoptUserSettings {
- // There should only be one selected color scheme
- for (NSString *scheme in _colorSchemeState.selectedCells) {
- if ([scheme isEqualToString:@"Light"]) {
- _signInButton.colorScheme = kGIDSignInButtonColorSchemeLight;
- } else {
- _signInButton.colorScheme = kGIDSignInButtonColorSchemeDark;
- }
- }
- // There should only be one selected style
- for (NSString *style in _styleState.selectedCells) {
- GIDSignInButtonStyle newStyle;
- if ([style isEqualToString:@"Standard"]) {
- newStyle = kGIDSignInButtonStyleStandard;
- self.signInButtonWidthSlider.enabled = YES;
- } else if ([style isEqualToString:@"Wide"]) {
- newStyle = kGIDSignInButtonStyleWide;
- self.signInButtonWidthSlider.enabled = YES;
- } else {
- newStyle = kGIDSignInButtonStyleIconOnly;
- self.signInButtonWidthSlider.enabled = NO;
- }
- if (self.signInButton.style != newStyle) {
- self.signInButton.style = newStyle;
- self.signInButtonWidthSlider.minimumValue = [self minimumButtonWidth];
- }
- self.signInButtonWidthSlider.value = _signInButton.frame.size.width;
- }
- }
- // Temporarily force the sign in button to adopt its minimum allowed frame
- // so that we can find out its minimum allowed width (used for setting the
- // range of the width slider).
- - (CGFloat)minimumButtonWidth {
- CGRect frame = self.signInButton.frame;
- self.signInButton.frame = CGRectZero;
- CGFloat minimumWidth = self.signInButton.frame.size.width;
- self.signInButton.frame = frame;
- return minimumWidth;
- }
- - (void)reportAuthStatus {
- GIDGoogleUser *googleUser = [GIDSignIn.sharedInstance currentUser];
- if (googleUser.authentication) {
- _signInAuthStatus.text = @"Status: Authenticated";
- } else {
- // To authenticate, use Google Sign-In button.
- _signInAuthStatus.text = @"Status: Not authenticated";
- }
- [self refreshUserInfo];
- }
- // Update the interface elements containing user data to reflect the
- // currently signed in user.
- - (void)refreshUserInfo {
- if (GIDSignIn.sharedInstance.currentUser.authentication == nil) {
- self.userName.text = kPlaceholderUserName;
- self.userEmailAddress.text = kPlaceholderEmailAddress;
- self.userAvatar.image = [UIImage imageNamed:kPlaceholderAvatarImageName];
- return;
- }
- self.userEmailAddress.text = GIDSignIn.sharedInstance.currentUser.profile.email;
- self.userName.text = GIDSignIn.sharedInstance.currentUser.profile.name;
- if (!GIDSignIn.sharedInstance.currentUser.profile.hasImage) {
- // There is no Profile Image to be loaded.
- return;
- }
- // Load avatar image asynchronously, in background
- dispatch_queue_t backgroundQueue =
- dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- __weak SignInViewController *weakSelf = self;
- NSUInteger dimension = round(self.userAvatar.frame.size.width * [[UIScreen mainScreen] scale]);
- NSURL *imageURL =
- [GIDSignIn.sharedInstance.currentUser.profile imageURLWithDimension:dimension];
- dispatch_async(backgroundQueue, ^{
- NSData *avatarData = [NSData dataWithContentsOfURL:imageURL];
- if (avatarData) {
- // Update UI from the main thread when available
- dispatch_async(dispatch_get_main_queue(), ^{
- SignInViewController *strongSelf = weakSelf;
- if (strongSelf) {
- strongSelf.userAvatar.image = [UIImage imageWithData:avatarData];
- }
- });
- }
- });
- }
- // Adjusts "Sign in", "Sign out", "Disconnect", and "Add Scopes" buttons to reflect the current
- // sign-in state (ie, the "Sign in" button becomes disabled when a user is already signed in).
- - (void)updateButtons {
- BOOL hasCurrentUser = (GIDSignIn.sharedInstance.currentUser != nil);
- self.signInButton.enabled = !hasCurrentUser;
- self.signOutButton.enabled = hasCurrentUser;
- self.disconnectButton.enabled = hasCurrentUser;
- self.addScopesButton.enabled = hasCurrentUser;
- self.credentialsButton.hidden = !hasCurrentUser;
- if (hasCurrentUser) {
- self.signInButton.alpha = 0.5;
- self.signOutButton.alpha = self.disconnectButton.alpha = self.addScopesButton.alpha = 1.0;
- } else {
- self.signInButton.alpha = 1.0;
- self.signOutButton.alpha = self.disconnectButton.alpha = self.addScopesButton.alpha = 0.5;
- }
- }
- #pragma mark - IBActions
- - (IBAction)signIn:(id)sender {
- [GIDSignIn.sharedInstance signInWithConfiguration:_configuration
- presentingViewController:self
- completion:^(GIDGoogleUser *user, NSError *error) {
- if (error) {
- self->_signInAuthStatus.text =
- [NSString stringWithFormat:@"Status: Authentication error: %@", error];
- return;
- }
- [self reportAuthStatus];
- [self updateButtons];
- }];
- }
- - (IBAction)signOut:(id)sender {
- [GIDSignIn.sharedInstance signOut];
- [self reportAuthStatus];
- [self updateButtons];
- }
- - (IBAction)disconnect:(id)sender {
- [GIDSignIn.sharedInstance disconnectWithCompletion:^(NSError *error) {
- if (error) {
- self->_signInAuthStatus.text = [NSString stringWithFormat:@"Status: Failed to disconnect: %@",
- error];
- } else {
- self->_signInAuthStatus.text = [NSString stringWithFormat:@"Status: Disconnected"];
- }
- [self reportAuthStatus];
- [self updateButtons];
- }];
- }
- - (IBAction)addScopes:(id)sender {
- [GIDSignIn.sharedInstance addScopes:@[ @"https://www.googleapis.com/auth/user.birthday.read" ]
- presentingViewController:self
- completion:^(GIDGoogleUser *user, NSError *error) {
- if (error) {
- self->_signInAuthStatus.text = [NSString stringWithFormat:@"Status: Failed to add scopes: %@",
- error];
- } else {
- self->_signInAuthStatus.text = [NSString stringWithFormat:@"Status: Scopes added"];
- }
- [self refreshUserInfo];
- }];
- }
- - (IBAction)showAuthInspector:(id)sender {
- AuthInspectorViewController *authInspector = [[AuthInspectorViewController alloc] init];
- [[self navigationController] pushViewController:authInspector animated:YES];
- }
- - (IBAction)checkSignIn:(id)sender {
- [self reportAuthStatus];
- }
- - (void)changeSignInButtonWidth:(UISlider *)sender {
- CGRect frame = self.signInButton.frame;
- frame.size.width = sender.value;
- self.signInButton.frame = frame;
- }
- #pragma mark - UITableView Data Source
- - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
- return [_sectionCellLabels count];
- }
- - (NSInteger)tableView:(UITableView *)tableView
- numberOfRowsInSection:(NSInteger)section {
- return [_sectionCellLabels[section] count];
- }
- - (NSString *)tableView:(UITableView *)tableView
- titleForHeaderInSection:(NSInteger)section {
- if (section == 0) {
- return @"Sign-In Button Options";
- } else if (section == 1) {
- return @"Other Options";
- } else {
- return nil;
- }
- }
- - (BOOL)tableView:(UITableView *)tableView
- shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath {
- // Cells that drill down to other table views should be highlight-able.
- // The other cells contain control elements, so they should not be selectable.
- NSString *label = _sectionCellLabels[indexPath.section][indexPath.row];
- if ([_drillDownCells containsObject:label]) {
- return YES;
- } else {
- return NO;
- }
- }
- - (UITableViewCell *)tableView:(UITableView *)tableView
- cellForRowAtIndexPath:(NSIndexPath *)indexPath {
- static NSString * const kDrilldownCell = @"DrilldownCell";
- static NSString * const kSliderCell = @"SliderCell";
- NSString *label = _sectionCellLabels[indexPath.section][indexPath.row];
- UITableViewCell *cell;
- NSString *identifier;
- if ([_drillDownCells containsObject:label]) {
- identifier = kDrilldownCell;
- } else if ([_sliderCells containsObject:label]) {
- identifier = kSliderCell;
- }
- cell = [tableView dequeueReusableCellWithIdentifier:identifier];
- if (cell == nil) {
- cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
- reuseIdentifier:identifier];
- }
- // Assign accessibility labels to each cell row.
- cell.accessibilityLabel = label;
- if (identifier == kDrilldownCell) {
- cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
- DataPickerState *dataState = _drilldownCellState[label];
- if (dataState.multipleSelectEnabled) {
- cell.detailTextLabel.text = @"";
- } else {
- cell.detailTextLabel.text = [dataState.selectedCells anyObject];
- }
- cell.accessibilityValue = cell.detailTextLabel.text;
- } else if (identifier == kSliderCell) {
- UISlider *slider = [[UISlider alloc] initWithFrame:CGRectMake(0, 0, 150, 0)];
- slider.minimumValue = [self minimumButtonWidth];
- slider.maximumValue = 268.0;
- slider.value = self.signInButton.frame.size.width;
- slider.enabled = self.signInButton.style != kGIDSignInButtonStyleIconOnly;
- [slider addTarget:self
- action:@selector(changeSignInButtonWidth:)
- forControlEvents:UIControlEventValueChanged];
- slider.accessibilityIdentifier = [NSString stringWithFormat:@"%@ Slider", label];
- self.signInButtonWidthSlider = slider;
- cell.accessoryView = slider;
- [self.signInButtonWidthSlider sizeToFit];
- }
- cell.textLabel.text = label;
- return cell;
- }
- - (void)tableView:(UITableView *)tableView
- didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
- [tableView deselectRowAtIndexPath:indexPath animated:YES];
- UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
- NSString *label = selectedCell.textLabel.text;
- DataPickerState *dataState = [_drilldownCellState objectForKey:label];
- if (!dataState) {
- return;
- }
- DataPickerViewController *dataPicker =
- [[DataPickerViewController alloc] initWithNibName:nil
- bundle:nil
- dataState:dataState];
- dataPicker.navigationItem.title = label;
- // Force the back button title to be 'Back'
- UIBarButtonItem *newBackButton =
- [[UIBarButtonItem alloc] initWithTitle:@"Back"
- style:UIBarButtonItemStylePlain
- target:nil
- action:nil];
- [[self navigationItem] setBackBarButtonItem:newBackButton];
- [self.navigationController pushViewController:dataPicker animated:YES];
- }
- @end
|