blob: 3dec5b0e67a90b2d0d08bfe1079b57ba1d2e8d88 [file] [log] [blame]
// Copyright 2013 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/promos/signin_promo_view_controller.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/strings/sys_string_conversions.h"
#include "base/version.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/version_info/version_info.h"
#include "ios/chrome/app/tests_hook.h"
#import "ios/chrome/browser/browser_state/chrome_browser_state.h"
#include "ios/chrome/browser/signin/authentication_service.h"
#include "ios/chrome/browser/signin/authentication_service_factory.h"
#import "ios/chrome/browser/ui/commands/application_commands.h"
#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
#import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
#import "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
#include "net/base/network_change_notifier.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
// Key in the UserDefaults to record the version of the application when the
// SSO Recall promo has been displayed.
NSString* kDisplayedSSORecallForMajorVersionKey =
@"DisplayedSSORecallForMajorVersionKey";
// Key in the UserDefaults to record the GAIA id list when the sign-in promo
// was shown.
NSString* kLastShownAccountGaiaIdVersionKey =
@"LastShownAccountGaiaIdVersionKey";
// Key in the UserDefaults to record the number of time the sign-in promo has
// been shown.
NSString* kSigninPromoViewDisplayCountKey = @"SigninPromoViewDisplayCountKey";
namespace {
// Key in the UserDefaults to track how many times the SSO Recall promo has been
// displayed.
NSString* kDisplayedSSORecallPromoCountKey = @"DisplayedSSORecallPromoCount";
// Name of the UMA SSO Recall histogram.
const char* const kUMASSORecallPromoAction = "SSORecallPromo.PromoAction";
// Name of the histogram recording how many accounts were available on the
// device when the promo was shown.
const char* const kUMASSORecallAccountsAvailable =
"SSORecallPromo.AccountsAvailable";
// Name of the histogram recording how many times the promo has been shown.
const char* const kUMASSORecallPromoSeenCount = "SSORecallPromo.PromoSeenCount";
// Values of the UMA SSORecallPromo.PromoAction histogram.
enum PromoAction {
ACTION_DISMISSED,
ACTION_ENABLED_SSO_ACCOUNT,
ACTION_ADDED_ANOTHER_ACCOUNT,
PROMO_ACTION_COUNT
};
NSSet* GaiaIdSetWithIdentities(NSArray* identities) {
NSMutableSet* gaiaIdSet = [NSMutableSet set];
for (ChromeIdentity* identity in identities) {
[gaiaIdSet addObject:identity.gaiaID];
}
return [gaiaIdSet copy];
}
} // namespace
@interface SigninPromoViewController ()<ChromeSigninViewControllerDelegate>
@end
@implementation SigninPromoViewController {
BOOL _addAccountOperation;
}
- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
dispatcher:(id<ApplicationCommands>)dispatcher {
self = [super initWithBrowserState:browserState
accessPoint:signin_metrics::AccessPoint::
ACCESS_POINT_SIGNIN_PROMO
promoAction:signin_metrics::PromoAction::
PROMO_ACTION_NO_SIGNIN_PROMO
signInIdentity:nil
dispatcher:dispatcher];
if (self) {
super.delegate = self;
}
return self;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if ([self isBeingPresented] || [self isMovingToParentViewController]) {
signin_metrics::LogSigninAccessPointStarted(
signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO,
signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO);
signin_metrics::RecordSigninUserActionForAccessPoint(
signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO,
signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO);
}
[self recordPromoDisplayed];
}
- (void)dismissWithSignedIn:(BOOL)signedIn
showAccountsSettings:(BOOL)showAccountsSettings {
DCHECK(self.presentingViewController);
ProceduralBlock completion = nil;
if (showAccountsSettings) {
__weak UIViewController* presentingViewController =
self.presentingViewController;
__weak id<ApplicationCommands> dispatcher = self.dispatcher;
completion = ^{
[dispatcher showAdvancedSigninSettingsFromViewController:
presentingViewController];
};
}
[self.presentingViewController dismissViewControllerAnimated:YES
completion:completion];
}
// Records in user defaults that the promo has been shown as well as the number
// of times it's been displayed.
- (void)recordPromoDisplayed {
[[self class] recordVersionSeen];
NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults];
int promoSeenCount =
[standardDefaults integerForKey:kDisplayedSSORecallPromoCountKey];
promoSeenCount++;
[standardDefaults setInteger:promoSeenCount
forKey:kDisplayedSSORecallPromoCountKey];
NSArray* identities = ios::GetChromeBrowserProvider()
->GetChromeIdentityService()
->GetAllIdentitiesSortedForDisplay();
UMA_HISTOGRAM_COUNTS_100(kUMASSORecallAccountsAvailable, [identities count]);
UMA_HISTOGRAM_COUNTS_100(kUMASSORecallPromoSeenCount, promoSeenCount);
}
+ (void)recordVersionSeen {
base::Version currentVersion = [self currentVersion];
NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults];
[standardDefaults
setObject:base::SysUTF8ToNSString(currentVersion.GetString())
forKey:kDisplayedSSORecallForMajorVersionKey];
NSArray* identities = ios::GetChromeBrowserProvider()
->GetChromeIdentityService()
->GetAllIdentitiesSortedForDisplay();
NSArray* gaiaIdList = GaiaIdSetWithIdentities(identities).allObjects;
[standardDefaults setObject:gaiaIdList
forKey:kLastShownAccountGaiaIdVersionKey];
NSInteger displayCount =
[standardDefaults integerForKey:kSigninPromoViewDisplayCountKey];
++displayCount;
[standardDefaults setInteger:displayCount
forKey:kSigninPromoViewDisplayCountKey];
}
+ (base::Version)currentVersion {
base::Version currentVersion = version_info::GetVersion();
DCHECK(currentVersion.IsValid());
return currentVersion;
}
#pragma mark - PromoViewController
+ (BOOL)shouldBePresentedForBrowserState:
(ios::ChromeBrowserState*)browserState {
if (tests_hook::DisableSigninRecallPromo())
return NO;
if (browserState->IsOffTheRecord())
return NO;
// There will be an error shown if the user chooses to sign in or select
// another account while offline.
if (net::NetworkChangeNotifier::IsOffline())
return NO;
AuthenticationService* authService =
AuthenticationServiceFactory::GetForBrowserState(browserState);
// Do not show the SSO promo if the user is already logged in.
if (authService->IsAuthenticated())
return NO;
// Show the promo at most every two major versions.
NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults];
NSString* versionShown =
[standardDefaults stringForKey:kDisplayedSSORecallForMajorVersionKey];
if (versionShown) {
base::Version seenVersion(base::SysNSStringToUTF8(versionShown));
base::Version currentVersion = [[self class] currentVersion];
if (currentVersion.components()[0] - seenVersion.components()[0] < 2)
return NO;
}
// Don't show the promo if there is no identities.
NSArray* identities = ios::GetChromeBrowserProvider()
->GetChromeIdentityService()
->GetAllIdentitiesSortedForDisplay();
if ([identities count] == 0)
return NO;
// The sign-in promo should be shown twice, even if no account has been added.
NSInteger displayCount =
[standardDefaults integerForKey:kSigninPromoViewDisplayCountKey];
if (displayCount <= 1)
return YES;
// Otherwise, it can be shown only if a new account has been added.
NSArray* lastKnownGaiaIdList =
[standardDefaults arrayForKey:kLastShownAccountGaiaIdVersionKey];
NSSet* lastKnownGaiaIdSet = lastKnownGaiaIdList
? [NSSet setWithArray:lastKnownGaiaIdList]
: [NSSet set];
NSSet* currentGaiaIdSet = GaiaIdSetWithIdentities(identities);
return [lastKnownGaiaIdSet isSubsetOfSet:currentGaiaIdSet] &&
![lastKnownGaiaIdSet isEqualToSet:currentGaiaIdSet];
}
#pragma mark - ChromeSigninViewControllerDelegate
- (void)willStartSignIn:(ChromeSigninViewController*)controller {
DCHECK_EQ(self, controller);
_addAccountOperation = NO;
}
- (void)willStartAddAccount:(ChromeSigninViewController*)controller {
DCHECK_EQ(self, controller);
_addAccountOperation = YES;
}
- (void)didSkipSignIn:(ChromeSigninViewController*)controller {
DCHECK_EQ(self, controller);
UMA_HISTOGRAM_ENUMERATION(kUMASSORecallPromoAction, ACTION_DISMISSED,
PROMO_ACTION_COUNT);
[self dismissWithSignedIn:NO showAccountsSettings:NO];
}
- (void)didFailSignIn:(ChromeSigninViewController*)controller {
DCHECK_EQ(self, controller);
[self dismissWithSignedIn:NO showAccountsSettings:NO];
}
- (void)didSignIn:(ChromeSigninViewController*)controller {
DCHECK_EQ(self, controller);
}
- (void)didUndoSignIn:(ChromeSigninViewController*)controller
identity:(ChromeIdentity*)identity {
DCHECK_EQ(self, controller);
// No accounts case is impossible in SigninPromoViewController, nothing to do.
}
- (void)didAcceptSignIn:(ChromeSigninViewController*)controller
showAccountsSettings:(BOOL)showAccountsSettings {
DCHECK_EQ(self, controller);
PromoAction promoAction = _addAccountOperation ? ACTION_ADDED_ANOTHER_ACCOUNT
: ACTION_ENABLED_SSO_ACCOUNT;
UMA_HISTOGRAM_ENUMERATION(kUMASSORecallPromoAction, promoAction,
PROMO_ACTION_COUNT);
[self dismissWithSignedIn:YES showAccountsSettings:showAccountsSettings];
}
@end