chromium/remoting/ios/app/settings/remoting_settings_view_controller.mm

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "remoting/ios/app/settings/remoting_settings_view_controller.h"

#import <MaterialComponents/MaterialAppBar.h>
#import <MaterialComponents/MaterialButtons.h>

#import "remoting/ios/app/app_delegate.h"
#import "remoting/ios/app/remoting_theme.h"
#import "remoting/ios/app/settings/setting_option.h"
#import "remoting/ios/app/settings/settings_view_cell.h"
#import "remoting/ios/app/view_utils.h"
#include "base/check_op.h"
#include "remoting/base/string_resources.h"
#include "ui/base/l10n/l10n_util.h"

static NSString* const kReusableIdentifierItem = @"remotingSettingsVCItem";

static const CGFloat kSectionSeparatorHeight = 1.f;

@interface RemotingSettingsViewController () {
  MDCAppBarViewController* _appBarViewController;
  NSArray* _sections;
  NSMutableArray* _content;
}
@end

@implementation RemotingSettingsViewController

@synthesize delegate = _delegate;
@synthesize inputMode = _inputMode;
@synthesize shouldResizeHostToFit = _shouldResizeHostToFit;

#pragma mark - UIViewController

- (void)viewDidLoad {
  [super viewDidLoad];

  _appBarViewController = [[MDCAppBarViewController alloc] init];
  [self addChildViewController:_appBarViewController];

  self.view.backgroundColor = RemotingTheme.menuBlueColor;
  _appBarViewController.headerView.backgroundColor =
      RemotingTheme.menuBlueColor;
  MDCNavigationBarTextColorAccessibilityMutator* mutator =
      [[MDCNavigationBarTextColorAccessibilityMutator alloc] init];
  [mutator mutate:_appBarViewController.navigationBar];

  _appBarViewController.headerView.trackingScrollView = self.collectionView;
  [self.view addSubview:_appBarViewController.view];
  [_appBarViewController didMoveToParentViewController:self];

  self.collectionView.backgroundColor = RemotingTheme.menuBlueColor;

  UIBarButtonItem* closeButton =
      [[UIBarButtonItem alloc] initWithImage:RemotingTheme.closeIcon
                                       style:UIBarButtonItemStyleDone
                                      target:self
                                      action:@selector(didTapClose:)];
  remoting::SetAccessibilityInfoFromImage(closeButton);
  self.navigationItem.leftBarButtonItem = nil;
  self.navigationItem.rightBarButtonItem = closeButton;

  [self.collectionView registerClass:[SettingsViewCell class]
          forCellWithReuseIdentifier:kReusableIdentifierItem];

  [self.collectionView registerClass:[MDCCollectionViewTextCell class]
          forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
                 withReuseIdentifier:UICollectionElementKindSectionHeader];

  // A 1px height cell acting as the separator. Not being shown on the last
  // section. See also:
  // -collectionView:layout:referenceSizeForFooterInSection:
  [self.collectionView registerClass:[UICollectionViewCell class]
          forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
                 withReuseIdentifier:UICollectionElementKindSectionFooter];

  _sections = @[
    l10n_util::GetNSString(IDS_DISPLAY_OPTIONS),
    l10n_util::GetNSString(IDS_MOUSE_OPTIONS),
    l10n_util::GetNSString(IDS_KEYBOARD_OPTIONS),
    l10n_util::GetNSString(IDS_SUPPORT_MENU),
  ];
  self.styler.cellStyle = MDCCollectionViewCellStyleDefault;
  self.styler.cellBackgroundColor = UIColor.clearColor;
  self.styler.shouldHideSeparators = YES;
}

- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
  [self.navigationController setNavigationBarHidden:YES animated:animated];
  [self loadContent];
}

- (UIStatusBarStyle)preferredStatusBarStyle {
  return UIStatusBarStyleLightContent;
}

#pragma mark - UICollectionViewDataSource

- (NSInteger)numberOfSectionsInCollectionView:
    (UICollectionView*)collectionView {
  return (NSInteger)[_content count];
}

- (NSInteger)collectionView:(UICollectionView*)collectionView
     numberOfItemsInSection:(NSInteger)section {
  return (NSInteger)[_content[(NSUInteger)section] count];
}

- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
                 cellForItemAtIndexPath:(NSIndexPath*)indexPath {
  SettingOption* setting = _content[indexPath.section][indexPath.item];
  SettingsViewCell* cell = [collectionView
      dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem
                                forIndexPath:indexPath];
  [cell setSettingOption:setting];
  return cell;
}

- (CGFloat)collectionView:(UICollectionView*)collectionView
    cellHeightAtIndexPath:(NSIndexPath*)indexPath {
  SettingOption* setting = _content[indexPath.section][indexPath.item];
  switch (setting.style) {
    case OptionCheckbox:  // Fall-through
    case OptionSelector:
      return MDCCellDefaultTwoLineHeight;
    case FlatButton:  // Fall-through.
    default:
      return MDCCellDefaultOneLineHeight;
  }
}

#pragma mark - UICollectionViewDelegate

- (void)collectionView:(UICollectionView*)collectionView
    didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
  [super collectionView:collectionView didSelectItemAtIndexPath:indexPath];

  SettingOption* setting = _content[indexPath.section][indexPath.item];

  NSMutableArray* updatedIndexPaths = [NSMutableArray arrayWithCapacity:1];
  int i = 0;
  switch (setting.style) {
    case OptionCheckbox:
      setting.checked = !setting.checked;
      [updatedIndexPaths
          addObject:[NSIndexPath indexPathForItem:indexPath.item
                                        inSection:indexPath.section]];
      break;
    case OptionSelector:
      for (SettingOption* s in _content[indexPath.section]) {
        s.checked = NO;
        [updatedIndexPaths
            addObject:[NSIndexPath indexPathForItem:i
                                          inSection:indexPath.section]];
        i++;
      }
      setting.checked = YES;
      break;
    case FlatButton:
      break;
  }
  [self.collectionView reloadItemsAtIndexPaths:updatedIndexPaths];
  if (setting.action) {
    setting.action();
  }
}

- (UICollectionReusableView*)collectionView:(UICollectionView*)collectionView
          viewForSupplementaryElementOfKind:(NSString*)kind
                                atIndexPath:(NSIndexPath*)indexPath {
  if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
    MDCCollectionViewTextCell* supplementaryView =
        [collectionView dequeueReusableSupplementaryViewOfKind:kind
                                           withReuseIdentifier:kind
                                                  forIndexPath:indexPath];
    supplementaryView.contentView.backgroundColor = RemotingTheme.menuBlueColor;
    supplementaryView.textLabel.text = _sections[(NSUInteger)indexPath.section];
    supplementaryView.textLabel.textColor = RemotingTheme.menuTextColor;
    supplementaryView.isAccessibilityElement = YES;
    supplementaryView.accessibilityLabel = supplementaryView.textLabel.text;
    return supplementaryView;
  }
  DCHECK([kind isEqualToString:UICollectionElementKindSectionFooter]);
  UICollectionViewCell* view =
      [collectionView dequeueReusableSupplementaryViewOfKind:kind
                                         withReuseIdentifier:kind
                                                forIndexPath:indexPath];
  view.contentView.backgroundColor = RemotingTheme.menuSeparatorColor;
  return view;
}

#pragma mark - <UICollectionViewDelegateFlowLayout>

- (CGSize)collectionView:(UICollectionView*)collectionView
                             layout:
                                 (UICollectionViewLayout*)collectionViewLayout
    referenceSizeForHeaderInSection:(NSInteger)section {
  return CGSizeMake(collectionView.bounds.size.width,
                    MDCCellDefaultOneLineHeight);
}

- (CGSize)collectionView:(UICollectionView*)collectionView
                             layout:
                                 (UICollectionViewLayout*)collectionViewLayout
    referenceSizeForFooterInSection:(NSInteger)section {
  if (section == (NSInteger)(_sections.count - 1)) {
    // No separator for last section. Note that the footer cell will not be
    // created if 0 is returned.
    return CGSizeZero;
  }
  return CGSizeMake(collectionView.bounds.size.width, kSectionSeparatorHeight);
}

#pragma mark - Private

