[go: nahoru, domu]

blob: d8ce8766eb34ad2cb0721dcfd6870f03c99e7732 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// 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/google_services/google_services_settings_mediator.h"
#import "base/apple/foundation_util.h"
#import "base/auto_reset.h"
#import "base/notreached.h"
#import "components/metrics/metrics_pref_names.h"
#import "components/password_manager/core/common/password_manager_features.h"
#import "components/password_manager/core/common/password_manager_pref_names.h"
#import "components/prefs/pref_service.h"
#import "components/signin/public/base/signin_pref_names.h"
#import "components/supervised_user/core/common/supervised_user_utils.h"
#import "components/sync/service/sync_service.h"
#import "components/unified_consent/pref_names.h"
#import "ios/chrome/browser/policy/policy_util.h"
#import "ios/chrome/browser/settings/sync/utils/sync_util.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_info_button_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_switch_item.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/browser/signin/authentication_service.h"
#import "ios/chrome/browser/signin/authentication_service_factory.h"
#import "ios/chrome/browser/signin/system_identity.h"
#import "ios/chrome/browser/ui/authentication/authentication_constants.h"
#import "ios/chrome/browser/ui/authentication/cells/table_view_account_item.h"
#import "ios/chrome/browser/ui/settings/cells/account_sign_in_item.h"
#import "ios/chrome/browser/ui/settings/cells/settings_image_detail_text_item.h"
#import "ios/chrome/browser/ui/settings/cells/sync_switch_item.h"
#import "ios/chrome/browser/ui/settings/google_services/google_services_settings_command_handler.h"
#import "ios/chrome/browser/ui/settings/google_services/google_services_settings_constants.h"
#import "ios/chrome/browser/ui/settings/settings_table_view_controller_constants.h"
#import "ios/chrome/browser/ui/settings/utils/observable_boolean.h"
#import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
#import "ios/chrome/grit/ios_chromium_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/public/provider/chrome/browser/signin/signin_resources_api.h"
#import "ui/base/l10n/l10n_util.h"
using l10n_util::GetNSString;
typedef NSArray<TableViewItem*>* ItemArray;
namespace {
NSString* const kBetterSearchAndBrowsingItemAccessibilityID =
@"betterSearchAndBrowsingItem_switch";
NSString* const kTrackPricesOnTabsItemAccessibilityID =
@"trackPricesOnTabsItem_switch";
// List of sections.
typedef NS_ENUM(NSInteger, SectionIdentifier) {
NonPersonalizedSectionIdentifier = kSectionIdentifierEnumZero,
};
// List of items. For implementation details in
// GoogleServicesSettingsViewController, two SyncSwitchItem items should not
// share the same type. The cell UISwitch tag is used to save the item type, and
// when the user taps on the switch, this tag is used to retrieve the item
// based on the type.
typedef NS_ENUM(NSInteger, ItemType) {
AllowChromeSigninItemType = kItemTypeEnumZero,
ImproveChromeItemType,
ImproveChromeManagedItemType,
BetterSearchAndBrowsingItemType,
BetterSearchAndBrowsingManagedItemType,
ImproveSearchSuggestionsItemType,
ImproveSearchSuggestionsManagedItemType,
TrackPricesOnTabsItemType,
};
// TODO(crbug.com/1244632): Use the Authentication Service sign-in status API
// instead of this when available.
// Returns true when sign-in can be enabled/disabled by the user from the
// google service settings.
bool IsSigninControllableByUser(const PrefService* prefService) {
if (supervised_user::IsSubjectToParentalControls(prefService)) {
return false;
}
BrowserSigninMode policy_mode = static_cast<BrowserSigninMode>(
GetApplicationContext()->GetLocalState()->GetInteger(
prefs::kBrowserSigninPolicy));
switch (policy_mode) {
case BrowserSigninMode::kEnabled:
return true;
case BrowserSigninMode::kDisabled:
case BrowserSigninMode::kForced:
return false;
}
NOTREACHED();
return true;
}
bool GetStatusForSigninPolicy() {
BrowserSigninMode policy_mode = static_cast<BrowserSigninMode>(
GetApplicationContext()->GetLocalState()->GetInteger(
prefs::kBrowserSigninPolicy));
switch (policy_mode) {
case BrowserSigninMode::kEnabled:
case BrowserSigninMode::kForced:
return true;
case BrowserSigninMode::kDisabled:
return false;
}
NOTREACHED();
return false;
}
} // namespace
@interface GoogleServicesSettingsMediator () <BooleanObserver>
// Returns YES if the user is authenticated.
@property(nonatomic, assign, readonly) BOOL hasPrimaryIdentity;
// ** Non personalized section.
// Preference value for the "Allow Chrome Sign-in" feature.
@property(nonatomic, strong, readonly)
PrefBackedBoolean* allowChromeSigninPreference;
// Preference value for the "Help improve Chromium's features" for Wifi-Only.
// TODO(crbug.com/872101): Needs to create the UI to change from Wifi-Only to
// always
@property(nonatomic, strong, readonly)
PrefBackedBoolean* sendDataUsageWifiOnlyPreference;
// Preference value for the "Make searches and browsing better" feature.
@property(nonatomic, strong, readonly)
PrefBackedBoolean* anonymizedDataCollectionPreference;
// Preference value for the "Improve search suggestions" feature.
@property(nonatomic, strong, readonly)
PrefBackedBoolean* improveSearchSuggestionsPreference;
// Preference value for the "Help improve Chromium's features" feature.
@property(nonatomic, strong, readonly)
PrefBackedBoolean* sendDataUsagePreference;
// All the items for the non-personalized section.
@property(nonatomic, strong, readonly) ItemArray nonPersonalizedItems;
// User pref service used to check if a specific pref is managed by enterprise
// policies.
@property(nonatomic, assign, readonly) PrefService* userPrefService;
// Local pref service used to check if a specific pref is managed by enterprise
// policies.
@property(nonatomic, assign, readonly) PrefService* localPrefService;
// Preference value for displaying price drop annotations on Tabs for shopping
// URLs in the Tab Switching UI as price drops are identified.
@property(nonatomic, strong, readonly)
PrefBackedBoolean* trackPricesOnTabsPreference;
@end
@implementation GoogleServicesSettingsMediator
@synthesize nonPersonalizedItems = _nonPersonalizedItems;
- (instancetype)initWithUserPrefService:(PrefService*)userPrefService
localPrefService:(PrefService*)localPrefService {
self = [super init];
if (self) {
DCHECK(userPrefService);
DCHECK(localPrefService);
_userPrefService = userPrefService;
_localPrefService = localPrefService;
_allowChromeSigninPreference =
[[PrefBackedBoolean alloc] initWithPrefService:userPrefService
prefName:prefs::kSigninAllowed];
_allowChromeSigninPreference.observer = self;
_sendDataUsagePreference = [[PrefBackedBoolean alloc]
initWithPrefService:localPrefService
prefName:metrics::prefs::kMetricsReportingEnabled];
_sendDataUsagePreference.observer = self;
_anonymizedDataCollectionPreference = [[PrefBackedBoolean alloc]
initWithPrefService:userPrefService
prefName:unified_consent::prefs::
kUrlKeyedAnonymizedDataCollectionEnabled];
_anonymizedDataCollectionPreference.observer = self;
_improveSearchSuggestionsPreference = [[PrefBackedBoolean alloc]
initWithPrefService:userPrefService
prefName:prefs::kSearchSuggestEnabled];
_improveSearchSuggestionsPreference.observer = self;
_trackPricesOnTabsPreference = [[PrefBackedBoolean alloc]
initWithPrefService:userPrefService
prefName:prefs::kTrackPricesOnTabsEnabled];
_trackPricesOnTabsPreference.observer = self;
}
return self;
}
- (TableViewItem*)allowChromeSigninItem {
if (IsSigninControllableByUser(self.userPrefService)) {
return
[self switchItemWithItemType:AllowChromeSigninItemType
textStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_ALLOW_SIGNIN_TEXT
detailStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_ALLOW_SIGNIN_DETAIL];
}
// Disables "Allow Chrome Sign-in" switch with a disclosure that the
// setting has been disabled by the organization.
return [self
tableViewInfoButtonItemType:AllowChromeSigninItemType
textStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_ALLOW_SIGNIN_TEXT
detailStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_ALLOW_SIGNIN_DETAIL
status:GetStatusForSigninPolicy()];
}
#pragma mark - Load non personalized section
// Loads NonPersonalizedSectionIdentifier section.
- (void)loadNonPersonalizedSection {
TableViewModel* model = self.consumer.tableViewModel;
[model addSectionWithIdentifier:NonPersonalizedSectionIdentifier];
for (TableViewItem* item in self.nonPersonalizedItems) {
[model addItem:item
toSectionWithIdentifier:NonPersonalizedSectionIdentifier];
}
[self updateNonPersonalizedSectionWithNotification:NO];
}
// Updates the non-personalized section according to the user consent. If
// `notifyConsumer` is YES, the consumer is notified about model changes.
- (void)updateNonPersonalizedSectionWithNotification:(BOOL)notifyConsumer {
for (TableViewItem* item in self.nonPersonalizedItems) {
ItemType type = static_cast<ItemType>(item.type);
switch (type) {
case AllowChromeSigninItemType: {
SyncSwitchItem* signinDisabledItem =
base::apple::ObjCCast<SyncSwitchItem>(item);
if (IsSigninControllableByUser(self.userPrefService)) {
signinDisabledItem.on = self.allowChromeSigninPreference.value;
} else {
signinDisabledItem.on = NO;
signinDisabledItem.enabled = NO;
}
break;
}
case ImproveChromeItemType:
base::apple::ObjCCast<SyncSwitchItem>(item).on =
self.sendDataUsagePreference.value;
break;
case ImproveChromeManagedItemType:
base::apple::ObjCCast<TableViewInfoButtonItem>(item).statusText =
self.sendDataUsagePreference.value
? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
: l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
break;
case BetterSearchAndBrowsingItemType:
base::apple::ObjCCast<SyncSwitchItem>(item).on =
self.anonymizedDataCollectionPreference.value;
break;
case BetterSearchAndBrowsingManagedItemType:
base::apple::ObjCCast<TableViewInfoButtonItem>(item).statusText =
self.anonymizedDataCollectionPreference.value
? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
: l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
break;
case ImproveSearchSuggestionsItemType:
base::apple::ObjCCast<SyncSwitchItem>(item).on =
self.improveSearchSuggestionsPreference.value;
break;
case ImproveSearchSuggestionsManagedItemType:
base::apple::ObjCCast<TableViewInfoButtonItem>(item).statusText =
self.improveSearchSuggestionsPreference.value
? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
: l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
break;
case TrackPricesOnTabsItemType:
base::apple::ObjCCast<SyncSwitchItem>(item).on =
self.trackPricesOnTabsPreference.value;
break;
}
}
if (notifyConsumer) {
TableViewModel* model = self.consumer.tableViewModel;
NSUInteger sectionIndex =
[model sectionForSectionIdentifier:NonPersonalizedSectionIdentifier];
NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:sectionIndex];
[self.consumer reloadSections:indexSet];
}
}
#pragma mark - Properties
- (BOOL)hasPrimaryIdentity {
return self.authService->HasPrimaryIdentity(signin::ConsentLevel::kSignin);
}
- (ItemArray)nonPersonalizedItems {
if (!_nonPersonalizedItems) {
NSMutableArray* items = [NSMutableArray array];
TableViewItem* allowSigninItem = [self allowChromeSigninItem];
allowSigninItem.accessibilityIdentifier =
kAllowSigninItemAccessibilityIdentifier;
[items addObject:allowSigninItem];
if (self.localPrefService->IsManagedPreference(
metrics::prefs::kMetricsReportingEnabled) &&
!self.localPrefService->GetBoolean(
metrics::prefs::kMetricsReportingEnabled)) {
TableViewInfoButtonItem* improveChromeItem = [self
tableViewInfoButtonItemType:ImproveChromeManagedItemType
textStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_IMPROVE_CHROME_TEXT
detailStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_IMPROVE_CHROME_DETAIL
status:self.sendDataUsagePreference];
[items addObject:improveChromeItem];
} else {
SyncSwitchItem* improveChromeItem = [self
switchItemWithItemType:ImproveChromeItemType
textStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_IMPROVE_CHROME_TEXT
detailStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_IMPROVE_CHROME_DETAIL];
improveChromeItem.accessibilityIdentifier =
kImproveChromeItemAccessibilityIdentifier;
[items addObject:improveChromeItem];
}
if (self.userPrefService->IsManagedPreference(
unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled)) {
TableViewInfoButtonItem* betterSearchAndBrowsingItem = [self
tableViewInfoButtonItemType:BetterSearchAndBrowsingManagedItemType
textStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_BETTER_SEARCH_AND_BROWSING_TEXT
detailStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_BETTER_SEARCH_AND_BROWSING_DETAIL
status:self.anonymizedDataCollectionPreference];
betterSearchAndBrowsingItem.accessibilityIdentifier =
kBetterSearchAndBrowsingItemAccessibilityID;
[items addObject:betterSearchAndBrowsingItem];
} else {
SyncSwitchItem* betterSearchAndBrowsingItem = [self
switchItemWithItemType:BetterSearchAndBrowsingItemType
textStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_BETTER_SEARCH_AND_BROWSING_TEXT
detailStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_BETTER_SEARCH_AND_BROWSING_DETAIL];
betterSearchAndBrowsingItem.accessibilityIdentifier =
kBetterSearchAndBrowsingItemAccessibilityID;
[items addObject:betterSearchAndBrowsingItem];
}
if (self.userPrefService->IsManagedPreference(
prefs::kSearchSuggestEnabled)) {
TableViewInfoButtonItem* improveSearchSuggestionsItem = [self
tableViewInfoButtonItemType:ImproveSearchSuggestionsManagedItemType
textStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_IMPROVE_SEARCH_SUGGESTIONS_TEXT
detailStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_IMPROVE_SEARCH_SUGGESTIONS_DETAIL
status:self.improveSearchSuggestionsPreference
.value];
[items addObject:improveSearchSuggestionsItem];
} else {
SyncSwitchItem* improveSearchSuggestionsItem = [self
switchItemWithItemType:ImproveSearchSuggestionsItemType
textStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_IMPROVE_SEARCH_SUGGESTIONS_TEXT
detailStringID:
IDS_IOS_GOOGLE_SERVICES_SETTINGS_IMPROVE_SEARCH_SUGGESTIONS_DETAIL];
[items addObject:improveSearchSuggestionsItem];
}
if (self.userPrefService->IsManagedPreference(
prefs::kTrackPricesOnTabsEnabled)) {
TableViewInfoButtonItem* trackPricesOnTabsItem = [self
tableViewInfoButtonItemType:TrackPricesOnTabsItemType
textStringID:IDS_IOS_TRACK_PRICES_ON_TABS
detailStringID:IDS_IOS_TRACK_PRICES_ON_TABS_DESCRIPTION
status:self.trackPricesOnTabsPreference];
trackPricesOnTabsItem.accessibilityIdentifier =
kTrackPricesOnTabsItemAccessibilityID;
[items addObject:trackPricesOnTabsItem];
} else {
SyncSwitchItem* trackPricesOnTabsItem = [self
switchItemWithItemType:TrackPricesOnTabsItemType
textStringID:IDS_IOS_TRACK_PRICES_ON_TABS
detailStringID:IDS_IOS_TRACK_PRICES_ON_TABS_DESCRIPTION];
trackPricesOnTabsItem.accessibilityIdentifier =
kTrackPricesOnTabsItemAccessibilityID;
[items addObject:trackPricesOnTabsItem];
}
_nonPersonalizedItems = items;
}
return _nonPersonalizedItems;
}
#pragma mark - Private
// Creates an item with a switch toggle.
- (SyncSwitchItem*)switchItemWithItemType:(NSInteger)itemType
textStringID:(int)textStringID
detailStringID:(int)detailStringID {
SyncSwitchItem* switchItem = [[SyncSwitchItem alloc] initWithType:itemType];
switchItem.text = GetNSString(textStringID);
if (detailStringID)
switchItem.detailText = GetNSString(detailStringID);
return switchItem;
}
// Create a TableViewInfoButtonItem instance used for items that the user is
// not allowed to switch on or off (enterprise reason for example).
- (TableViewInfoButtonItem*)tableViewInfoButtonItemType:(NSInteger)itemType
textStringID:(int)textStringID
detailStringID:(int)detailStringID
status:(BOOL)status {
TableViewInfoButtonItem* managedItem =
[[TableViewInfoButtonItem alloc] initWithType:itemType];
managedItem.text = GetNSString(textStringID);
managedItem.detailText = GetNSString(detailStringID);
managedItem.statusText = status ? l10n_util::GetNSString(IDS_IOS_SETTING_ON)
: l10n_util::GetNSString(IDS_IOS_SETTING_OFF);
if (!status) {
managedItem.iconTintColor = [UIColor colorNamed:kGrey300Color];
}
// This item is not controllable, then set the color opacity to 40%.
managedItem.textColor =
[[UIColor colorNamed:kTextPrimaryColor] colorWithAlphaComponent:0.4f];
managedItem.detailTextColor =
[[UIColor colorNamed:kTextSecondaryColor] colorWithAlphaComponent:0.4f];
managedItem.accessibilityHint =
l10n_util::GetNSString(IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT);
return managedItem;
}
#pragma mark - GoogleServicesSettingsViewControllerModelDelegate
- (void)googleServicesSettingsViewControllerLoadModel:
(GoogleServicesSettingsViewController*)controller {
DCHECK_EQ(self.consumer, controller);
[self loadNonPersonalizedSection];
}
- (BOOL)isAllowChromeSigninItem:(int)type {
return type == AllowChromeSigninItemType;
}
- (BOOL)isViewControllerSubjectToParentalControls {
return supervised_user::IsSubjectToParentalControls(self.userPrefService);
}
#pragma mark - GoogleServicesSettingsServiceDelegate
- (void)toggleSwitchItem:(TableViewItem*)item
withValue:(BOOL)value
targetRect:(CGRect)targetRect {
SyncSwitchItem* syncSwitchItem = base::apple::ObjCCast<SyncSwitchItem>(item);
syncSwitchItem.on = value;
ItemType type = static_cast<ItemType>(item.type);
switch (type) {
case AllowChromeSigninItemType: {
if (self.hasPrimaryIdentity) {
__weak GoogleServicesSettingsMediator* weakSelf = self;
[self.commandHandler
showSignOutFromTargetRect:targetRect
completion:^(BOOL success) {
weakSelf.allowChromeSigninPreference.value =
success ? value : !value;
[weakSelf
updateNonPersonalizedSectionWithNotification:
YES];
}];
} else {
self.allowChromeSigninPreference.value = value;
}
break;
}
case ImproveChromeItemType:
self.sendDataUsagePreference.value = value;
// Don't set value if sendDataUsageWifiOnlyPreference has not been
// allocated.
if (value && self.sendDataUsageWifiOnlyPreference) {
// Should be wifi only, until https://crbug.com/872101 is fixed.
self.sendDataUsageWifiOnlyPreference.value = YES;
}
break;
case BetterSearchAndBrowsingItemType:
self.anonymizedDataCollectionPreference.value = value;
break;
case ImproveSearchSuggestionsItemType:
self.improveSearchSuggestionsPreference.value = value;
break;
case TrackPricesOnTabsItemType:
self.trackPricesOnTabsPreference.value = value;
break;
case BetterSearchAndBrowsingManagedItemType:
case ImproveChromeManagedItemType:
case ImproveSearchSuggestionsManagedItemType:
NOTREACHED();
break;
}
}
#pragma mark - BooleanObserver
- (void)booleanDidChange:(id<ObservableBoolean>)observableBoolean {
[self updateNonPersonalizedSectionWithNotification:YES];
}
@end