Commit 85fc916f authored by Chris Lu's avatar Chris Lu Committed by Commit Bot

[ios] Refine Default Browser Promo trigger

Instead of relying on SceneState order calls, which cannot be
guaranteed, a new AppState shouldShowDefaultBrowserPromo property is
added to keep track of when a resumption of session is due to a cold
start.

A new DefaultBrowserSceneAgent class will listen to SceneActivationLevel
and check the shouldShowDefaultBrowserPromo property to determine
when to show the promo.

Also, an iOS14 check is added to IsLikelyInterestedDefaultBrowserUser to
ensure that only users on iOS14 see this promo.

Bug: 1107489
Change-Id: I71c77a98e20644c620f4c4d3c60cb0b253931cc3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2378710
Commit-Queue: Chris Lu <thegreenfrog@chromium.org>
Reviewed-by: default avatarStepan Khapugin <stkhapugin@chromium.org>
Reviewed-by: default avatarRohit Rao <rohitrao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805357}
parent 38e66853
...@@ -60,6 +60,10 @@ initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher ...@@ -60,6 +60,10 @@ initWithBrowserLauncher:(id<BrowserLauncher>)browserLauncher
// YES if the sign-in upgrade promo has been presented to the user, once. // YES if the sign-in upgrade promo has been presented to the user, once.
@property(nonatomic) BOOL signinUpgradePromoPresentedOnce; @property(nonatomic) BOOL signinUpgradePromoPresentedOnce;
// YES if the default browser fullscreen promo has met the qualifications to be
// shown after the last cold start.
@property(nonatomic) BOOL shouldShowDefaultBrowserPromo;
// When multiwindow is unavailable, this is the only scene state. It is created // When multiwindow is unavailable, this is the only scene state. It is created
// by the app delegate. // by the app delegate.
@property(nonatomic, strong) SceneState* mainSceneState; @property(nonatomic, strong) SceneState* mainSceneState;
......
...@@ -40,6 +40,8 @@ source_set("scene") { ...@@ -40,6 +40,8 @@ source_set("scene") {
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
sources = [ sources = [
"connection_information.h", "connection_information.h",
"default_browser_scene_agent.h",
"default_browser_scene_agent.mm",
"scene_controller.h", "scene_controller.h",
"scene_controller.mm", "scene_controller.mm",
"scene_delegate.h", "scene_delegate.h",
......
// Copyright 2020 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.
#ifndef IOS_CHROME_BROWSER_UI_MAIN_DEFAULT_BROWSER_SCENE_AGENT_H_
#define IOS_CHROME_BROWSER_UI_MAIN_DEFAULT_BROWSER_SCENE_AGENT_H_
#import "ios/chrome/browser/ui/main/scene_state.h"
@class CommandDispatcher;
// A scene agent that shows the default browser fullscreen promo UI based on the
// SceneActivationLevel changes.
@interface DefaultBrowserSceneAgent : NSObject <SceneAgent>
- (instancetype)initWithCommandDispatcher:(CommandDispatcher*)dispatcher;
@end
#endif // IOS_CHROME_BROWSER_UI_MAIN_DEFAULT_BROWSER_SCENE_AGENT_H_
// Copyright 2020 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/main/default_browser_scene_agent.h"
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/browser/ui/commands/command_dispatcher.h"
#import "ios/chrome/browser/ui/commands/whats_new_commands.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface DefaultBrowserSceneAgent () <SceneStateObserver>
// Scene to which this agent is attached.
// Implements the setter from SceneAgent protocol.
@property(nonatomic, weak) SceneState* sceneState;
// Command Dispatcher.
@property(nonatomic, weak) CommandDispatcher* dispatcher;
@end
@implementation DefaultBrowserSceneAgent
- (instancetype)initWithCommandDispatcher:(CommandDispatcher*)dispatcher {
if ([super init])
_dispatcher = dispatcher;
return self;
}
#pragma mark - SceneAgent
- (void)setSceneState:(SceneState*)sceneState {
DCHECK(!_sceneState);
_sceneState = sceneState;
[sceneState addObserver:self];
}
#pragma mark - SceneStateObserver
- (void)sceneState:(SceneState*)sceneState
transitionedToActivationLevel:(SceneActivationLevel)level {
AppState* appState = self.sceneState.appState;
// Can only present UI when activation level is
// SceneActivationLevelForegroundActive. Show the UI if user has met the
// qualifications to be shown the promo.
if (level == SceneActivationLevelForegroundActive &&
appState.shouldShowDefaultBrowserPromo && appState.currentUIBlocker) {
[HandlerForProtocol(self.dispatcher, WhatsNewCommands)
showDefaultBrowserFullscreenPromo];
appState.shouldShowDefaultBrowserPromo = NO;
}
}
@end
...@@ -54,13 +54,13 @@ ...@@ -54,13 +54,13 @@
#import "ios/chrome/browser/ui/commands/omnibox_commands.h" #import "ios/chrome/browser/ui/commands/omnibox_commands.h"
#import "ios/chrome/browser/ui/commands/open_new_tab_command.h" #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
#import "ios/chrome/browser/ui/commands/show_signin_command.h" #import "ios/chrome/browser/ui/commands/show_signin_command.h"
#import "ios/chrome/browser/ui/commands/whats_new_commands.h"
#import "ios/chrome/browser/ui/first_run/first_run_util.h" #import "ios/chrome/browser/ui/first_run/first_run_util.h"
#import "ios/chrome/browser/ui/first_run/orientation_limiting_navigation_controller.h" #import "ios/chrome/browser/ui/first_run/orientation_limiting_navigation_controller.h"
#import "ios/chrome/browser/ui/first_run/welcome_to_chrome_view_controller.h" #import "ios/chrome/browser/ui/first_run/welcome_to_chrome_view_controller.h"
#include "ios/chrome/browser/ui/history/history_coordinator.h" #include "ios/chrome/browser/ui/history/history_coordinator.h"
#import "ios/chrome/browser/ui/main/browser_interface_provider.h" #import "ios/chrome/browser/ui/main/browser_interface_provider.h"
#import "ios/chrome/browser/ui/main/browser_view_wrangler.h" #import "ios/chrome/browser/ui/main/browser_view_wrangler.h"
#import "ios/chrome/browser/ui/main/default_browser_scene_agent.h"
#import "ios/chrome/browser/ui/main/ui_blocker_scene_agent.h" #import "ios/chrome/browser/ui/main/ui_blocker_scene_agent.h"
#import "ios/chrome/browser/ui/scoped_ui_blocker/scoped_ui_blocker.h" #import "ios/chrome/browser/ui/scoped_ui_blocker/scoped_ui_blocker.h"
#import "ios/chrome/browser/ui/settings/settings_navigation_controller.h" #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
...@@ -298,23 +298,7 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -298,23 +298,7 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
if (level == SceneActivationLevelForegroundActive) { if (level == SceneActivationLevelForegroundActive) {
if (![self presentSigninUpgradePromoIfPossible]) { if (![self presentSigninUpgradePromoIfPossible]) {
if (![self presentSignInAccountsViewControllerIfNecessary] && [self presentSignInAccountsViewControllerIfNecessary];
initializingUIInColdStart) {
// Show the Default Browser promo UI if the user's past behavior fits
// the categorization of potentially interested users or if the user is
// signed in. Do not show if it is determined that Chrome is already the
// default browser or if the user has already seen the promo UI.
BOOL isEligibleUser = IsLikelyInterestedDefaultBrowserUser() ||
ios::GetChromeBrowserProvider()
->GetChromeIdentityService()
->HasIdentities();
if (!IsChromeLikelyDefaultBrowser() &&
!HasUserInteractedWithFullscreenPromoBefore() && isEligibleUser) {
[HandlerForProtocol(
self.currentInterface.browser->GetCommandDispatcher(),
WhatsNewCommands) showDefaultBrowserFullscreenPromo];
}
}
} }
// Mitigation for crbug.com/1092326, where a nil browser state is passed // Mitigation for crbug.com/1092326, where a nil browser state is passed
// (presumably because mainInterface is nil as well). // (presumably because mainInterface is nil as well).
...@@ -548,6 +532,12 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -548,6 +532,12 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
// Ensure the main browser is created. This also creates the BVC. // Ensure the main browser is created. This also creates the BVC.
[self.browserViewWrangler createMainBrowser]; [self.browserViewWrangler createMainBrowser];
// Add Scene Agent that requires CommandDispatcher.
[self.sceneState
addAgent:[[DefaultBrowserSceneAgent alloc]
initWithCommandDispatcher:self.mainInterface.browser
->GetCommandDispatcher()]];
if (IsSceneStartupSupported() && if (IsSceneStartupSupported() &&
base::FeatureList::IsEnabled(kEnableFullPageScreenshot)) { base::FeatureList::IsEnabled(kEnableFullPageScreenshot)) {
if (@available(iOS 13, *)) { if (@available(iOS 13, *)) {
...@@ -663,6 +653,23 @@ const char kMultiWindowOpenInNewWindowHistogram[] = ...@@ -663,6 +653,23 @@ const char kMultiWindowOpenInNewWindowHistogram[] =
// Do not ever show the 'restore' infobar during first run. // Do not ever show the 'restore' infobar during first run.
self.mainController.restoreHelper = nil; self.mainController.restoreHelper = nil;
} }
// If skipping first run and not in Safe Mode, consider showing the default
// browser promo.
if (!firstRun && !self.sceneState.appState.isInSafeMode) {
// Show the Default Browser promo UI if the user's past behavior fits
// the categorization of potentially interested users or if the user is
// signed in. Do not show if it is determined that Chrome is already the
// default browser or if the user has already seen the promo UI.
BOOL isEligibleUser = IsLikelyInterestedDefaultBrowserUser() ||
ios::GetChromeBrowserProvider()
->GetChromeIdentityService()
->HasIdentities();
if (!IsChromeLikelyDefaultBrowser() &&
!HasUserInteractedWithFullscreenPromoBefore() && isEligibleUser) {
self.sceneState.appState.shouldShowDefaultBrowserPromo = YES;
}
}
} }
// This method completely destroys all of the UI. It should be called when the // This method completely destroys all of the UI. It should be called when the
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#import "ios/chrome/browser/ui/whats_new/default_browser_utils.h" #import "ios/chrome/browser/ui/whats_new/default_browser_utils.h"
#include "base/ios/ios_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
...@@ -87,5 +89,5 @@ bool IsLikelyInterestedDefaultBrowserUser() { ...@@ -87,5 +89,5 @@ bool IsLikelyInterestedDefaultBrowserUser() {
[[[NSUserDefaults standardUserDefaults] [[[NSUserDefaults standardUserDefaults]
arrayForKey:kLastSignificantUserEvent] mutableCopy]; arrayForKey:kLastSignificantUserEvent] mutableCopy];
pastUserEvents = SanitizePastUserEvents(pastUserEvents); pastUserEvents = SanitizePastUserEvents(pastUserEvents);
return [pastUserEvents count] > 1; return [pastUserEvents count] > 1 && base::ios::IsRunningOnIOS14OrLater();
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment