| 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
|
|
|