blob: e4bcdd9093f70c571c51fc479d3ea9845caa02b4 [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/core/browser/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/UIKit+ChromeExecuteCommand.h"
#include "ios/public/provider/chrome/browser/chrome_browser_provider.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";
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 avilable 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
};
} // namespace
@interface SigninPromoViewController ()<ChromeSigninViewControllerDelegate>
@end
@implementation SigninPromoViewController {
BOOL _addAccountOperation;
}
- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState {
self = [super initWithBrowserState:browserState
isPresentedOnSettings:NO
signInAccessPoint:signin_metrics::AccessPoint::
ACCESS_POINT_SIGNIN_PROMO
signInIdentity:nil];
if (self) {
super.delegate = self;
}
return self;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if ([self isBeingPresented] || [self isMovingToParentViewController]) {
base::RecordAction(
base::UserMetricsAction("Signin_Signin_FromSigninPromo"));
signin_metrics::LogSigninAccessPointStarted(
signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO);
}
[self recordPromoDisplayed];
}
- (void)dismissWithSignedIn:(BOOL)signedIn
executeCommand:(GenericChromeCommand*)command {
DCHECK(self.presentingViewController);
UIViewController* presentingViewController = self.presentingViewController;
[presentingViewController
dismissViewControllerAnimated:YES
completion:^{
if (command) {
[presentingViewController
chromeExecuteCommand:command];
}
}];
}
// 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);
}
// Called to obtain the current version of the application to compare against
// the version the last time the promo was seen.
// Separated out into a discrete function to allow overriding when testing.
+ (void)recordVersionSeen {
base::Version currentVersion = [self currentVersion];
NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults];
[standardDefaults
setObject:base::SysUTF8ToNSString(currentVersion.GetString())
forKey:kDisplayedSSORecallForMajorVersionKey];
}
+ (base::Version)currentVersion {
base::Version currentVersion(version_info::GetVersionNumber());
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->GetAuthenticatedUserEmail())
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;
}
NSArray* identities = ios::GetChromeBrowserProvider()
->GetChromeIdentityService()
->GetAllIdentitiesSortedForDisplay();
return [identities count] > 0;
}
+ (UIViewController*)controllerToPresentForBrowserState:
(ios::ChromeBrowserState*)browserState {
UIViewController* controller =
[[SigninPromoViewController alloc] initWithBrowserState:browserState];
return controller;
}
#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 executeCommand:nil];
}
- (void)didFailSignIn:(ChromeSigninViewController*)controller {
DCHECK_EQ(self, controller);
[self dismissWithSignedIn:NO executeCommand:nil];
}
- (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
executeCommand:(GenericChromeCommand*)command {
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 executeCommand:command];
}
@end