- (void)didTapClose:(id)button {
  [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)loadContent {
  _content = [NSMutableArray array];

  __weak RemotingSettingsViewController* weakSelf = self;

  SettingOption* resizeOption = [[SettingOption alloc] init];
  resizeOption.title = l10n_util::GetNSString(IDS_RESIZE_TO_CLIENT);
  // TODO(nicholss): I think this text changes based on value. Confirm.
  resizeOption.subtext = l10n_util::GetNSString(IDS_RESIZE_TO_CLIENT_SUBTITLE);
  resizeOption.style = OptionCheckbox;
  resizeOption.checked = self.shouldResizeHostToFit;
  __weak SettingOption* weakResizeOption = resizeOption;
  resizeOption.action = ^{
    if ([weakSelf.delegate respondsToSelector:@selector(setResizeToFit:)]) {
      [weakSelf.delegate setResizeToFit:weakResizeOption.checked];
    }
  };

  [_content addObject:@[ resizeOption ]];

  SettingOption* directMode = [[SettingOption alloc] init];
  directMode.title = l10n_util::GetNSString(IDS_SELECT_TOUCH_MODE);
  // TODO(nicholss): I think this text changes based on value. Confirm.
  directMode.subtext = l10n_util::GetNSString(IDS_TOUCH_MODE_DESCRIPTION);
  directMode.style = OptionSelector;
  directMode.checked =
      self.inputMode == remoting::GestureInterpreter::DIRECT_INPUT_MODE;
  directMode.action = ^{
    if ([weakSelf.delegate respondsToSelector:@selector(useDirectInputMode)]) {
      [weakSelf.delegate useDirectInputMode];
    }
  };

  SettingOption* trackpadMode = [[SettingOption alloc] init];
  trackpadMode.title = l10n_util::GetNSString(IDS_SELECT_TRACKPAD_MODE);
  // TODO(nicholss): I think this text changes based on value. Confirm.
  trackpadMode.subtext = l10n_util::GetNSString(IDS_TRACKPAD_MODE_DESCRIPTION);
  trackpadMode.style = OptionSelector;
  trackpadMode.checked =
      self.inputMode == remoting::GestureInterpreter::TRACKPAD_INPUT_MODE;
  trackpadMode.action = ^{
    if ([weakSelf.delegate
            respondsToSelector:@selector(useTrackpadInputMode)]) {
      [weakSelf.delegate useTrackpadInputMode];
    }
  };

  [_content addObject:@[ directMode, trackpadMode ]];

  SettingOption* ctrlAltDelOption = [[SettingOption alloc] init];
  ctrlAltDelOption.title = l10n_util::GetNSString(IDS_SEND_CTRL_ALT_DEL);
  ctrlAltDelOption.style = FlatButton;
  ctrlAltDelOption.action = ^{
    if ([weakSelf.delegate respondsToSelector:@selector(sendCtrAltDel)]) {
      [weakSelf.delegate sendCtrAltDel];
      [weakSelf dismissViewControllerAnimated:YES completion:nil];
    }
  };

  SettingOption* printScreenOption = [[SettingOption alloc] init];
  printScreenOption.title = l10n_util::GetNSString(IDS_SEND_PRINT_SCREEN);
  printScreenOption.style = FlatButton;
  printScreenOption.action = ^{
    if ([weakSelf.delegate respondsToSelector:@selector(sendPrintScreen)]) {
      [weakSelf.delegate sendPrintScreen];
      [weakSelf dismissViewControllerAnimated:YES completion:nil];
    }
  };

  [_content addObject:@[ ctrlAltDelOption, printScreenOption ]];

  SettingOption* helpCenterOption = [[SettingOption alloc] init];
  helpCenterOption.title = l10n_util::GetNSString(IDS_HELP_CENTER);
  helpCenterOption.style = FlatButton;
  helpCenterOption.action = ^{
    [AppDelegate.instance navigateToHelpCenter:weakSelf.navigationController];
    [weakSelf.navigationController setNavigationBarHidden:NO animated:YES];
  };

  // TODO(yuweih): Currently the EAGLView is not captured by the feedback tool.
  // To get it working we need to override renderInContext in CAEAGLLayer.
  SettingOption* sendFeedbackOption = [[SettingOption alloc] init];
  sendFeedbackOption.title =
      l10n_util::GetNSString(IDS_ACTIONBAR_SEND_FEEDBACK);
  sendFeedbackOption.style = FlatButton;
  sendFeedbackOption.action = ^{
    // Dismiss self so that it can capture the screenshot of HostView.
    [weakSelf dismissViewControllerAnimated:YES
                                 completion:^{
                                   [weakSelf.delegate sendFeedback];
                                 }];
  };

  [_content addObject:@[ helpCenterOption, sendFeedbackOption ]];

  DCHECK_EQ(_content.count, _sections.count);
}

@end