Index: ios/chrome/browser/payments/payment_request_picker_view_controller.mm |
diff --git a/ios/chrome/browser/payments/payment_request_picker_view_controller.mm b/ios/chrome/browser/payments/payment_request_picker_view_controller.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..77977025349f39b4c428c908d983c7a6afc798a7 |
--- /dev/null |
+++ b/ios/chrome/browser/payments/payment_request_picker_view_controller.mm |
@@ -0,0 +1,251 @@ |
+// Copyright 2017 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/payments/payment_request_picker_view_controller.h" |
+ |
+#import "base/logging.h" |
+#import "base/mac/foundation_util.h" |
+#import "ios/chrome/browser/payments/payment_request_picker_row.h" |
+ |
+#if !defined(__has_feature) || !__has_feature(objc_arc) |
+#error "This file requires ARC support." |
+#endif |
+ |
+NSString* const kPaymentRequestPickerRowAccessibilityID = |
+ @"kPaymentRequestPickerRowAccessibilityID"; |
+NSString* const kPaymentRequestPickerSearchBarAccessibilityID = |
+ @"kPaymentRequestPickerSearchBarAccessibilityID"; |
+ |
+namespace { |
+NSString* const kPaymentRequestPickerViewControllerAccessibilityID = |
+ @"kPaymentRequestPickerViewControllerAccessibilityID"; |
+} // namespace |
+ |
+@interface PaymentRequestPickerViewController ()<UISearchResultsUpdating> |
+ |
+// Search controller that contains search bar. |
+@property(nonatomic, strong) UISearchController* searchController; |
+ |
+// Full data set displayed when tableView is not filtered. |
+@property(nonatomic, strong) NSArray<PickerRow*>* allRows; |
+ |
+// Displayed rows in the tableView. |
+@property(nonatomic, strong) NSArray<PickerRow*>* displayedRows; |
+ |
+// Selected row. |
+@property(nonatomic, strong) PickerRow* selectedRow; |
+ |
+@property(nonatomic, strong) |
+ NSDictionary<NSString*, NSArray<PickerRow*>*>* sectionTitleToSectionRowsMap; |
+ |
+@end |
+ |
+@implementation PaymentRequestPickerViewController |
+@synthesize searchController = _searchController; |
+@synthesize allRows = _allRows; |
+@synthesize displayedRows = _displayedRows; |
+@synthesize selectedRow = _selectedRow; |
+@synthesize sectionTitleToSectionRowsMap = _sectionTitleToSectionRowsMap; |
+@synthesize delegate = _delegate; |
+ |
+- (instancetype)initWithRows:(NSArray<PickerRow*>*)rows |
+ selected:(PickerRow*)selectedRow { |
+ self = [super initWithStyle:UITableViewStylePlain]; |
+ if (self) { |
+ self.allRows = [rows sortedArrayUsingComparator:^NSComparisonResult( |
+ PickerRow* row1, PickerRow* row2) { |
+ return [row1.label localizedCaseInsensitiveCompare:row2.label]; |
+ }]; |
+ self.selectedRow = selectedRow; |
+ // Default to displaying all the rows. |
+ self.displayedRows = self.allRows; |
+ } |
+ return self; |
+} |
+ |
+- (void)setDisplayedRows:(NSArray<PickerRow*>*)displayedRows { |
+ _displayedRows = displayedRows; |
+ |
+ // Update the mapping from section titles to rows in that section, for |
+ // currently displayed rows. |
+ [self updateSectionTitleToSectionRowsMap]; |
+} |
+ |
+#pragma mark - UIViewController |
+ |
+- (void)viewDidLoad { |
+ [super viewDidLoad]; |
+ |
+ self.tableView.rowHeight = 48.0f; // The same as MDCCellDefaultOneLineHeight. |
+ self.tableView.accessibilityIdentifier = |
+ kPaymentRequestPickerViewControllerAccessibilityID; |
+ |
+ self.searchController = |
+ [[UISearchController alloc] initWithSearchResultsController:nil]; |
+ self.searchController.searchResultsUpdater = self; |
+ self.searchController.dimsBackgroundDuringPresentation = NO; |
+ self.searchController.searchBar.accessibilityIdentifier = |
+ kPaymentRequestPickerSearchBarAccessibilityID; |
+ self.tableView.tableHeaderView = self.searchController.searchBar; |
+ |
+ // Presentation of searchController will walk up the view controller hierarchy |
+ // until it finds the root view controller or one that defines a presentation |
+ // context. Make this class the presentation context so that the search |
+ // controller does not present on top of the navigation controller. |
+ self.definesPresentationContext = YES; |
+} |
+ |
+#pragma mark - UITableViewDataSource |
+ |
+- (NSArray<NSString*>*)sectionIndexTitlesForTableView:(UITableView*)tableView { |
+ return [self sectionTitles]; |
+} |
+ |
+- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView { |
+ return [[self sectionTitles] count]; |
+} |
+ |
+- (NSString*)tableView:(UITableView*)tableView |
+ titleForHeaderInSection:(NSInteger)section { |
+ return [[self sectionTitles] objectAtIndex:section]; |
+} |
+ |
+- (NSInteger)tableView:(UITableView*)tableView |
+ numberOfRowsInSection:(NSInteger)section { |
+ return [[self rowsInSection:section] count]; |
+} |
+ |
+- (UITableViewCell*)tableView:(UITableView*)tableView |
+ cellForRowAtIndexPath:(NSIndexPath*)indexPath { |
+ UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; |
+ if (!cell) { |
+ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle |
+ reuseIdentifier:@"cell"]; |
+ cell.isAccessibilityElement = YES; |
+ cell.accessibilityIdentifier = kPaymentRequestPickerRowAccessibilityID; |
+ } |
+ PickerRow* row = |
+ [[self rowsInSection:indexPath.section] objectAtIndex:indexPath.row]; |
+ cell.textLabel.text = row.label; |
+ cell.accessoryType = (row == self.selectedRow) |
+ ? UITableViewCellAccessoryCheckmark |
+ : UITableViewCellAccessoryNone; |
+ if (row == self.selectedRow) |
+ cell.accessibilityTraits |= UIAccessibilityTraitSelected; |
+ else |
+ cell.accessibilityTraits &= ~UIAccessibilityTraitSelected; |
+ |
+ return cell; |
+} |
+ |
+#pragma mark - UITableViewDelegate |
+ |
+- (void)tableView:(UITableView*)tableView |
+ didSelectRowAtIndexPath:(NSIndexPath*)indexPath { |
+ if (self.selectedRow) { |
+ NSIndexPath* oldSelectedIndexPath = [self indexPathForRow:self.selectedRow]; |
+ self.selectedRow = nil; |
+ // Reload the previously selected row if it is displaying. |
+ if (oldSelectedIndexPath) { |
+ [self.tableView reloadRowsAtIndexPaths:@[ oldSelectedIndexPath ] |
+ withRowAnimation:UITableViewRowAnimationFade]; |
+ } |
+ } |
+ |
+ self.selectedRow = |
+ [[self rowsInSection:indexPath.section] objectAtIndex:indexPath.row]; |
+ // Reload the newly selected row. |
+ [self.tableView reloadRowsAtIndexPaths:@[ indexPath ] |
+ withRowAnimation:UITableViewRowAnimationFade]; |
+ |
+ [_delegate paymentRequestPickerViewController:self |
+ didSelectRow:self.selectedRow]; |
+} |
+ |
+#pragma mark - UISearchResultsUpdating |
+ |
+- (void)updateSearchResultsForSearchController: |
+ (UISearchController*)searchController { |
+ NSString* searchText = searchController.searchBar.text; |
+ |
+ // Filter |allRows| for |searchText| and reload the tableView. If |searchText| |
+ // is empty, tableView will be loaded with |allRows|. |
+ if (searchText.length != 0) { |
+ // The search is case-insensitive and ignores diacritics. |
+ NSPredicate* predicate = |
+ [NSPredicate predicateWithFormat:@"label CONTAINS[cd] %@", searchText]; |
+ self.displayedRows = [self.allRows filteredArrayUsingPredicate:predicate]; |
+ } else { |
+ self.displayedRows = self.allRows; |
+ } |
+ |
+ [self.tableView reloadData]; |
+} |
+ |
+#pragma mark - Private |
+ |
+// Creates a mapping from section titles to rows in that section, for currently |
+// displaying rows, and updates |sectionTitleToSectionRowsMap|. |
+- (void)updateSectionTitleToSectionRowsMap { |
+ NSMutableDictionary<NSString*, NSArray<PickerRow*>*>* |
+ sectionTitleToSectionRowsMap = [[NSMutableDictionary alloc] init]; |
+ |
+ for (PickerRow* row in self.displayedRows) { |
+ NSString* sectionTitle = [self sectionTitleForRow:row]; |
+ NSMutableArray<PickerRow*>* sectionRows = |
+ base::mac::ObjCCastStrict<NSMutableArray<PickerRow*>>( |
+ sectionTitleToSectionRowsMap[sectionTitle]); |
+ if (!sectionRows) |
+ sectionRows = [[NSMutableArray alloc] init]; |
+ [sectionRows addObject:row]; |
+ [sectionTitleToSectionRowsMap setObject:sectionRows forKey:sectionTitle]; |
+ } |
+ |
+ self.sectionTitleToSectionRowsMap = sectionTitleToSectionRowsMap; |
+} |
+ |
+// Returns the indexPath for |row| by calculating its section and its index |
+// within the section. Returns nil if the row is not currently displaying. |
+- (NSIndexPath*)indexPathForRow:(PickerRow*)row { |
+ NSString* sectionTitle = [self sectionTitleForRow:row]; |
+ |
+ NSInteger section = [[self sectionTitles] indexOfObject:sectionTitle]; |
+ if (section == NSNotFound) |
+ return nil; |
+ |
+ NSInteger indexInSection = |
+ [self.sectionTitleToSectionRowsMap[sectionTitle] indexOfObject:row]; |
+ if (indexInSection == NSNotFound) |
+ return nil; |
+ |
+ return [NSIndexPath indexPathForRow:indexInSection inSection:section]; |
+} |
+ |
+// Returns the titles for the displayed sections in the tableView. |
+- (NSArray<NSString*>*)sectionTitles { |
+ return [[self.sectionTitleToSectionRowsMap allKeys] |
+ sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]; |
+} |
+ |
+// Returns the displayed rows in the given section. |
+- (NSArray<PickerRow*>*)rowsInSection:(NSInteger)section { |
+ NSArray<NSString*>* sectionTitles = [self sectionTitles]; |
+ DCHECK(section >= 0 && section < static_cast<NSInteger>(sectionTitles.count)); |
+ |
+ NSString* sectionTitle = [sectionTitles objectAtIndex:section]; |
+ |
+ return self.sectionTitleToSectionRowsMap[sectionTitle]; |
+} |
+ |
+// Returns the title for the section the given row gets added to. The section |
+// title for a row is the capitalized first letter of the label for that row. |
+- (NSString*)sectionTitleForRow:(PickerRow*)row { |
+ return [[row.label substringToIndex:1] uppercaseString]; |
+} |
+ |
+- (NSString*)description { |
+ return kPaymentRequestPickerViewControllerAccessibilityID; |
+} |
+ |
+@end |