[go: nahoru, domu]

blob: 44d54d704329ca9c265297a44955bb620284b427 [file] [log] [blame]
// Copyright 2022 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/app_store_rating/app_store_rating_scene_agent.h"
#import <Foundation/Foundation.h>
#import "base/apple/foundation_util.h"
#import "base/metrics/histogram_functions.h"
#import "base/time/time.h"
#import "components/password_manager/core/browser/password_manager_util.h"
#import "components/prefs/pref_service.h"
#import "components/version_info/channel.h"
#import "ios/chrome/browser/default_browser/utils.h"
#import "ios/chrome/browser/promos_manager/constants.h"
#import "ios/chrome/browser/promos_manager/promos_manager.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/browser/browser.h"
#import "ios/chrome/browser/shared/model/browser/browser_provider.h"
#import "ios/chrome/browser/shared/model/browser/browser_provider_interface.h"
#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/ui/app_store_rating/constants.h"
#import "ios/chrome/browser/ui/app_store_rating/features.h"
#import "ios/chrome/common/channel_info.h"
@interface AppStoreRatingSceneAgent ()
// Determines whether the user has used Chrome for at least 3
// different days within the past 7 days for stable channel.
// In Canary and Dev channels, the requirement is at least 1 day
// in the past 7 days.
@property(nonatomic, assign, readonly, getter=isDaysInPastWeekRequirementMet)
BOOL daysInPastWeekRequirementMet;
// Determines whether the user has used Chrome for at least 15
// different days overall for stable channel. In Canary and Dev channels,
// the requirement is at least 1 day.
@property(nonatomic, assign, readonly, getter=isTotalDaysRequirementMet)
BOOL totalDaysRequirementMet;
// Determines whether the user has enabled the Credentials
// Provider Extension.
@property(nonatomic, assign, readonly, getter=isCPEEnabled) BOOL CPEEnabled;
// The PromosManager is used to register promos.
@property(nonatomic, assign) PromosManager* promosManager;
@end
@implementation AppStoreRatingSceneAgent
- (instancetype)initWithPromosManager:(PromosManager*)promosManager {
if (self = [super init]) {
_promosManager = promosManager;
}
return self;
}
- (BOOL)isUserEngaged {
if (IsAppStoreRatingLoosenedTriggersEnabled()) {
return IsChromeLikelyDefaultBrowser() || self.CPEEnabled;
}
return IsChromeLikelyDefaultBrowser() && self.daysInPastWeekRequirementMet &&
self.totalDaysRequirementMet && self.CPEEnabled;
}
#pragma mark - SceneStateObserver
- (void)sceneState:(SceneState*)sceneState
transitionedToActivationLevel:(SceneActivationLevel)level {
switch (level) {
case SceneActivationLevelUnattached:
case SceneActivationLevelDisconnected:
// no-op.
break;
case SceneActivationLevelBackground:
// no-op.
break;
case SceneActivationLevelForegroundInactive:
// no-op.
break;
case SceneActivationLevelForegroundActive:
[self updateUserDefaults];
BOOL isUserEngaged = [self isUserEngaged];
base::UmaHistogramBoolean("IOS.AppStoreRating.UserIsEligible",
isUserEngaged);
if (isUserEngaged && [self promoShownOver365DaysAgo]) {
[self requestPromoDisplay];
}
break;
}
}
#pragma mark - Getters
- (BOOL)isDaysInPastWeekRequirementMet {
NSArray* activeDaysInPastWeek = base::apple::ObjCCastStrict<NSArray>(
[[NSUserDefaults standardUserDefaults]
objectForKey:kAppStoreRatingActiveDaysInPastWeekKey]);
const NSUInteger appStoreRatingTotalDaysOnChromeRequirement =
(GetChannel() == version_info::Channel::DEV ||
GetChannel() == version_info::Channel::CANARY)
? 1
: 3;
return [activeDaysInPastWeek count] >=
appStoreRatingTotalDaysOnChromeRequirement;
;
}
- (BOOL)isTotalDaysRequirementMet {
NSInteger appStoreRatingDaysOnChromeInPastWeekRequirement =
(GetChannel() == version_info::Channel::DEV ||
GetChannel() == version_info::Channel::CANARY)
? 1
: 15;
return [[NSUserDefaults standardUserDefaults]
integerForKey:kAppStoreRatingTotalDaysOnChromeKey] >=
appStoreRatingDaysOnChromeInPastWeekRequirement;
}
- (BOOL)isCPEEnabled {
DCHECK(self.sceneState.browserProviderInterface.mainBrowserProvider.browser);
PrefService* pref_service =
self.sceneState.browserProviderInterface.mainBrowserProvider.browser
->GetBrowserState()
->GetPrefs();
return password_manager_util::IsCredentialProviderEnabledOnStartup(
pref_service);
}
#pragma mark - Private
// Calls the PromosManager to request iOS displays the
// App Store Rating prompt to the user.
- (void)requestPromoDisplay {
if (!_promosManager || !GetApplicationContext()->GetLocalState()->GetBoolean(
prefs::kAppStoreRatingPolicyEnabled)) {
return;
}
_promosManager->RegisterPromoForSingleDisplay(
promos_manager::Promo::AppStoreRating);
[self recordPromoRequested];
}
// Returns an array of user's active days in the past week, not including the
// current session.
- (std::vector<base::Time>)activeDaysInPastWeek {
NSArray* storedActiveDaysInPastWeek = base::apple::ObjCCastStrict<NSArray>(
[[NSUserDefaults standardUserDefaults]
objectForKey:kAppStoreRatingActiveDaysInPastWeekKey]);
std::vector<base::Time> activeDaysInPastWeek;
base::Time midnightToday = base::Time::Now().UTCMidnight();
for (NSDate* storedDate : storedActiveDaysInPastWeek) {
base::Time date = base::Time::FromNSDate(storedDate).UTCMidnight();
if (midnightToday - date < base::Days(7)) {
activeDaysInPastWeek.push_back(date.UTCMidnight());
}
}
return activeDaysInPastWeek;
}
// Stores array of user's active days in the past week to
// `kAppStoreRatingActiveDaysInPastWeekKey` in NSUserDefaults.
- (void)storeActiveDaysInPastWeek:
(const std::vector<base::Time>&)activeDaysInPastWeek {
NSMutableArray* datesToStore = [[NSMutableArray alloc] init];
for (base::Time date : activeDaysInPastWeek) {
NSDate* dateToStore = date.ToNSDate();
[datesToStore addObject:dateToStore];
}
[[NSUserDefaults standardUserDefaults]
setObject:datesToStore
forKey:kAppStoreRatingActiveDaysInPastWeekKey];
}
// Updates `kAppStoreRatingTotalDaysOnChromeKey` and
// `kAppStoreRatingActiveDaysInPastWeekKey` in NSUserDefaults. This method is
// destructive and may modify `kAppStoreRatingActiveDaysInPastWeekKey`.
- (void)updateUserDefaults {
std::vector<base::Time> activeDaysInPastWeek = [self activeDaysInPastWeek];
// Check if today has been recorded. If not, record today.
base::Time today = base::Time::Now().UTCMidnight();
BOOL isTodayRecorded = !activeDaysInPastWeek.empty() &&
today - activeDaysInPastWeek.back() < base::Days(1);
if (isTodayRecorded) {
return;
}
activeDaysInPastWeek.push_back(today);
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
NSInteger totalDaysOnChrome =
[defaults integerForKey:kAppStoreRatingTotalDaysOnChromeKey] + 1;
[defaults setInteger:totalDaysOnChrome
forKey:kAppStoreRatingTotalDaysOnChromeKey];
[self storeActiveDaysInPastWeek:activeDaysInPastWeek];
}
// Called when promo is registered with promos manager. Saves today's date to
// NSUserDefaults.
- (void)recordPromoRequested {
base::Time today = base::Time::Now().UTCMidnight();
[[NSUserDefaults standardUserDefaults]
setObject:today.ToNSDate()
forKey:kAppStoreRatingLastShownPromoDayKey];
}
// Checks if the the promo was already requested for the user within the past
// 365 days.
- (BOOL)promoShownOver365DaysAgo {
NSDate* lastShown =
base::apple::ObjCCastStrict<NSDate>([[NSUserDefaults standardUserDefaults]
objectForKey:kAppStoreRatingLastShownPromoDayKey]);
if (!lastShown) {
return YES;
}
base::TimeDelta daysSincePromoLastShown =
base::Time::Now().UTCMidnight() - base::Time::FromNSDate(lastShown);
return daysSincePromoLastShown > base::Days(365);
}
@end