Commit acde4b3f authored by Jérôme Lebel's avatar Jérôme Lebel Committed by Commit Bot

[iOS] Adding collapsable section for Google services settings

Adding chevron with animation to collapse and expand the 2 sections
in the Sync and Google services settings.
To support collapsable section, SettingsCollapsibleItem and
SettingsCollapsibleCell classes are added.

Screenshot:
https://drive.google.com/open?id=1Q82j3xiB7ZYihyyCiS_xalh2AFcCzKTs

Mocks:
https://docs.google.com/presentation/d/1cZfr5FGWGSy0PNaQ8uzik0alLAH-5glh1vsb030vha8/edit?ts=5aba5455#slide=id.g3ae6bac383_31_43

Bug: 827072
Cq-Include-Trybots: luci.chromium.try:ios-simulator-full-configs;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: I9f75fa1105faaf5e81133a1250a800aa5bfa2136
Reviewed-on: https://chromium-review.googlesource.com/1128758
Commit-Queue: Jérôme Lebel <jlebel@chromium.org>
Reviewed-by: default avatarRohit Rao <rohitrao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#574892}
parent c9ccc649
......@@ -26,6 +26,8 @@ source_set("cells") {
"passphrase_error_item.mm",
"password_details_item.h",
"password_details_item.mm",
"settings_collapsible_item.h",
"settings_collapsible_item.mm",
"settings_detail_item.h",
"settings_detail_item.mm",
"settings_switch_item.h",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_SETTINGS_COLLAPSIBLE_ITEM_H_
#define IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_SETTINGS_COLLAPSIBLE_ITEM_H_
#import <UIKit/UIKit.h>
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_cell.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h"
// SettingsCollapsibleItem is the model class corresponding to
// SettingsCollapsibleCell.
@interface SettingsCollapsibleItem : CollectionViewTextItem
// YES if collapsed.
@property(nonatomic, assign, getter=isCollapsed) BOOL collapsed;
@end
// MDCCollectionViewCell that displays a cell with one string (on multiple
// lines), and chevron (pointing down if expanded or pointing up if collapsed).
@interface SettingsCollapsibleCell : CollectionViewTextCell
// YES if the chevron is pointing up.
@property(nonatomic, assign, getter=isCollapsed, readonly) BOOL collapsed;
// Moves the chevron to the position collapsed/expanded.
- (void)setCollapsed:(BOOL)collapsed animated:(BOOL)animated;
@end
#endif // IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_SETTINGS_COLLAPSIBLE_ITEM_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/settings/cells/settings_collapsible_item.h"
#include "base/numerics/math_constants.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Chevron asset for expand/collapse.
NSString* const kTableViewCellChevron = @"table_view_cell_chevron";
// Rotation to apply to the chevron to point down.
constexpr CGFloat kPointingDownAngle = (90 / 180.0) * M_PI;
// Rotation to apply to the chevron to point up.
constexpr CGFloat kPointingUpAngle = -(90 / 180.0) * M_PI;
// Duration of the chevron rotation.
const NSTimeInterval kChevronFlipDuration = .15f;
} // namespace
@interface SettingsCollapsibleCell ()
// Animator that handles all cell animations.
@property(nonatomic, strong) UIViewPropertyAnimator* cellAnimator;
@end
@implementation SettingsCollapsibleItem
@synthesize collapsed = _collapsed;
- (instancetype)initWithType:(NSInteger)type {
self = [super initWithType:type];
if (self) {
self.cellClass = [SettingsCollapsibleCell class];
}
return self;
}
#pragma mark CollectionViewItem
- (void)configureCell:(SettingsCollapsibleCell*)cell {
[super configureCell:cell];
cell.accessoryView = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:kTableViewCellChevron]];
[cell setCollapsed:self.collapsed animated:NO];
}
@end
@implementation SettingsCollapsibleCell
@synthesize collapsed = _collapsed;
@synthesize cellAnimator = _cellAnimator;
- (void)prepareForReuse {
[super prepareForReuse];
if (self.cellAnimator.isRunning) {
[self.cellAnimator stopAnimation:YES];
self.cellAnimator = nil;
}
}
- (void)setCollapsed:(BOOL)collapsed animated:(BOOL)animated {
_collapsed = collapsed;
__weak SettingsCollapsibleCell* weakSelf = self;
void (^rotateChevron)(void) = ^{
CGFloat angle = self.collapsed ? kPointingUpAngle : kPointingDownAngle;
weakSelf.accessoryView.transform =
CGAffineTransformRotate(CGAffineTransformIdentity, angle);
};
if (!animated) {
rotateChevron();
} else {
self.cellAnimator = [[UIViewPropertyAnimator alloc]
initWithDuration:kChevronFlipDuration
curve:UIViewAnimationCurveLinear
animations:rotateChevron];
[self.cellAnimator startAnimation];
}
}
@end
......@@ -62,6 +62,10 @@ using chrome_test_util::SettingsDoneButton;
[self openGoogleServicesSettings];
[self assertSyncPersonalizedServicesCollapsed:YES];
[self assertNonPersonalizedServicesCollapsed:NO];
[self toggleNonPersonalizedServicesSection];
[self assertNonPersonalizedServicesCollapsed:YES];
[self toggleNonPersonalizedServicesSection];
[self assertNonPersonalizedServicesCollapsed:NO];
}
#pragma mark - Helpers
......@@ -75,6 +79,14 @@ using chrome_test_util::SettingsDoneButton;
assertWithMatcher:grey_notNil()];
}
- (void)toggleNonPersonalizedServicesSection {
[[EarlGrey
selectElementWithMatcher:
grey_accessibilityLabel(GetNSString(
IDS_IOS_GOOGLE_SERVICES_SETTINGS_NON_PERSONALIZED_SERVICES_TEXT))]
performAction:grey_tap()];
}
- (void)assertCellWithTitleID:(int)titleID detailTextID:(int)detailTextID {
[[[EarlGrey
selectElementWithMatcher:grey_accessibilityLabel(GetNSString(titleID))]
......
......@@ -5,8 +5,10 @@
#import "ios/chrome/browser/ui/settings/google_services_settings_mediator.h"
#import "ios/chrome/browser/signin/authentication_service.h"
#import "ios/chrome/browser/signin/authentication_service_factory.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h"
#import "ios/chrome/browser/ui/settings/cells/settings_collapsible_item.h"
#import "ios/chrome/browser/ui/settings/cells/sync_switch_item.h"
#include "ios/chrome/grit/ios_chromium_strings.h"
#include "ios/chrome/grit/ios_strings.h"
......@@ -98,7 +100,11 @@ typedef NS_ENUM(NSInteger, ItemType) {
[model addSectionWithIdentifier:PersonalizedSectionIdentifier];
[model setSectionIdentifier:PersonalizedSectionIdentifier
collapsedKey:kGoogleServicesSettingsPersonalizedSectionKey];
[model addItem:[self syncPersonalizationItem]
SettingsCollapsibleItem* syncPersonalizationItem =
[self syncPersonalizationItem];
syncPersonalizationItem.collapsed =
[model sectionIsCollapsed:PersonalizedSectionIdentifier];
[model addItem:syncPersonalizationItem
toSectionWithIdentifier:PersonalizedSectionIdentifier];
[model addItem:[self syncBookmarksItem]
toSectionWithIdentifier:PersonalizedSectionIdentifier];
......@@ -125,9 +131,9 @@ typedef NS_ENUM(NSInteger, ItemType) {
}
// Creates SyncPersonalizationItemType item.
- (CollectionViewItem*)syncPersonalizationItem {
CollectionViewTextItem* item =
[[CollectionViewTextItem alloc] initWithType:SyncPersonalizationItemType];
- (SettingsCollapsibleItem*)syncPersonalizationItem {
SettingsCollapsibleItem* item = [[SettingsCollapsibleItem alloc]
initWithType:SyncPersonalizationItemType];
item.text =
GetNSString(IDS_IOS_GOOGLE_SERVICES_SETTINGS_SYNC_PERSONALIZATION_TEXT);
item.numberOfTextLines = 0;
......@@ -252,7 +258,11 @@ typedef NS_ENUM(NSInteger, ItemType) {
[model addSectionWithIdentifier:NonPersonalizedSectionIdentifier];
[model setSectionIdentifier:NonPersonalizedSectionIdentifier
collapsedKey:kGoogleServicesSettingsNonPersonalizedSectionKey];
[model addItem:[self nonPersonalizedServicesItem]
SettingsCollapsibleItem* nonPersonalizedServicesItem =
[self nonPersonalizedServicesItem];
nonPersonalizedServicesItem.collapsed =
[model sectionIsCollapsed:NonPersonalizedSectionIdentifier];
[model addItem:nonPersonalizedServicesItem
toSectionWithIdentifier:NonPersonalizedSectionIdentifier];
[model addItem:[self autocompleteSearchesAndURLsItem]
toSectionWithIdentifier:NonPersonalizedSectionIdentifier];
......@@ -266,8 +276,8 @@ typedef NS_ENUM(NSInteger, ItemType) {
}
// Creates NonPersonalizedServicesItemType item.
- (CollectionViewItem*)nonPersonalizedServicesItem {
CollectionViewTextItem* item = [[CollectionViewTextItem alloc]
- (SettingsCollapsibleItem*)nonPersonalizedServicesItem {
SettingsCollapsibleItem* item = [[SettingsCollapsibleItem alloc]
initWithType:NonPersonalizedServicesItemType];
item.text = GetNSString(
IDS_IOS_GOOGLE_SERVICES_SETTINGS_NON_PERSONALIZED_SERVICES_TEXT);
......
......@@ -7,6 +7,7 @@
#import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.h"
#import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h"
#import "ios/chrome/browser/ui/settings/cells/settings_collapsible_item.h"
#import "ios/chrome/browser/ui/settings/cells/sync_switch_item.h"
#import "ios/chrome/browser/ui/settings/google_services_settings_view_controller_model_delegate.h"
#include "ios/chrome/grit/ios_strings.h"
......@@ -32,6 +33,44 @@
return self;
}
// Collapse/expand a section at |sectionIndex|.
- (void)toggleSectionWithIndexPath:(NSIndexPath*)indexPath {
DCHECK_EQ(0, indexPath.row);
NSMutableArray* cellIndexPathsToDeleteOrInsert = [NSMutableArray array];
CollectionViewModel* model = self.collectionViewModel;
NSInteger sectionIdentifier =
[model sectionIdentifierForSection:indexPath.section];
NSEnumerator* itemEnumerator =
[[model itemsInSectionWithIdentifier:sectionIdentifier] objectEnumerator];
// The first item is the title item that does the collapse/expand. This item
// should always be visible and should be skipped.
[itemEnumerator nextObject];
for (ListItem* item in itemEnumerator) {
NSIndexPath* tabIndexPath = [model indexPathForItem:item];
[cellIndexPathsToDeleteOrInsert addObject:tabIndexPath];
}
BOOL shouldCollapse = ![model sectionIsCollapsed:sectionIdentifier];
void (^tableUpdates)(void) = ^{
if (!shouldCollapse) {
[model setSection:sectionIdentifier collapsed:NO];
[self.collectionView
insertItemsAtIndexPaths:cellIndexPathsToDeleteOrInsert];
} else {
[model setSection:sectionIdentifier collapsed:YES];
[self.collectionView
deleteItemsAtIndexPaths:cellIndexPathsToDeleteOrInsert];
}
};
[self.collectionView performBatchUpdates:tableUpdates completion:nil];
SettingsCollapsibleItem* item = [model itemAtIndexPath:indexPath];
item.collapsed = shouldCollapse;
SettingsCollapsibleCell* cell = (SettingsCollapsibleCell*)[self.collectionView
cellForItemAtIndexPath:indexPath];
[cell setCollapsed:shouldCollapse animated:YES];
}
#pragma mark - CollectionViewController
- (void)loadModel {
......@@ -79,4 +118,14 @@
return ![item isKindOfClass:[SyncSwitchItem class]];
}
- (void)collectionView:(UICollectionView*)collectionView
didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
[super collectionView:collectionView didSelectItemAtIndexPath:indexPath];
CollectionViewItem* item =
[self.collectionViewModel itemAtIndexPath:indexPath];
if ([item isKindOfClass:[SettingsCollapsibleItem class]]) {
[self toggleSectionWithIndexPath:indexPath];
}
}
@end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment