Skip to content

Commit

Permalink
Add parameter to multiple Autofill pixels (#2032)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/0/1205431733474502/f
Tech Design URL:
CC:

Description:
Adding a temporary "default state" parameter to some autofill pixels ahead of Autofill becoming "on by default"
  • Loading branch information
amddg44 authored Oct 8, 2023
1 parent 495985d commit 325d8cc
Show file tree
Hide file tree
Showing 15 changed files with 167 additions and 46 deletions.
3 changes: 3 additions & 0 deletions Core/FeatureFlag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public enum FeatureFlag: String {
case autofillInlineIconCredentials
case autofillAccessCredentialManagement
case autofillPasswordGeneration
case autofillOnByDefault
case incontextSignup
case appTrackingProtection
case networkProtection
Expand All @@ -48,6 +49,8 @@ extension FeatureFlag: FeatureFlagSourceProviding {
return .remoteReleasable(.subfeature(AutofillSubfeature.accessCredentialManagement))
case .autofillPasswordGeneration:
return .remoteReleasable(.subfeature(AutofillSubfeature.autofillPasswordGeneration))
case .autofillOnByDefault:
return .remoteReleasable(.subfeature(AutofillSubfeature.onByDefault))
case .incontextSignup:
return .remoteReleasable(.feature(.incontextSignup))
}
Expand Down
2 changes: 2 additions & 0 deletions Core/Pixel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ public struct PixelParameters {
public static let returnUserErrorCode = "error_code"
public static let returnUserOldATB = "old_atb"
public static let returnUserNewATB = "new_atb"

public static let autofillDefaultState = "default_state"
}

public struct PixelValues {
Expand Down
6 changes: 6 additions & 0 deletions DuckDuckGo/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
ThemeManager.shared.updateUserInterfaceStyle(window: window)

appIsLaunching = true

// Temporary logic for rollout of Autofill as on by default for new installs only
if AppDependencyProvider.shared.appSettings.autofillIsNewInstallForOnByDefault == nil {
AppDependencyProvider.shared.appSettings.setAutofillIsNewInstallForOnByDefault()
}

return true
}

Expand Down
2 changes: 2 additions & 0 deletions DuckDuckGo/AppSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ protocol AppSettings: AnyObject {
var autofillCredentialsEnabled: Bool { get set }
var autofillCredentialsSavePromptShowAtLeastOnce: Bool { get set }
var autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary: Bool { get set }
var autofillIsNewInstallForOnByDefault: Bool? { get set }
func setAutofillIsNewInstallForOnByDefault()

var voiceSearchEnabled: Bool { get set }

Expand Down
34 changes: 28 additions & 6 deletions DuckDuckGo/AppUserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public class AppUserDefaults: AppSettings {
static let currentFireButtonAnimationKey = "com.duckduckgo.app.currentFireButtonAnimationKey"

static let autofillCredentialsEnabled = "com.duckduckgo.ios.autofillCredentialsEnabled"
static let autofillIsNewInstallForOnByDefault = "com.duckduckgo.ios.autofillIsNewInstallForOnByDefault"
}

private struct DebugKeys {
Expand All @@ -70,6 +71,8 @@ public class AppUserDefaults: AppSettings {
return UserDefaults(suiteName: groupName)
}

lazy var featureFlagger = AppDependencyProvider.shared.featureFlagger

init(groupName: String = "group.com.duckduckgo.app") {
self.groupName = groupName
}
Expand Down Expand Up @@ -182,16 +185,22 @@ public class AppUserDefaults: AppSettings {
return
}
if !autofillCredentialsSavePromptShowAtLeastOnce {
autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary = true
autofillCredentialsEnabled = true
if let isNewInstall = autofillIsNewInstallForOnByDefault,
isNewInstall,
featureFlagger.isFeatureOn(.autofillOnByDefault) {
autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary = true
autofillCredentialsEnabled = true
}
}
}

var autofillCredentialsEnabled: Bool {
get {
// In future, we'll use setAutofillCredentialsEnabledAutomaticallyIfNecessary() here to automatically turn on autofill for people
// That haven't seen the save prompt before.
// For now, whilst internal testing is still happening, it's still set to default to be enabled
// setAutofillCredentialsEnabledAutomaticallyIfNecessary() used here to automatically turn on autofill for people if:
// 1. They haven't seen the save prompt before
// 2. They are a new install
// 3. The feature flag is enabled
setAutofillCredentialsEnabledAutomaticallyIfNecessary()
return userDefaults?.object(forKey: Keys.autofillCredentialsEnabled) as? Bool ?? false
}

Expand All @@ -205,7 +214,20 @@ public class AppUserDefaults: AppSettings {

@UserDefaultsWrapper(key: .autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary, defaultValue: false)
var autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary: Bool


var autofillIsNewInstallForOnByDefault: Bool? {
get {
return userDefaults?.object(forKey: Keys.autofillIsNewInstallForOnByDefault) as? Bool
}
set {
userDefaults?.set(newValue, forKey: Keys.autofillIsNewInstallForOnByDefault)
}
}

func setAutofillIsNewInstallForOnByDefault() {
autofillIsNewInstallForOnByDefault = StatisticsUserDefaults().installDate == nil
}

@UserDefaultsWrapper(key: .voiceSearchEnabled, defaultValue: false)
var voiceSearchEnabled: Bool

Expand Down
24 changes: 16 additions & 8 deletions DuckDuckGo/AutofillLoginPromptViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,11 @@ class AutofillLoginPromptViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if trigger == AutofillUserScript.GetTriggerType.autoprompt {
Pixel.fire(pixel: .autofillLoginsFillLoginInlineAutopromptDisplayed)
Pixel.fire(pixel: .autofillLoginsFillLoginInlineAutopromptDisplayed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
} else {
Pixel.fire(pixel: .autofillLoginsFillLoginInlineManualDisplayed)
Pixel.fire(pixel: .autofillLoginsFillLoginInlineManualDisplayed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
}
}

Expand All @@ -95,9 +97,11 @@ class AutofillLoginPromptViewController: UIViewController {
extension AutofillLoginPromptViewController: UISheetPresentationControllerDelegate {
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
if self.trigger == AutofillUserScript.GetTriggerType.autoprompt {
Pixel.fire(pixel: .autofillLoginsAutopromptDismissed)
Pixel.fire(pixel: .autofillLoginsAutopromptDismissed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
} else {
Pixel.fire(pixel: .autofillLoginsFillLoginInlineManualDismissed)
Pixel.fire(pixel: .autofillLoginsFillLoginInlineManualDismissed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
}
completion?(nil, false)
}
Expand All @@ -107,9 +111,11 @@ extension AutofillLoginPromptViewController: AutofillLoginPromptViewModelDelegat
func autofillLoginPromptViewModel(_ viewModel: AutofillLoginPromptViewModel, didSelectAccount account: SecureVaultModels.WebsiteAccount) {

if trigger == AutofillUserScript.GetTriggerType.autoprompt {
Pixel.fire(pixel: .autofillLoginsFillLoginInlineAutopromptConfirmed)
Pixel.fire(pixel: .autofillLoginsFillLoginInlineAutopromptConfirmed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
} else {
Pixel.fire(pixel: .autofillLoginsFillLoginInlineManualConfirmed)
Pixel.fire(pixel: .autofillLoginsFillLoginInlineManualConfirmed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
}

if AppDependencyProvider.shared.autofillLoginSession.isValidSession {
Expand Down Expand Up @@ -163,9 +169,11 @@ extension AutofillLoginPromptViewController: AutofillLoginPromptViewModelDelegat
func autofillLoginPromptViewModelDidCancel(_ viewModel: AutofillLoginPromptViewModel) {
dismiss(animated: true) {
if self.trigger == AutofillUserScript.GetTriggerType.autoprompt {
Pixel.fire(pixel: .autofillLoginsAutopromptDismissed)
Pixel.fire(pixel: .autofillLoginsAutopromptDismissed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
} else {
Pixel.fire(pixel: .autofillLoginsFillLoginInlineManualDismissed)
Pixel.fire(pixel: .autofillLoginsFillLoginInlineManualDismissed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
}

self.completion?(nil, false)
Expand Down
6 changes: 4 additions & 2 deletions DuckDuckGo/AutofillLoginSettingsListViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -658,9 +658,11 @@ extension AutofillLoginSettingsListViewController: AutofillLoginDetailsViewContr
extension AutofillLoginSettingsListViewController: EnableAutofillSettingsTableViewCellDelegate {
func enableAutofillSettingsTableViewCell(_ cell: EnableAutofillSettingsTableViewCell, didChangeSettings value: Bool) {
if value {
Pixel.fire(pixel: .autofillLoginsSettingsEnabled)
Pixel.fire(pixel: .autofillLoginsSettingsEnabled,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
} else {
Pixel.fire(pixel: .autofillLoginsSettingsDisabled)
Pixel.fire(pixel: .autofillLoginsSettingsDisabled,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
}

viewModel.isAutofillEnabledInSettings = value
Expand Down
4 changes: 4 additions & 0 deletions DuckDuckGo/AutofillSettingStatus.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@ struct AutofillSettingStatus {
let canAuthenticate = context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error)
return appSettings.autofillCredentialsEnabled && canAuthenticate
}

static var defaultState: String {
return appSettings.autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary ? "on" : "off"
}
}
12 changes: 8 additions & 4 deletions DuckDuckGo/PasswordGenerationPromptViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ class PasswordGenerationPromptViewController: UIViewController {

setupPasswordGenerationPromptView()

Pixel.fire(pixel: .autofillLoginsPasswordGenerationPromptDisplayed)
Pixel.fire(pixel: .autofillLoginsPasswordGenerationPromptDisplayed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
}

private func setupPasswordGenerationPromptView() {
Expand All @@ -67,23 +68,26 @@ class PasswordGenerationPromptViewController: UIViewController {

extension PasswordGenerationPromptViewController: UISheetPresentationControllerDelegate {
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
Pixel.fire(pixel: .autofillLoginsPasswordGenerationPromptDismissed)
Pixel.fire(pixel: .autofillLoginsPasswordGenerationPromptDismissed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])

self.completion?(false)
}
}

extension PasswordGenerationPromptViewController: PasswordGenerationPromptViewModelDelegate {
func passwordGenerationPromptViewModelDidSelect(_ viewModel: PasswordGenerationPromptViewModel) {
Pixel.fire(pixel: .autofillLoginsPasswordGenerationPromptConfirmed)
Pixel.fire(pixel: .autofillLoginsPasswordGenerationPromptConfirmed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])

dismiss(animated: true) {
self.completion?(true)
}
}

func passwordGenerationPromptViewModelDidCancel(_ viewModel: PasswordGenerationPromptViewModel) {
Pixel.fire(pixel: .autofillLoginsPasswordGenerationPromptDismissed)
Pixel.fire(pixel: .autofillLoginsPasswordGenerationPromptDismissed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])

dismiss(animated: true) {
self.completion?(false)
Expand Down
35 changes: 20 additions & 15 deletions DuckDuckGo/SaveLoginViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ class SaveLoginViewController: UIViewController {
return
}
switch viewModel.layoutType {
case .newUser:
Pixel.fire(pixel: .autofillLoginsSaveLoginModalDismissed)
case .saveLogin:
Pixel.fire(pixel: .autofillLoginsSaveLoginModalDismissed)
case .newUser, .saveLogin:
Pixel.fire(pixel: .autofillLoginsSaveLoginModalDismissed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
case .savePassword:
Pixel.fire(pixel: .autofillLoginsSavePasswordModalDismissed)
Pixel.fire(pixel: .autofillLoginsSavePasswordModalDismissed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
case .updateUsername:
Pixel.fire(pixel: .autofillLoginsUpdateUsernameModalDismissed)
case .updatePassword:
Expand All @@ -96,12 +96,12 @@ class SaveLoginViewController: UIViewController {
installChildViewController(controller)

switch saveViewModel.layoutType {
case .newUser:
Pixel.fire(pixel: .autofillLoginsSaveLoginModalDisplayed)
case .saveLogin:
Pixel.fire(pixel: .autofillLoginsSaveLoginModalDisplayed)
case .newUser, .saveLogin:
Pixel.fire(pixel: .autofillLoginsSaveLoginModalDisplayed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
case .savePassword:
Pixel.fire(pixel: .autofillLoginsSavePasswordModalDisplayed)
Pixel.fire(pixel: .autofillLoginsSavePasswordModalDisplayed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
case .updateUsername:
Pixel.fire(pixel: .autofillLoginsUpdateUsernameModalDisplayed)
case .updatePassword:
Expand All @@ -115,9 +115,11 @@ extension SaveLoginViewController: SaveLoginViewModelDelegate {
switch viewModel.layoutType {
case .saveLogin, .savePassword, .newUser:
if viewModel.layoutType == .savePassword {
Pixel.fire(pixel: .autofillLoginsSavePasswordModalConfirmed)
Pixel.fire(pixel: .autofillLoginsSavePasswordModalConfirmed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
} else {
Pixel.fire(pixel: .autofillLoginsSaveLoginModalConfirmed)
Pixel.fire(pixel: .autofillLoginsSaveLoginModalConfirmed,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
}
delegate?.saveLoginViewController(self, didSaveCredentials: credentialManager.credentials)
case .updatePassword, .updateUsername:
Expand All @@ -144,15 +146,17 @@ extension SaveLoginViewController: SaveLoginViewModelDelegate {
alertController.overrideUserInterfaceStyle()

let disableAction = UIAlertAction(title: UserText.autofillKeepEnabledAlertDisableAction, style: .cancel) { _ in
Pixel.fire(pixel: .autofillLoginsFillLoginInlineDisablePromptAutofillDisabled)
Pixel.fire(pixel: .autofillLoginsFillLoginInlineDisablePromptAutofillDisabled,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
if isSelfPresentingAlert {
self.delegate?.saveLoginViewControllerDidCancel(self)
}
AppDependencyProvider.shared.appSettings.autofillCredentialsEnabled = false
}

let keepUsingAction = UIAlertAction(title: UserText.autofillKeepEnabledAlertKeepUsingAction, style: .default) { _ in
Pixel.fire(pixel: .autofillLoginsFillLoginInlineDisablePromptAutofillKept)
Pixel.fire(pixel: .autofillLoginsFillLoginInlineDisablePromptAutofillKept,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
if isSelfPresentingAlert {
self.delegate?.saveLoginViewControllerDidCancel(self)
}
Expand All @@ -166,7 +170,8 @@ extension SaveLoginViewController: SaveLoginViewModelDelegate {
if isAlreadyDismissed {
delegate?.saveLoginViewController(self, didRequestPresentConfirmKeepUsingAlertController: alertController)
} else {
Pixel.fire(pixel: .autofillLoginsFillLoginInlineDisablePromptShown)
Pixel.fire(pixel: .autofillLoginsFillLoginInlineDisablePromptShown,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
present(alertController, animated: true)
}
}
Expand Down
3 changes: 2 additions & 1 deletion DuckDuckGo/SettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,8 @@ class SettingsViewController: UITableViewController {
syncDataProviders: syncDataProviders
)
autofillController.delegate = self
Pixel.fire(pixel: .autofillSettingsOpened)
Pixel.fire(pixel: .autofillSettingsOpened,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
navigationController?.pushViewController(autofillController, animated: animated)
}

Expand Down
3 changes: 2 additions & 1 deletion DuckDuckGo/TabViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2496,7 +2496,8 @@ extension TabViewController: SaveLoginViewControllerDelegate {

func saveLoginViewController(_ viewController: SaveLoginViewController,
didRequestPresentConfirmKeepUsingAlertController alertController: UIAlertController) {
Pixel.fire(pixel: .autofillLoginsFillLoginInlineDisablePromptShown)
Pixel.fire(pixel: .autofillLoginsFillLoginInlineDisablePromptShown,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
present(alertController, animated: true)
}
}
Expand Down
3 changes: 2 additions & 1 deletion DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,8 @@ extension TabViewController {
}

private func onOpenAutofillLoginsAction() {
Pixel.fire(pixel: .browsingMenuAutofill)
Pixel.fire(pixel: .browsingMenuAutofill,
withAdditionalParameters: [PixelParameters.autofillDefaultState: AutofillSettingStatus.defaultState])
delegate?.tabDidRequestAutofillLogins(tab: self)
}

Expand Down
4 changes: 4 additions & 0 deletions DuckDuckGoTests/AppSettingsMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ class AppSettingsMock: AppSettings {

var autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary: Bool = false

var autofillIsNewInstallForOnByDefault: Bool?

func setAutofillIsNewInstallForOnByDefault() { }

var autocomplete: Bool = true

var currentThemeName: DuckDuckGo.ThemeName = .systemDefault
Expand Down
Loading

0 comments on commit 325d8cc

Please sign in to comment.