Index: ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.mm |
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.mm b/ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d595c8e32aaeea26a038b7870445657f875e4ed7 |
--- /dev/null |
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.mm |
@@ -0,0 +1,900 @@ |
+// Copyright 2015 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/clear_browsing_data_collection_view_controller.h" |
+ |
+#include <memory> |
+#include <string> |
+ |
+#include "base/ios/ios_util.h" |
+#include "base/ios/weak_nsobject.h" |
+#include "base/logging.h" |
+#include "base/mac/bind_objc_block.h" |
+#include "base/mac/foundation_util.h" |
+#include "base/mac/scoped_nsobject.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/metrics/histogram_macros.h" |
+#include "base/strings/string_piece.h" |
+#include "base/strings/sys_string_conversions.h" |
+#include "components/browser_sync/profile_sync_service.h" |
+#include "components/browsing_data/core/browsing_data_utils.h" |
+#include "components/browsing_data/core/counters/browsing_data_counter.h" |
+#include "components/browsing_data/core/history_notice_utils.h" |
+#include "components/browsing_data/core/pref_names.h" |
+#include "components/google/core/browser/google_util.h" |
+#include "components/history/core/browser/web_history_service.h" |
+#include "components/prefs/pref_service.h" |
+#include "components/signin/core/browser/signin_manager.h" |
+#include "components/strings/grit/components_strings.h" |
+#include "ios/chrome/browser/application_context.h" |
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
+#include "ios/chrome/browser/browsing_data/browsing_data_counter_wrapper.h" |
+#include "ios/chrome/browser/browsing_data/ios_browsing_data_counter_factory.h" |
+#include "ios/chrome/browser/browsing_data/ios_chrome_browsing_data_remover.h" |
+#include "ios/chrome/browser/chrome_url_constants.h" |
+#include "ios/chrome/browser/experimental_flags.h" |
+#include "ios/chrome/browser/history/web_history_service_factory.h" |
+#include "ios/chrome/browser/signin/signin_manager_factory.h" |
+#include "ios/chrome/browser/sync/ios_chrome_profile_sync_service_factory.h" |
+#import "ios/chrome/browser/ui/collection_view/cells/MDCCollectionViewCell+Chrome.h" |
+#import "ios/chrome/browser/ui/collection_view/cells/collection_view_detail_item.h" |
+#import "ios/chrome/browser/ui/collection_view/cells/collection_view_footer_item.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/collection_view/collection_view_model.h" |
+#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h" |
+#import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h" |
+#import "ios/chrome/browser/ui/commands/clear_browsing_data_command.h" |
+#include "ios/chrome/browser/ui/commands/ios_command_ids.h" |
+#import "ios/chrome/browser/ui/commands/open_url_command.h" |
+#import "ios/chrome/browser/ui/icons/chrome_icon.h" |
+#import "ios/chrome/browser/ui/settings/time_range_selector_collection_view_controller.h" |
+#include "ios/chrome/browser/ui/ui_util.h" |
+#import "ios/chrome/browser/ui/uikit_ui_util.h" |
+#include "ios/chrome/common/channel_info.h" |
+#include "ios/chrome/grit/ios_chromium_strings.h" |
+#include "ios/chrome/grit/ios_strings.h" |
+#include "ios/public/provider/chrome/browser/chrome_browser_provider.h" |
+#include "ios/public/provider/chrome/browser/images/branded_image_provider.h" |
+#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h" |
+#include "ui/base/l10n/l10n_util_mac.h" |
+#include "url/gurl.h" |
+ |
+NSString* const kClearBrowsingDataCollectionViewId = |
+ @"kClearBrowsingDataCollectionViewId"; |
+NSString* const kClearBrowsingHistoryCellId = @"kClearBrowsingHistoryCellId"; |
+NSString* const kClearCookiesCellId = @"kClearCookiesCellId"; |
+NSString* const kClearCacheCellId = @"kClearCacheCellId"; |
+NSString* const kClearSavedPasswordsCellId = @"kClearSavedPasswordsCellId"; |
+NSString* const kClearAutofillCellId = @"kClearAutofillCellId"; |
+ |
+namespace { |
+ |
+typedef NS_ENUM(NSInteger, SectionIdentifier) { |
+ SectionIdentifierDataTypes = kSectionIdentifierEnumZero, |
+ SectionIdentifierClearBrowsingDataButton, |
+ SectionIdentifierGoogleAccount, |
+ SectionIdentifierClearSyncAndSavedSiteData, |
+ SectionIdentifierSavedSiteData, |
+ SectionIdentifierTimeRange, |
+}; |
+ |
+typedef NS_ENUM(NSInteger, ItemType) { |
+ ItemTypeDataTypeBrowsingHistory = kItemTypeEnumZero, |
+ ItemTypeDataTypeCookiesSiteData, |
+ ItemTypeDataTypeCache, |
+ ItemTypeDataTypeSavedPasswords, |
+ ItemTypeDataTypeAutofill, |
+ ItemTypeClearBrowsingDataButton, |
+ ItemTypeFooterGoogleAccount, |
+ ItemTypeFooterGoogleAccountAndMyActivity, |
+ ItemTypeFooterSavedSiteData, |
+ ItemTypeFooterClearSyncAndSavedSiteData, |
+ ItemTypeTimeRange, |
+}; |
+ |
+const int kMaxTimesHistoryNoticeShown = 1; |
+ |
+} // namespace |
+ |
+// Collection view item identifying a clear browsing data content view. |
+@interface ClearDataItem : CollectionViewTextItem { |
+ // Data volume counter associated with the item. |
+ std::unique_ptr<BrowsingDataCounterWrapper> _counter; |
+} |
+ |
+// Mask of the data to be cleared. |
+@property(nonatomic, assign) int dataTypeMask; |
+ |
+// Pref name associated with the item. |
+@property(nonatomic, assign) const char* prefName; |
+ |
+// Sets the counter associated with the data type represented by the item. |
+- (void)setCounter:(std::unique_ptr<BrowsingDataCounterWrapper>)counter; |
+ |
+// Checks if the item has a counter. |
+- (BOOL)hasCounter; |
+ |
+// Restarts the counter. |
+- (void)restartCounter; |
+ |
+@end |
+ |
+@implementation ClearDataItem |
+ |
+@synthesize dataTypeMask = _dataTypeMask; |
+@synthesize prefName = _prefName; |
+ |
+- (void)setCounter:(std::unique_ptr<BrowsingDataCounterWrapper>)counter { |
+ _counter = std::move(counter); |
+} |
+ |
+- (BOOL)hasCounter { |
+ return !!_counter; |
+} |
+ |
+- (void)restartCounter { |
+ if (_counter) |
+ _counter->RestartCounter(); |
+} |
+ |
+@end |
+ |
+@interface ClearBrowsingDataCollectionViewController ()< |
+ TimeRangeSelectorCollectionViewControllerDelegate> { |
+ ios::ChromeBrowserState* _browserState; // weak |
+ |
+ browsing_data::TimePeriod _timePeriod; |
+ |
+ IOSChromeBrowsingDataRemover::CallbackSubscription _callbackSubscription; |
+} |
+ |
+@property(nonatomic, assign) |
+ BOOL shouldShowNoticeAboutOtherFormsOfBrowsingHistory; |
+@property(nonatomic, assign) |
+ BOOL shouldPopupDialogAboutOtherFormsOfBrowsingHistory; |
+ |
+// Displays an action sheet to the user confirming the clearing of user data. If |
+// the clearing is confirmed, clears the data. |
+// Always returns YES to ensure that the collection view cell is deselected. |
+- (BOOL)alertAndClearData; |
+ |
+// Clears the data stored for |dataTypeMask|. |
+- (void)clearDataForDataTypes:(int)dataTypeMask; |
+ |
+// Returns the accessibility identifier for the cell corresponding to |
+// |itemType|. |
+- (NSString*)getAccessibilityIdentifierFromItemType:(NSInteger)itemType; |
+ |
+// Restarts the counters for data types specified in the mask. |
+- (void)restartCounters:(int)data_mask; |
+ |
+@end |
+ |
+@implementation ClearBrowsingDataCollectionViewController |
+ |
+@synthesize shouldShowNoticeAboutOtherFormsOfBrowsingHistory = |
+ _shouldShowNoticeAboutOtherFormsOfBrowsingHistory; |
+@synthesize shouldPopupDialogAboutOtherFormsOfBrowsingHistory = |
+ _shouldPopupDialogAboutOtherFormsOfBrowsingHistory; |
+ |
+#pragma mark Initialization |
+ |
+- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState { |
+ DCHECK(browserState); |
+ self = [super initWithStyle:CollectionViewControllerStyleAppBar]; |
+ if (self) { |
+ self.accessibilityTraits |= UIAccessibilityTraitButton; |
+ |
+ _browserState = browserState; |
+ if (experimental_flags::IsNewClearBrowsingDataUIEnabled()) { |
+ int prefValue = browserState->GetPrefs()->GetInteger( |
+ browsing_data::prefs::kDeleteTimePeriod); |
+ prefValue = MAX(0, prefValue); |
+ if (prefValue > browsing_data::TIME_PERIOD_LAST) { |
+ prefValue = browsing_data::TIME_PERIOD_LAST; |
+ } |
+ _timePeriod = static_cast<browsing_data::TimePeriod>(prefValue); |
+ } else { |
+ _timePeriod = browsing_data::ALL_TIME; |
+ } |
+ |
+ self.title = l10n_util::GetNSString(IDS_IOS_CLEAR_BROWSING_DATA_TITLE); |
+ self.collectionViewAccessibilityIdentifier = |
+ kClearBrowsingDataCollectionViewId; |
+ |
+ if (experimental_flags::IsNewClearBrowsingDataUIEnabled()) { |
+ base::WeakNSObject<ClearBrowsingDataCollectionViewController> weakSelf( |
+ self); |
+ void (^dataClearedCallback)( |
+ const IOSChromeBrowsingDataRemover::NotificationDetails&) = |
+ ^(const IOSChromeBrowsingDataRemover::NotificationDetails& details) { |
+ base::scoped_nsobject<ClearBrowsingDataCollectionViewController> |
+ strongSelf([weakSelf retain]); |
+ [strongSelf restartCounters:details.removal_mask]; |
+ }; |
+ _callbackSubscription = |
+ IOSChromeBrowsingDataRemover::RegisterOnBrowsingDataRemovedCallback( |
+ base::BindBlock(dataClearedCallback)); |
+ } |
+ |
+ [self loadModel]; |
+ [self restartCounters:IOSChromeBrowsingDataRemover::REMOVE_ALL]; |
+ } |
+ return self; |
+} |
+ |
+- (void)viewDidLoad { |
+ [super viewDidLoad]; |
+ |
+ SigninManager* signinManager = |
+ ios::SigninManagerFactory::GetForBrowserState(_browserState); |
+ if (!signinManager->IsAuthenticated()) { |
+ return; |
+ } |
+ |
+ browser_sync::ProfileSyncService* syncService = |
+ IOSChromeProfileSyncServiceFactory::GetForBrowserState(_browserState); |
+ history::WebHistoryService* historyService = |
+ ios::WebHistoryServiceFactory::GetForBrowserState(_browserState); |
+ |
+ base::WeakNSObject<ClearBrowsingDataCollectionViewController> weakSelf(self); |
+ browsing_data::ShouldShowNoticeAboutOtherFormsOfBrowsingHistory( |
+ syncService, historyService, base::BindBlock(^(bool shouldShowNotice) { |
+ base::scoped_nsobject<ClearBrowsingDataCollectionViewController> |
+ strongSelf([weakSelf retain]); |
+ [strongSelf setShouldShowNoticeAboutOtherFormsOfBrowsingHistory: |
+ shouldShowNotice]; |
+ })); |
+ |
+ browsing_data::ShouldPopupDialogAboutOtherFormsOfBrowsingHistory( |
+ syncService, historyService, GetChannel(), |
+ base::BindBlock(^(bool shouldShowPopup) { |
+ base::scoped_nsobject<ClearBrowsingDataCollectionViewController> |
+ strongSelf([weakSelf retain]); |
+ [strongSelf setShouldPopupDialogAboutOtherFormsOfBrowsingHistory: |
+ shouldShowPopup]; |
+ })); |
+} |
+ |
+#pragma mark CollectionViewController |
+ |
+- (void)loadModel { |
+ [super loadModel]; |
+ CollectionViewModel* model = self.collectionViewModel; |
+ |
+ // Time range section. |
+ if (experimental_flags::IsNewClearBrowsingDataUIEnabled()) { |
+ [model addSectionWithIdentifier:SectionIdentifierTimeRange]; |
+ [model addItem:[self timeRangeItem] |
+ toSectionWithIdentifier:SectionIdentifierTimeRange]; |
+ } |
+ |
+ // Data types section. |
+ [model addSectionWithIdentifier:SectionIdentifierDataTypes]; |
+ int clearBrowsingHistoryMask = |
+ IOSChromeBrowsingDataRemover::REMOVE_HISTORY | |
+ IOSChromeBrowsingDataRemover::REMOVE_GOOGLE_APP_LAUNCHER_DATA; |
+ CollectionViewItem* browsingHistoryItem = |
+ [self clearDataItemWithType:ItemTypeDataTypeBrowsingHistory |
+ titleID:IDS_IOS_CLEAR_BROWSING_HISTORY |
+ mask:clearBrowsingHistoryMask |
+ prefName:browsing_data::prefs::kDeleteBrowsingHistory]; |
+ [model addItem:browsingHistoryItem |
+ toSectionWithIdentifier:SectionIdentifierDataTypes]; |
+ |
+ // This data type doesn't currently have an associated counter, but displays |
+ // an explanatory text instead, when the new UI is enabled. |
+ ClearDataItem* cookiesSiteDataItem = |
+ [self clearDataItemWithType:ItemTypeDataTypeCookiesSiteData |
+ titleID:IDS_IOS_CLEAR_COOKIES |
+ mask:IOSChromeBrowsingDataRemover::REMOVE_SITE_DATA |
+ prefName:browsing_data::prefs::kDeleteCookies]; |
+ if (experimental_flags::IsNewClearBrowsingDataUIEnabled()) { |
+ if (_browserState->GetPrefs()->GetBoolean( |
+ browsing_data::prefs::kDeleteCookies)) { |
+ cookiesSiteDataItem.detailText = |
+ l10n_util::GetNSString(IDS_DEL_COOKIES_COUNTER); |
+ } |
+ } |
+ [model addItem:cookiesSiteDataItem |
+ toSectionWithIdentifier:SectionIdentifierDataTypes]; |
+ |
+ ClearDataItem* cacheItem = |
+ [self clearDataItemWithType:ItemTypeDataTypeCache |
+ titleID:IDS_IOS_CLEAR_CACHE |
+ mask:IOSChromeBrowsingDataRemover::REMOVE_CACHE |
+ prefName:browsing_data::prefs::kDeleteCache]; |
+ [model addItem:cacheItem toSectionWithIdentifier:SectionIdentifierDataTypes]; |
+ |
+ ClearDataItem* savedPasswordsItem = |
+ [self clearDataItemWithType:ItemTypeDataTypeSavedPasswords |
+ titleID:IDS_IOS_CLEAR_SAVED_PASSWORDS |
+ mask:IOSChromeBrowsingDataRemover::REMOVE_PASSWORDS |
+ prefName:browsing_data::prefs::kDeletePasswords]; |
+ [model addItem:savedPasswordsItem |
+ toSectionWithIdentifier:SectionIdentifierDataTypes]; |
+ |
+ ClearDataItem* autofillItem = |
+ [self clearDataItemWithType:ItemTypeDataTypeAutofill |
+ titleID:IDS_IOS_CLEAR_AUTOFILL |
+ mask:IOSChromeBrowsingDataRemover::REMOVE_FORM_DATA |
+ prefName:browsing_data::prefs::kDeleteFormData]; |
+ [model addItem:autofillItem |
+ toSectionWithIdentifier:SectionIdentifierDataTypes]; |
+ |
+ // Clear Browsing Data button. |
+ [model addSectionWithIdentifier:SectionIdentifierClearBrowsingDataButton]; |
+ CollectionViewTextItem* clearButtonItem = [[[CollectionViewTextItem alloc] |
+ initWithType:ItemTypeClearBrowsingDataButton] autorelease]; |
+ clearButtonItem.text = l10n_util::GetNSString(IDS_IOS_CLEAR_BUTTON); |
+ clearButtonItem.accessibilityTraits |= UIAccessibilityTraitButton; |
+ [model addItem:clearButtonItem |
+ toSectionWithIdentifier:SectionIdentifierClearBrowsingDataButton]; |
+ |
+ // Google Account footer. |
+ SigninManager* signinManager = |
+ ios::SigninManagerFactory::GetForBrowserState(_browserState); |
+ if (signinManager->IsAuthenticated()) { |
+ // TODO(crbug.com/650424): Footer items must currently go into a separate |
+ // section, to work around a drawing bug in MDC. |
+ [model addSectionWithIdentifier:SectionIdentifierGoogleAccount]; |
+ [model addItem:[self footerForGoogleAccountSectionItem] |
+ toSectionWithIdentifier:SectionIdentifierGoogleAccount]; |
+ } |
+ |
+ browser_sync::ProfileSyncService* syncService = |
+ IOSChromeProfileSyncServiceFactory::GetForBrowserState(_browserState); |
+ if (syncService && syncService->IsSyncActive()) { |
+ // TODO(crbug.com/650424): Footer items must currently go into a separate |
+ // section, to work around a drawing bug in MDC. |
+ [model addSectionWithIdentifier:SectionIdentifierClearSyncAndSavedSiteData]; |
+ [model addItem:[self footerClearSyncAndSavedSiteDataItem] |
+ toSectionWithIdentifier:SectionIdentifierClearSyncAndSavedSiteData]; |
+ } else { |
+ // TODO(crbug.com/650424): Footer items must currently go into a separate |
+ // section, to work around a drawing bug in MDC. |
+ [model addSectionWithIdentifier:SectionIdentifierSavedSiteData]; |
+ [model addItem:[self footerSavedSiteDataItem] |
+ toSectionWithIdentifier:SectionIdentifierSavedSiteData]; |
+ } |
+} |
+ |
+#pragma mark Items |
+ |
+- (ClearDataItem*)clearDataItemWithType:(ItemType)itemType |
+ titleID:(int)titleMessageID |
+ mask:(int)mask |
+ prefName:(const char*)prefName { |
+ PrefService* prefs = _browserState->GetPrefs(); |
+ ClearDataItem* clearDataItem = |
+ [[[ClearDataItem alloc] initWithType:itemType] autorelease]; |
+ clearDataItem.text = l10n_util::GetNSString(titleMessageID); |
+ if (prefs->GetBoolean(prefName)) { |
+ clearDataItem.accessoryType = MDCCollectionViewCellAccessoryCheckmark; |
+ } |
+ clearDataItem.dataTypeMask = mask; |
+ clearDataItem.prefName = prefName; |
+ clearDataItem.accessibilityIdentifier = |
+ [self getAccessibilityIdentifierFromItemType:itemType]; |
+ |
+ base::WeakNSObject<ClearBrowsingDataCollectionViewController> weakSelf(self); |
+ void (^updateUICallback)(const browsing_data::BrowsingDataCounter::Result&) = |
+ ^(const browsing_data::BrowsingDataCounter::Result& result) { |
+ base::scoped_nsobject<ClearBrowsingDataCollectionViewController> |
+ strongSelf([weakSelf retain]); |
+ NSString* counterText = [strongSelf getCounterTextFromResult:result]; |
+ [strongSelf updateCounter:itemType detailText:counterText]; |
+ }; |
+ |
+ [clearDataItem setCounter:BrowsingDataCounterWrapper::CreateCounterWrapper( |
+ prefName, _browserState, prefs, |
+ base::BindBlock(updateUICallback))]; |
+ return clearDataItem; |
+} |
+ |
+- (void)updateCounter:(NSInteger)itemType detailText:(NSString*)detailText { |
+ NSIndexPath* indexPath = [self.collectionViewModel |
+ indexPathForItemType:itemType |
+ sectionIdentifier:SectionIdentifierDataTypes]; |
+ |
+ CollectionViewModel* model = self.collectionViewModel; |
+ if (!model) { |
+ return; |
+ } |
+ ClearDataItem* clearDataItem = base::mac::ObjCCastStrict<ClearDataItem>( |
+ [model itemAtIndexPath:indexPath]); |
+ // Because there is no counter for cookies, an explanatory text is displayed. |
+ if (![clearDataItem hasCounter] && |
+ itemType != ItemTypeDataTypeCookiesSiteData) { |
+ return; |
+ } |
+ clearDataItem.detailText = detailText; |
+ [self reconfigureCellsForItems:@[ clearDataItem ] |
+ inSectionWithIdentifier:SectionIdentifierDataTypes]; |
+ [self.collectionView.collectionViewLayout invalidateLayout]; |
+} |
+ |
+- (CollectionViewItem*)footerForGoogleAccountSectionItem { |
+ return _shouldShowNoticeAboutOtherFormsOfBrowsingHistory |
+ ? [self footerGoogleAccountAndMyActivityItem] |
+ : [self footerGoogleAccountItem]; |
+} |
+ |
+- (CollectionViewItem*)footerGoogleAccountItem { |
+ CollectionViewFooterItem* footerItem = [[[CollectionViewFooterItem alloc] |
+ initWithType:ItemTypeFooterGoogleAccount] autorelease]; |
+ footerItem.text = |
+ l10n_util::GetNSString(IDS_IOS_CLEAR_BROWSING_DATA_FOOTER_ACCOUNT); |
+ UIImage* image = ios::GetChromeBrowserProvider() |
+ ->GetBrandedImageProvider() |
+ ->GetClearBrowsingDataAccountActivityImage(); |
+ footerItem.image = image; |
+ return footerItem; |
+} |
+ |
+- (CollectionViewItem*)footerGoogleAccountAndMyActivityItem { |
+ UIImage* image = ios::GetChromeBrowserProvider() |
+ ->GetBrandedImageProvider() |
+ ->GetClearBrowsingDataAccountActivityImage(); |
+ return [self |
+ footerItemWithType:ItemTypeFooterGoogleAccountAndMyActivity |
+ titleID:IDS_IOS_CLEAR_BROWSING_DATA_FOOTER_ACCOUNT_AND_HISTORY |
+ URL:kClearBrowsingDataMyActivityUrlInFooterURL |
+ image:image]; |
+} |
+ |
+- (CollectionViewItem*)footerSavedSiteDataItem { |
+ UIImage* image = ios::GetChromeBrowserProvider() |
+ ->GetBrandedImageProvider() |
+ ->GetClearBrowsingDataSiteDataImage(); |
+ return [self |
+ footerItemWithType:ItemTypeFooterSavedSiteData |
+ titleID:IDS_IOS_CLEAR_BROWSING_DATA_FOOTER_SAVED_SITE_DATA |
+ URL:kClearBrowsingDataLearnMoreURL |
+ image:image]; |
+} |
+ |
+- (CollectionViewItem*)footerClearSyncAndSavedSiteDataItem { |
+ UIImage* infoIcon = [ChromeIcon infoIcon]; |
+ UIImage* image = TintImage(infoIcon, [[MDCPalette greyPalette] tint500]); |
+ return [self |
+ footerItemWithType:ItemTypeFooterClearSyncAndSavedSiteData |
+ titleID: |
+ IDS_IOS_CLEAR_BROWSING_DATA_FOOTER_CLEAR_SYNC_AND_SAVED_SITE_DATA |
+ URL:kClearBrowsingDataLearnMoreURL |
+ image:image]; |
+} |
+ |
+- (CollectionViewItem*)footerItemWithType:(ItemType)itemType |
+ titleID:(int)titleMessageID |
+ URL:(const char[])URL |
+ image:(UIImage*)image { |
+ CollectionViewFooterItem* footerItem = |
+ [[[CollectionViewFooterItem alloc] initWithType:itemType] autorelease]; |
+ footerItem.text = l10n_util::GetNSString(titleMessageID); |
+ footerItem.linkURL = google_util::AppendGoogleLocaleParam( |
+ GURL(URL), GetApplicationContext()->GetApplicationLocale()); |
+ footerItem.linkDelegate = self; |
+ footerItem.image = image; |
+ return footerItem; |
+} |
+ |
+- (CollectionViewItem*)timeRangeItem { |
+ CollectionViewDetailItem* timeRangeItem = [[[CollectionViewDetailItem alloc] |
+ initWithType:ItemTypeTimeRange] autorelease]; |
+ timeRangeItem.text = l10n_util::GetNSString( |
+ IDS_IOS_CLEAR_BROWSING_DATA_TIME_RANGE_SELECTOR_TITLE); |
+ NSString* detailText = [TimeRangeSelectorCollectionViewController |
+ timePeriodLabelForPrefs:_browserState->GetPrefs()]; |
+ DCHECK(detailText); |
+ timeRangeItem.detailText = detailText; |
+ timeRangeItem.accessoryType = |
+ MDCCollectionViewCellAccessoryDisclosureIndicator; |
+ timeRangeItem.accessibilityTraits |= UIAccessibilityTraitButton; |
+ return timeRangeItem; |
+} |
+ |
+#pragma mark UICollectionViewDataSource |
+ |
+- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView |
+ cellForItemAtIndexPath:(NSIndexPath*)indexPath { |
+ UICollectionViewCell* cell = |
+ [super collectionView:collectionView cellForItemAtIndexPath:indexPath]; |
+ |
+ NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath]; |
+ if (type == ItemTypeClearBrowsingDataButton) { |
+ MDCCollectionViewTextCell* textCell = |
+ base::mac::ObjCCastStrict<MDCCollectionViewTextCell>(cell); |
+ textCell.textLabel.textColor = [[MDCPalette cr_redPalette] tint500]; |
+ } |
+ |
+ return cell; |
+} |
+ |
+#pragma mark UICollectionViewDelegate |
+ |
+- (void)collectionView:(UICollectionView*)collectionView |
+ didSelectItemAtIndexPath:(NSIndexPath*)indexPath { |
+ [super collectionView:collectionView didSelectItemAtIndexPath:indexPath]; |
+ NSInteger itemType = |
+ [self.collectionViewModel itemTypeForIndexPath:indexPath]; |
+ |
+ switch (itemType) { |
+ case ItemTypeTimeRange: { |
+ base::scoped_nsobject<UIViewController> controller( |
+ [[TimeRangeSelectorCollectionViewController alloc] |
+ initWithPrefs:_browserState->GetPrefs() |
+ delegate:self]); |
+ [self.navigationController pushViewController:controller animated:YES]; |
+ break; |
+ } |
+ case ItemTypeDataTypeBrowsingHistory: |
+ case ItemTypeDataTypeCookiesSiteData: |
+ case ItemTypeDataTypeCache: |
+ case ItemTypeDataTypeSavedPasswords: |
+ case ItemTypeDataTypeAutofill: { |
+ // Toggle the checkmark. |
+ // TODO(crbug.com/631486): Custom checkmark animation to be implemented. |
+ ClearDataItem* clearDataItem = base::mac::ObjCCastStrict<ClearDataItem>( |
+ [self.collectionViewModel itemAtIndexPath:indexPath]); |
+ if (clearDataItem.accessoryType == MDCCollectionViewCellAccessoryNone) { |
+ clearDataItem.accessoryType = MDCCollectionViewCellAccessoryCheckmark; |
+ if (experimental_flags::IsNewClearBrowsingDataUIEnabled() && |
+ itemType == ItemTypeDataTypeCookiesSiteData) { |
+ [self updateCounter:itemType |
+ detailText:l10n_util::GetNSString(IDS_DEL_COOKIES_COUNTER)]; |
+ } |
+ _browserState->GetPrefs()->SetBoolean(clearDataItem.prefName, true); |
+ } else { |
+ clearDataItem.accessoryType = MDCCollectionViewCellAccessoryNone; |
+ _browserState->GetPrefs()->SetBoolean(clearDataItem.prefName, false); |
+ if (experimental_flags::IsNewClearBrowsingDataUIEnabled()) { |
+ // Hide counter text. |
+ [self updateCounter:itemType detailText:nil]; |
+ } |
+ } |
+ [self reconfigureCellsForItems:@[ clearDataItem ] |
+ inSectionWithIdentifier:SectionIdentifierDataTypes]; |
+ break; |
+ } |
+ case ItemTypeClearBrowsingDataButton: |
+ [self alertAndClearData]; |
+ break; |
+ default: |
+ break; |
+ } |
+} |
+ |
+#pragma mark Properties |
+ |
+- (void)setShouldShowNoticeAboutOtherFormsOfBrowsingHistory:(BOOL)showNotice { |
+ _shouldShowNoticeAboutOtherFormsOfBrowsingHistory = showNotice; |
+ UMA_HISTOGRAM_BOOLEAN( |
+ "History.ClearBrowsingData.HistoryNoticeShownInFooterWhenUpdated", |
+ _shouldShowNoticeAboutOtherFormsOfBrowsingHistory); |
+ |
+ // Update the account footer if the model was already loaded. |
+ CollectionViewModel* model = self.collectionViewModel; |
+ if (!model) { |
+ return; |
+ } |
+ SigninManager* signinManager = |
+ ios::SigninManagerFactory::GetForBrowserState(_browserState); |
+ if (!signinManager->IsAuthenticated()) { |
+ return; |
+ } |
+ |
+ CollectionViewItem* footerItem = [self footerForGoogleAccountSectionItem]; |
+ // TODO(crbug.com/650424): Simplify with setFooter:inSection: when the bug in |
+ // MDC is fixed. |
+ // Remove the footer if there is one in that section. |
+ if ([model hasSectionForSectionIdentifier:SectionIdentifierGoogleAccount]) { |
+ if ([model hasItemForItemType:ItemTypeFooterGoogleAccount |
+ sectionIdentifier:SectionIdentifierGoogleAccount]) { |
+ [model removeItemWithType:ItemTypeFooterGoogleAccount |
+ fromSectionWithIdentifier:SectionIdentifierGoogleAccount]; |
+ } else { |
+ [model removeItemWithType:ItemTypeFooterGoogleAccountAndMyActivity |
+ fromSectionWithIdentifier:SectionIdentifierGoogleAccount]; |
+ } |
+ } |
+ // Add the new footer. |
+ [model addItem:footerItem |
+ toSectionWithIdentifier:SectionIdentifierGoogleAccount]; |
+ [self reconfigureCellsForItems:@[ footerItem ] |
+ inSectionWithIdentifier:SectionIdentifierGoogleAccount]; |
+ |
+ // Relayout the cells to adapt to the new contents height. |
+ [self.collectionView.collectionViewLayout invalidateLayout]; |
+} |
+ |
+#pragma mark Clear browsing data |
+ |
+- (BOOL)alertAndClearData { |
+ int dataTypeMaskToRemove = 0; |
+ NSArray* dataTypeItems = [self.collectionViewModel |
+ itemsInSectionWithIdentifier:SectionIdentifierDataTypes]; |
+ for (ClearDataItem* dataTypeItem in dataTypeItems) { |
+ DCHECK([dataTypeItem isKindOfClass:[ClearDataItem class]]); |
+ if (dataTypeItem.accessoryType == MDCCollectionViewCellAccessoryCheckmark) { |
+ dataTypeMaskToRemove |= dataTypeItem.dataTypeMask; |
+ } |
+ } |
+ if (dataTypeMaskToRemove == 0) { |
+ // Nothing to clear (no data types selected). |
+ return YES; |
+ } |
+ base::WeakNSObject<ClearBrowsingDataCollectionViewController> weakSelf(self); |
+ UIAlertController* alertController = [UIAlertController |
+ alertControllerWithTitle:nil |
+ message:nil |
+ preferredStyle:UIAlertControllerStyleActionSheet]; |
+ |
+ UIAlertAction* clearDataAction = [UIAlertAction |
+ actionWithTitle:l10n_util::GetNSString(IDS_IOS_CLEAR_BUTTON) |
+ style:UIAlertActionStyleDestructive |
+ handler:^(UIAlertAction* action) { |
+ [weakSelf clearDataForDataTypes:dataTypeMaskToRemove]; |
+ }]; |
+ UIAlertAction* cancelAction = |
+ [UIAlertAction actionWithTitle:l10n_util::GetNSString(IDS_CANCEL) |
+ style:UIAlertActionStyleCancel |
+ handler:nil]; |
+ [alertController addAction:clearDataAction]; |
+ [alertController addAction:cancelAction]; |
+ [self presentViewController:alertController animated:YES completion:nil]; |
+ return YES; |
+} |
+ |
+- (void)clearDataForDataTypes:(int)dataTypeMask { |
+ DCHECK(dataTypeMask); |
+ base::scoped_nsobject<ClearBrowsingDataCommand> command( |
+ [[ClearBrowsingDataCommand alloc] initWithBrowserState:_browserState |
+ mask:dataTypeMask |
+ timePeriod:_timePeriod]); |
+ [self chromeExecuteCommand:command]; |
+ |
+ if (!!(dataTypeMask && IOSChromeBrowsingDataRemover::REMOVE_HISTORY)) { |
+ [self showBrowsingHistoryRemovedDialog]; |
+ } |
+} |
+ |
+- (void)showBrowsingHistoryRemovedDialog { |
+ PrefService* prefs = _browserState->GetPrefs(); |
+ int noticeShownTimes = prefs->GetInteger( |
+ browsing_data::prefs::kClearBrowsingDataHistoryNoticeShownTimes); |
+ |
+ // When the deletion is complete, we might show an additional dialog with |
+ // a notice about other forms of browsing history. This is the case if |
+ const bool showDialog = |
+ // 1. The dialog is relevant for the user. |
+ _shouldPopupDialogAboutOtherFormsOfBrowsingHistory && |
+ // 2. The notice has been shown less than |kMaxTimesHistoryNoticeShown|. |
+ noticeShownTimes < kMaxTimesHistoryNoticeShown; |
+ UMA_HISTOGRAM_BOOLEAN( |
+ "History.ClearBrowsingData.ShownHistoryNoticeAfterClearing", showDialog); |
+ if (!showDialog) { |
+ return; |
+ } |
+ |
+ // Increment the preference. |
+ prefs->SetInteger( |
+ browsing_data::prefs::kClearBrowsingDataHistoryNoticeShownTimes, |
+ noticeShownTimes + 1); |
+ |
+ NSString* title = |
+ l10n_util::GetNSString(IDS_IOS_CLEAR_BROWSING_DATA_HISTORY_NOTICE_TITLE); |
+ NSString* message = l10n_util::GetNSString( |
+ IDS_IOS_CLEAR_BROWSING_DATA_HISTORY_NOTICE_DESCRIPTION); |
+ |
+ UIAlertController* alertController = |
+ [UIAlertController alertControllerWithTitle:title |
+ message:message |
+ preferredStyle:UIAlertControllerStyleAlert]; |
+ |
+ base::WeakNSObject<ClearBrowsingDataCollectionViewController> weakSelf(self); |
+ UIAlertAction* openMyActivityAction = [UIAlertAction |
+ actionWithTitle: |
+ l10n_util::GetNSString( |
+ IDS_IOS_CLEAR_BROWSING_DATA_HISTORY_NOTICE_OPEN_HISTORY_BUTTON) |
+ style:UIAlertActionStyleDefault |
+ handler:^(UIAlertAction* action) { |
+ [weakSelf openMyActivityLink]; |
+ }]; |
+ |
+ UIAlertAction* okAction = [UIAlertAction |
+ actionWithTitle:l10n_util::GetNSString( |
+ IDS_IOS_CLEAR_BROWSING_DATA_HISTORY_NOTICE_OK_BUTTON) |
+ style:UIAlertActionStyleCancel |
+ handler:nil]; |
+ [alertController addAction:openMyActivityAction]; |
+ [alertController addAction:okAction]; |
+ [self presentViewController:alertController animated:YES completion:nil]; |
+} |
+ |
+- (void)openMyActivityLink { |
+ base::scoped_nsobject<OpenUrlCommand> openMyActivityCommand( |
+ [[OpenUrlCommand alloc] initWithURLFromChrome:GURL(kGoogleMyAccountURL)]); |
+ openMyActivityCommand.get().tag = IDC_CLOSE_SETTINGS_AND_OPEN_URL; |
+ [self chromeExecuteCommand:openMyActivityCommand]; |
+} |
+ |
+- (NSString*)getAccessibilityIdentifierFromItemType:(NSInteger)itemType { |
+ switch (itemType) { |
+ case ItemTypeDataTypeBrowsingHistory: |
+ return kClearBrowsingHistoryCellId; |
+ case ItemTypeDataTypeCookiesSiteData: |
+ return kClearCookiesCellId; |
+ case ItemTypeDataTypeCache: |
+ return kClearCacheCellId; |
+ case ItemTypeDataTypeSavedPasswords: |
+ return kClearSavedPasswordsCellId; |
+ case ItemTypeDataTypeAutofill: |
+ return kClearAutofillCellId; |
+ default: { |
+ NOTREACHED(); |
+ return nil; |
+ } |
+ } |
+} |
+ |
+- (void)restartCounters:(int)data_mask { |
+ CollectionViewModel* model = self.collectionViewModel; |
+ if (!model) |
+ return; |
+ |
+ if (data_mask & |
+ (IOSChromeBrowsingDataRemover::REMOVE_HISTORY | |
+ IOSChromeBrowsingDataRemover::REMOVE_GOOGLE_APP_LAUNCHER_DATA)) { |
+ NSIndexPath* indexPath = [self.collectionViewModel |
+ indexPathForItemType:ItemTypeDataTypeBrowsingHistory |
+ sectionIdentifier:SectionIdentifierDataTypes]; |
+ ClearDataItem* historyItem = base::mac::ObjCCastStrict<ClearDataItem>( |
+ [model itemAtIndexPath:indexPath]); |
+ [historyItem restartCounter]; |
+ } |
+ |
+ if (data_mask & IOSChromeBrowsingDataRemover::REMOVE_PASSWORDS) { |
+ NSIndexPath* indexPath = [self.collectionViewModel |
+ indexPathForItemType:ItemTypeDataTypeSavedPasswords |
+ sectionIdentifier:SectionIdentifierDataTypes]; |
+ ClearDataItem* passwordsItem = base::mac::ObjCCastStrict<ClearDataItem>( |
+ [model itemAtIndexPath:indexPath]); |
+ [passwordsItem restartCounter]; |
+ } |
+ |
+ if (data_mask & IOSChromeBrowsingDataRemover::REMOVE_FORM_DATA) { |
+ NSIndexPath* indexPath = [self.collectionViewModel |
+ indexPathForItemType:ItemTypeDataTypeAutofill |
+ sectionIdentifier:SectionIdentifierDataTypes]; |
+ ClearDataItem* autofillItem = base::mac::ObjCCastStrict<ClearDataItem>( |
+ [model itemAtIndexPath:indexPath]); |
+ [autofillItem restartCounter]; |
+ } |
+} |
+ |
+- (NSString*)getCounterTextFromResult: |
+ (const browsing_data::BrowsingDataCounter::Result&)result { |
+ std::string prefName = result.source()->GetPrefName(); |
+ if (!result.Finished()) { |
+ // The counter is still counting. |
+ return l10n_util::GetNSString(IDS_CLEAR_BROWSING_DATA_CALCULATING); |
+ } |
+ |
+ if (prefName == browsing_data::prefs::kDeleteCache) { |
+ browsing_data::BrowsingDataCounter::ResultInt cacheSizeBytes = |
+ static_cast<const browsing_data::BrowsingDataCounter::FinishedResult*>( |
+ &result) |
+ ->Value(); |
+ |
+ // Three cases: Nonzero result for the entire cache, nonzero result for |
+ // a subset of cache (i.e. a finite time interval), and almost zero (less |
+ // than 1 MB). There is no exact information that the cache is empty so that |
+ // falls into the almost zero case, which is displayed as less than 1 MB. |
+ // Because of this, the lowest unit that can be used is MB. |
+ static const int kBytesInAMegabyte = 1 << 20; |
+ if (cacheSizeBytes >= kBytesInAMegabyte) { |
+ base::scoped_nsobject<NSByteCountFormatter> formatter( |
+ [[NSByteCountFormatter alloc] init]); |
+ formatter.get().allowedUnits = NSByteCountFormatterUseAll & |
+ (~NSByteCountFormatterUseBytes) & |
+ (~NSByteCountFormatterUseKB); |
+ formatter.get().countStyle = NSByteCountFormatterCountStyleMemory; |
+ NSString* formattedSize = [formatter stringFromByteCount:cacheSizeBytes]; |
+ return (_timePeriod == browsing_data::ALL_TIME) |
+ ? formattedSize |
+ : l10n_util::GetNSStringF( |
+ IDS_DEL_CACHE_COUNTER_UPPER_ESTIMATE, |
+ base::SysNSStringToUTF16(formattedSize)); |
+ } |
+ return l10n_util::GetNSString(IDS_DEL_CACHE_COUNTER_ALMOST_EMPTY); |
+ } |
+ return base::SysUTF16ToNSString( |
+ browsing_data::GetCounterTextFromResult(&result)); |
+} |
+ |
+#pragma mark MDCCollectionViewStylingDelegate |
+ |
+- (BOOL)collectionView:(UICollectionView*)collectionView |
+ hidesInkViewAtIndexPath:(NSIndexPath*)indexPath { |
+ NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath]; |
+ switch (type) { |
+ case ItemTypeFooterSavedSiteData: |
+ case ItemTypeFooterGoogleAccount: |
+ case ItemTypeFooterGoogleAccountAndMyActivity: |
+ case ItemTypeFooterClearSyncAndSavedSiteData: |
+ return YES; |
+ default: |
+ return NO; |
+ } |
+} |
+ |
+- (MDCCollectionViewCellStyle)collectionView:(UICollectionView*)collectionView |
+ cellStyleForSection:(NSInteger)section { |
+ NSInteger sectionIdentifier = |
+ [self.collectionViewModel sectionIdentifierForSection:section]; |
+ switch (sectionIdentifier) { |
+ case SectionIdentifierGoogleAccount: |
+ case SectionIdentifierClearSyncAndSavedSiteData: |
+ case SectionIdentifierSavedSiteData: |
+ // Display the footer in the default style with no "card" UI and no |
+ // section padding. |
+ return MDCCollectionViewCellStyleDefault; |
+ default: |
+ return self.styler.cellStyle; |
+ } |
+} |
+ |
+- (BOOL)collectionView:(UICollectionView*)collectionView |
+ shouldHideItemBackgroundAtIndexPath:(NSIndexPath*)indexPath { |
+ NSInteger sectionIdentifier = |
+ [self.collectionViewModel sectionIdentifierForSection:indexPath.section]; |
+ switch (sectionIdentifier) { |
+ case SectionIdentifierGoogleAccount: |
+ case SectionIdentifierClearSyncAndSavedSiteData: |
+ case SectionIdentifierSavedSiteData: |
+ // Display the Learn More footer without any background image or |
+ // shadowing. |
+ return YES; |
+ default: |
+ return NO; |
+ } |
+} |
+ |
+- (CGFloat)collectionView:(UICollectionView*)collectionView |
+ cellHeightAtIndexPath:(NSIndexPath*)indexPath { |
+ NSInteger sectionIdentifier = |
+ [self.collectionViewModel sectionIdentifierForSection:indexPath.section]; |
+ switch (sectionIdentifier) { |
+ case SectionIdentifierGoogleAccount: |
+ case SectionIdentifierClearSyncAndSavedSiteData: |
+ case SectionIdentifierSavedSiteData: { |
+ CollectionViewItem* item = |
+ [self.collectionViewModel itemAtIndexPath:indexPath]; |
+ return [MDCCollectionViewCell |
+ cr_preferredHeightForWidth:CGRectGetWidth(collectionView.bounds) |
+ forItem:item]; |
+ } |
+ case SectionIdentifierDataTypes: { |
+ ClearDataItem* clearDataItem = base::mac::ObjCCastStrict<ClearDataItem>( |
+ [self.collectionViewModel itemAtIndexPath:indexPath]); |
+ return (clearDataItem.detailText.length > 0) |
+ ? MDCCellDefaultTwoLineHeight |
+ : MDCCellDefaultOneLineHeight; |
+ } |
+ default: |
+ return MDCCellDefaultOneLineHeight; |
+ } |
+} |
+ |
+#pragma mark TimeRangeSelectorCollectionViewControllerDelegate |
+ |
+- (void)timeRangeSelectorViewController: |
+ (TimeRangeSelectorCollectionViewController*)collectionViewController |
+ didSelectTimePeriod:(browsing_data::TimePeriod)timePeriod { |
+ _timePeriod = timePeriod; |
+} |
+ |
+@end |