diff --git a/BrowserKit/Sources/Shared/AppConstants.swift b/BrowserKit/Sources/Shared/AppConstants.swift index 74e7b16fe8f6..3ce2fc12f32d 100644 --- a/BrowserKit/Sources/Shared/AppConstants.swift +++ b/BrowserKit/Sources/Shared/AppConstants.swift @@ -81,7 +81,6 @@ public class AppConstants { }() public static let prefSendUsageData = "settings.sendUsageData" - public static let prefSendTechnicalData = "settings.sendTechnicalData" public static let prefSendCrashReports = "settings.sendCrashReports" public static let prefSendDailyUsagePing = "settings.sendDailyUsagePing" public static let prefStudiesToggle = "settings.studiesToggle" diff --git a/firefox-ios/Client/Application/AppLaunchUtil.swift b/firefox-ios/Client/Application/AppLaunchUtil.swift index 5df9b7a04b33..2ed0bc7257b6 100644 --- a/firefox-ios/Client/Application/AppLaunchUtil.swift +++ b/firefox-ios/Client/Application/AppLaunchUtil.swift @@ -34,15 +34,29 @@ class AppLaunchUtil { DefaultBrowserUtil().processUserDefaultState(isFirstRun: introScreenManager.shouldShowIntroScreen) - TelemetryWrapper.shared.setup(profile: profile) - recordStartUpTelemetry() + // Initialize the feature flag subsystem. + // Among other things, it toggles on and off Nimbus, Contile, Adjust. + // i.e. this must be run before initializing those systems. + LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: profile) // Need to get "settings.sendCrashReports" this way so that Sentry can be initialized before getting the Profile. let sendCrashReports = NSUserDefaultsPrefs(prefix: "profile").boolForKey(AppConstants.prefSendCrashReports) ?? true - // For this phase, we should handle terms of service as accepted, in case of app updates. - logger.setup(sendCrashReports: sendCrashReports && (termsOfServiceManager.isAccepted || - !introScreenManager.shouldShowIntroScreen)) + if termsOfServiceManager.isFeatureEnabled { + // Two cases: + // 1. when ToS screen has been presented and user accepted it + // 2. or when ToS screen is not presented because is not fresh install + let isTermsOfServiceAccepted = termsOfServiceManager.isAccepted || !introScreenManager.shouldShowIntroScreen + logger.setup(sendCrashReports: sendCrashReports && isTermsOfServiceAccepted) + if isTermsOfServiceAccepted { + TelemetryWrapper.shared.setup(profile: profile) + TelemetryWrapper.shared.recordStartUpTelemetry() + } + } else { + logger.setup(sendCrashReports: sendCrashReports) + TelemetryWrapper.shared.setup(profile: profile) + TelemetryWrapper.shared.recordStartUpTelemetry() + } setUserAgent() @@ -56,18 +70,11 @@ class AppLaunchUtil { let conversionValue = ConversionValueUtil(fineValue: 0, coarseValue: .low, logger: logger) conversionValue.adNetworkAttributionUpdateConversionEvent() - // Initialize the feature flag subsystem. - // Among other things, it toggles on and off Nimbus, Contile, Adjust. - // i.e. this must be run before initializing those systems. - LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: profile) - // Used by share extension to determine if the bookmarks refactor feature flag is enabled profile.prefs.setBool(LegacyFeatureFlagsManager.shared.isFeatureEnabled(.bookmarksRefactor, checking: .buildOnly), forKey: PrefsKeys.IsBookmarksRefactorEnabled) - logger.setup(sendCrashReports: sendCrashReports && !termsOfServiceManager.isFeatureEnabled) - // Start initializing the Nimbus SDK. This should be done after Glean // has been started. initializeExperiments() @@ -214,22 +221,6 @@ class AppLaunchUtil { } } - private func recordStartUpTelemetry() { - let isEnabled: Bool = (profile.prefs.boolForKey(PrefsKeys.UserFeatureFlagPrefs.SponsoredShortcuts) ?? true) && - (profile.prefs.boolForKey(PrefsKeys.UserFeatureFlagPrefs.TopSiteSection) ?? true) - TelemetryWrapper.recordEvent(category: .information, - method: .view, - object: .sponsoredShortcuts, - extras: [TelemetryWrapper.EventExtraKey.preference.rawValue: isEnabled]) - - if logger.crashedLastLaunch { - TelemetryWrapper.recordEvent(category: .information, - method: .error, - object: .app, - value: .crashedLastLaunch) - } - } - private func setMenuItems() { let webViewModel = MenuHelperWebViewModel(searchTitle: .MenuHelperSearchWithFirefox, findInPageTitle: .MenuHelperFindInPage) diff --git a/firefox-ios/Client/Coordinators/Launch/LaunchCoordinator.swift b/firefox-ios/Client/Coordinators/Launch/LaunchCoordinator.swift index 63b468b37efe..b95f2f252610 100644 --- a/firefox-ios/Client/Coordinators/Launch/LaunchCoordinator.swift +++ b/firefox-ios/Client/Coordinators/Launch/LaunchCoordinator.swift @@ -52,11 +52,20 @@ class LaunchCoordinator: BaseCoordinator, isFullScreen: Bool) { let viewController = TermsOfServiceViewController(profile: profile, windowUUID: windowUUID) viewController.didFinishFlow = { [weak self] in - manager.setAccepted() guard let self = self else { return } + manager.setAccepted() + + let sendTechnicalData = profile.prefs.boolForKey(AppConstants.prefSendUsageData) ?? true + manager.shouldSendTechnicalData(value: sendTechnicalData) + self.profile.prefs.setBool(sendTechnicalData, forKey: AppConstants.prefSendUsageData) + let sendCrashReports = profile.prefs.boolForKey(AppConstants.prefSendCrashReports) ?? true self.profile.prefs.setBool(sendCrashReports, forKey: AppConstants.prefSendCrashReports) self.logger.setup(sendCrashReports: sendCrashReports) + + TelemetryWrapper.shared.setup(profile: profile) + TelemetryWrapper.shared.recordStartUpTelemetry() + self.parentCoordinator?.didFinishTermsOfService(from: self) } viewController.modalPresentationStyle = .fullScreen diff --git a/firefox-ios/Client/Frontend/Onboarding/Views/PrivacyPreferencesViewController.swift b/firefox-ios/Client/Frontend/Onboarding/Views/PrivacyPreferencesViewController.swift index 8e61ccddd120..83a6b9742e93 100644 --- a/firefox-ios/Client/Frontend/Onboarding/Views/PrivacyPreferencesViewController.swift +++ b/firefox-ios/Client/Frontend/Onboarding/Views/PrivacyPreferencesViewController.swift @@ -51,7 +51,9 @@ final class PrivacyPreferencesViewController: UIViewController, view.setSwitchValue(isOn: self?.profile.prefs.boolForKey(AppConstants.prefSendCrashReports) ?? true) } - private lazy var technicalDataSwitch: SwitchDetailedView = .build() + private lazy var technicalDataSwitch: SwitchDetailedView = .build { [weak self] view in + view.setSwitchValue(isOn: self?.profile.prefs.boolForKey(AppConstants.prefSendUsageData) ?? true) + } // MARK: - Initializers init( @@ -163,8 +165,9 @@ final class PrivacyPreferencesViewController: UIViewController, self?.profile.prefs.setBool(value, forKey: AppConstants.prefSendCrashReports) } - // TODO: FXIOS-10675 Firefox iOS: Manage Technical Data during Onboarding and Settings - technicalDataSwitch.switchCallback = { _ in } + technicalDataSwitch.switchCallback = { [weak self] value in + self?.profile.prefs.setBool(value, forKey: AppConstants.prefSendUsageData) + } crashReportsSwitch.learnMoreCallBack = { [weak self] in self?.presentLink(with: SupportUtils.URLForTopic("mobile-crash-reports")) diff --git a/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift b/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift index faa333cf3f8b..db47ce5a7b71 100644 --- a/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift +++ b/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift @@ -178,14 +178,6 @@ class AppSettingsTableViewController: SettingsTableViewController, private func setupDataSettings() { let isTermsOfServiceFeatureEnabled = featureFlags.isFeatureEnabled(.tosFeature, checking: .buildOnly) - let anonymousUsageDataSetting = SendDataSetting( - prefs: profile.prefs, - delegate: settingsDelegate, - theme: themeManager.getCurrentTheme(for: windowUUID), - settingsDelegate: parentCoordinator, - sendDataType: .usageData - ) - let studiesSetting = StudiesToggleSetting( prefs: profile.prefs, delegate: settingsDelegate, @@ -193,10 +185,6 @@ class AppSettingsTableViewController: SettingsTableViewController, settingsDelegate: parentCoordinator ) - anonymousUsageDataSetting.shouldSendData = { value in - studiesSetting.updateSetting(for: value) - } - // Only add these toggles to the Settings if Terms Of Service feature flag is enabled if isTermsOfServiceFeatureEnabled { let sendTechnicalDataSettings = SendDataSetting( @@ -204,10 +192,20 @@ class AppSettingsTableViewController: SettingsTableViewController, delegate: settingsDelegate, theme: themeManager.getCurrentTheme(for: windowUUID), settingsDelegate: parentCoordinator, - sendDataType: .technicalData + title: .SendTechnicalDataSettingTitle, + message: String(format: .SendTechnicalDataSettingMessage, + MozillaName.shortName.rawValue, + AppName.shortName.rawValue), + linkedText: .SendTechnicalDataSettingLink, + prefKey: AppConstants.prefSendUsageData, + a11yId: AccessibilityIdentifiers.Settings.SendData.sendTechnicalDataTitle, + learnMoreURL: SupportUtils.URLForTopic("mobile-technical-and-interaction-data") ) - sendTechnicalDataSettings.shouldSendData = { value in - // TODO: FXIOS-10754 Firefox iOS: Manage Privacy Preferences in Settings - Logic + + sendTechnicalDataSettings.shouldSendData = { [weak self] value in + guard let self else { return } + TermsOfServiceManager(prefs: self.profile.prefs).shouldSendTechnicalData(value: value) + studiesSetting.updateSetting(for: value) } sendTechnicalDataSetting = sendTechnicalDataSettings @@ -216,12 +214,39 @@ class AppSettingsTableViewController: SettingsTableViewController, delegate: settingsDelegate, theme: themeManager.getCurrentTheme(for: windowUUID), settingsDelegate: parentCoordinator, - sendDataType: .dailyUsagePing + title: .SendDailyUsagePingSettingTitle, + message: String(format: .SendDailyUsagePingSettingMessage, MozillaName.shortName.rawValue), + linkedText: .SendDailyUsagePingSettingLink, + prefKey: AppConstants.prefSendDailyUsagePing, + a11yId: AccessibilityIdentifiers.Settings.SendData.sendDailyUsagePingTitle, + learnMoreURL: SupportUtils.URLForTopic("usage-ping-settings-mobile") ) sendDailyUsagePingSettings.shouldSendData = { value in - // TODO: FXIOS-10754 Firefox iOS: Manage Privacy Preferences in Settings - Logic + // TODO: FXIOS-10469 Firefox iOS: DAU Ping Setting } sendDailyUsagePingSetting = sendDailyUsagePingSettings + } else { + let sendAnonymousUsageDataSettings = SendDataSetting( + prefs: profile.prefs, + delegate: settingsDelegate, + theme: themeManager.getCurrentTheme(for: windowUUID), + settingsDelegate: parentCoordinator, + title: .SendUsageSettingTitle, + message: String(format: .SendUsageSettingMessage, + MozillaName.shortName.rawValue, + AppName.shortName.rawValue), + linkedText: .SendUsageSettingLink, + prefKey: AppConstants.prefSendUsageData, + a11yId: AccessibilityIdentifiers.Settings.SendData.sendAnonymousUsageDataTitle, + learnMoreURL: SupportUtils.URLForTopic("adjust") + ) + + sendAnonymousUsageDataSettings.shouldSendData = { [weak self] value in + guard let self else { return } + TermsOfServiceManager(prefs: self.profile.prefs).shouldSendTechnicalData(value: value) + studiesSetting.updateSetting(for: value) + } + sendAnonymousUsageDataSetting = sendAnonymousUsageDataSettings } let sendCrashReportsSettings = SendDataSetting( @@ -229,11 +254,15 @@ class AppSettingsTableViewController: SettingsTableViewController, delegate: settingsDelegate, theme: themeManager.getCurrentTheme(for: windowUUID), settingsDelegate: parentCoordinator, - sendDataType: .crashReports + title: .SendCrashReportsSettingTitle, + message: String(format: .SendCrashReportsSettingMessage, MozillaName.shortName.rawValue), + linkedText: .SendCrashReportsSettingLink, + prefKey: AppConstants.prefSendCrashReports, + a11yId: AccessibilityIdentifiers.Settings.SendData.sendCrashReportsTitle, + learnMoreURL: SupportUtils.URLForTopic("mobile-crash-reports") ) self.sendCrashReportsSetting = sendCrashReportsSettings - sendAnonymousUsageDataSetting = anonymousUsageDataSetting studiesToggleSetting = studiesSetting } @@ -399,7 +428,7 @@ class AppSettingsTableViewController: SettingsTableViewController, } private func getSupportSettings() -> [SettingSection] { - guard let sendAnonymousUsageDataSetting, let studiesToggleSetting else { return [] } + guard let studiesToggleSetting else { return [] } var supportSettings = [ ShowIntroductionSetting(settings: self, settingsDelegate: self), @@ -418,10 +447,10 @@ class AppSettingsTableViewController: SettingsTableViewController, ) } - supportSettings.append(sendAnonymousUsageDataSetting) - if let sendTechnicalDataSetting { supportSettings.append(sendTechnicalDataSetting) + } else if let sendAnonymousUsageDataSetting { + supportSettings.append(sendAnonymousUsageDataSetting) } if let sendCrashReportsSetting { diff --git a/firefox-ios/Client/Frontend/Settings/Main/Support/SendDataSetting.swift b/firefox-ios/Client/Frontend/Settings/Main/Support/SendDataSetting.swift index 8cefc6db3477..88579995e60f 100644 --- a/firefox-ios/Client/Frontend/Settings/Main/Support/SendDataSetting.swift +++ b/firefox-ios/Client/Frontend/Settings/Main/Support/SendDataSetting.swift @@ -7,16 +7,10 @@ import Foundation import Glean import Shared -enum SendDataType { - case usageData - case technicalData - case crashReports - case dailyUsagePing -} - class SendDataSetting: BoolSetting { private weak var settingsDelegate: SupportSettingsDelegate? - private let sendDataType: SendDataType + private var a11yId: String + private var learnMoreURL: URL? var shouldSendData: ((Bool) -> Void)? @@ -24,37 +18,12 @@ class SendDataSetting: BoolSetting { delegate: SettingsDelegate?, theme: Theme, settingsDelegate: SupportSettingsDelegate?, - sendDataType: SendDataType) { - var title: String - var message: String - var linkedText: String - var prefKey: String - - switch sendDataType { - case .usageData: - title = .SendUsageSettingTitle - message = .SendUsageSettingMessage - linkedText = .SendUsageSettingLink - prefKey = AppConstants.prefSendUsageData - case .technicalData: - title = .SendTechnicalDataSettingTitle - message = String(format: .SendTechnicalDataSettingMessage, - MozillaName.shortName.rawValue, - AppName.shortName.rawValue) - linkedText = .SendTechnicalDataSettingLink - prefKey = AppConstants.prefSendTechnicalData - case .crashReports: - title = .SendCrashReportsSettingTitle - message = .SendCrashReportsSettingMessage - linkedText = .SendCrashReportsSettingLink - prefKey = AppConstants.prefSendCrashReports - case .dailyUsagePing: - title = .SendDailyUsagePingSettingTitle - message = String(format: .SendDailyUsagePingSettingMessage, MozillaName.shortName.rawValue) - linkedText = .SendDailyUsagePingSettingLink - prefKey = AppConstants.prefSendDailyUsagePing - } - + title: String, + message: String, + linkedText: String, + prefKey: String, + a11yId: String, + learnMoreURL: URL?) { let statusText = NSMutableAttributedString() statusText.append( NSAttributedString( @@ -74,7 +43,8 @@ class SendDataSetting: BoolSetting { ) ) - self.sendDataType = sendDataType + self.a11yId = a11yId + self.learnMoreURL = learnMoreURL self.settingsDelegate = settingsDelegate super.init( prefs: prefs, @@ -84,58 +54,25 @@ class SendDataSetting: BoolSetting { attributedStatusText: statusText ) - setupSettingDidChange(for: sendDataType) + setupSettingDidChange() // We make sure to set this on initialization, in case the setting is turned off // in which case, we would to make sure that users are opted out of experiments Experiments.setTelemetrySetting(prefs.boolForKey(prefKey) ?? true) } - private func setupSettingDidChange(for sendDataType: SendDataType) { + private func setupSettingDidChange() { self.settingDidChange = { [weak self] value in - if sendDataType != .crashReports { - // AdjustHelper.setEnabled($0) - DefaultGleanWrapper.shared.setUpload(isEnabled: value) - - if !value { - self?.prefs?.removeObjectForKey(PrefsKeys.Usage.profileId) - - // set dummy uuid to make sure the previous one is deleted - if let uuid = UUID(uuidString: "beefbeef-beef-beef-beef-beeefbeefbee") { - GleanMetrics.Usage.profileId.set(uuid) - } - } - - Experiments.setTelemetrySetting(value) - self?.shouldSendData?(value) - } + self?.shouldSendData?(value) } } override var accessibilityIdentifier: String? { - switch sendDataType { - case .usageData: - return AccessibilityIdentifiers.Settings.SendData.sendAnonymousUsageDataTitle - case .technicalData: - return AccessibilityIdentifiers.Settings.SendData.sendTechnicalDataTitle - case .crashReports: - return AccessibilityIdentifiers.Settings.SendData.sendCrashReportsTitle - case .dailyUsagePing: - return AccessibilityIdentifiers.Settings.SendData.sendDailyUsagePingTitle - } + return a11yId } override var url: URL? { - switch sendDataType { - case .usageData: - return SupportUtils.URLForTopic("adjust") - case .technicalData: - return SupportUtils.URLForTopic("mobile-technical-and-interaction-data") - case .crashReports: - return SupportUtils.URLForTopic("mobile-crash-reports") - case .dailyUsagePing: - return SupportUtils.URLForTopic("usage-ping-settings-mobile") - } + return learnMoreURL } override func onClick(_ navigationController: UINavigationController?) { diff --git a/firefox-ios/Client/Telemetry/TelemetryWrapper.swift b/firefox-ios/Client/Telemetry/TelemetryWrapper.swift index 521fae236a54..9abf90ac41ef 100644 --- a/firefox-ios/Client/Telemetry/TelemetryWrapper.swift +++ b/firefox-ios/Client/Telemetry/TelemetryWrapper.swift @@ -155,6 +155,22 @@ class TelemetryWrapper: TelemetryWrapperProtocol, FeatureFlaggable { ) } + func recordStartUpTelemetry() { + let isEnabled: Bool = (profile?.prefs.boolForKey(PrefsKeys.UserFeatureFlagPrefs.SponsoredShortcuts) ?? true) && + (profile?.prefs.boolForKey(PrefsKeys.UserFeatureFlagPrefs.TopSiteSection) ?? true) + recordEvent(category: .information, + method: .view, + object: .sponsoredShortcuts, + extras: [EventExtraKey.preference.rawValue: isEnabled]) + + if logger.crashedLastLaunch { + recordEvent(category: .information, + method: .error, + object: .app, + value: .crashedLastLaunch) + } + } + @objc func recordFinishedLaunchingPreferenceMetrics(notification: NSNotification) { guard let profile = self.profile else { return } diff --git a/firefox-ios/Client/TermsOfServiceManager.swift b/firefox-ios/Client/TermsOfServiceManager.swift index f2c86855cf55..a12e705a7baf 100644 --- a/firefox-ios/Client/TermsOfServiceManager.swift +++ b/firefox-ios/Client/TermsOfServiceManager.swift @@ -4,6 +4,7 @@ import Foundation import Shared +import Glean struct TermsOfServiceManager: FeatureFlaggable { var prefs: Prefs @@ -25,4 +26,20 @@ struct TermsOfServiceManager: FeatureFlaggable { func setAccepted() { prefs.setInt(1, forKey: PrefsKeys.TermsOfServiceAccepted) } + + func shouldSendTechnicalData(value: Bool) { + // AdjustHelper.setEnabled($0) + DefaultGleanWrapper.shared.setUpload(isEnabled: value) + + if !value { + prefs.removeObjectForKey(PrefsKeys.Usage.profileId) + + // set dummy uuid to make sure the previous one is deleted + if let uuid = UUID(uuidString: "beefbeef-beef-beef-beef-beeefbeefbee") { + GleanMetrics.Usage.profileId.set(uuid) + } + } + + Experiments.setTelemetrySetting(value) + } }