diff --git a/.maestro/release_tests/bookmarks.yaml b/.maestro/release_tests/bookmarks.yaml index 4e0822e12d..d77e81e46f 100644 --- a/.maestro/release_tests/bookmarks.yaml +++ b/.maestro/release_tests/bookmarks.yaml @@ -20,7 +20,7 @@ tags: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://privacy-test-pages.glitch.me" +- inputText: "https://privacy-test-pages.site" - pressKey: Enter # Manage onboarding diff --git a/.maestro/release_tests/browsing.yaml b/.maestro/release_tests/browsing.yaml index 3be02ab923..f191138307 100644 --- a/.maestro/release_tests/browsing.yaml +++ b/.maestro/release_tests/browsing.yaml @@ -20,7 +20,7 @@ tags: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://privacy-test-pages.glitch.me" +- inputText: "https://privacy-test-pages.site" - pressKey: Enter - tapOn: optional: true diff --git a/.maestro/release_tests/favorites.yaml b/.maestro/release_tests/favorites.yaml index c3d8ae1b28..cdaa68fd46 100644 --- a/.maestro/release_tests/favorites.yaml +++ b/.maestro/release_tests/favorites.yaml @@ -20,7 +20,7 @@ tags: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://privacy-test-pages.glitch.me" +- inputText: "https://privacy-test-pages.site" - pressKey: Enter # Manage onboarding diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index b4c9364a70..44082cf630 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -183,7 +183,7 @@ { "identity" : "trackerradarkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit.git", + "location" : "https://github.com/duckduckgo/TrackerRadarKit", "state" : { "revision" : "a6b7ba151d9dc6684484f3785293875ec01cc1ff", "version" : "1.2.2" diff --git a/DuckDuckGo/Feedback/VPNMetadataCollector.swift b/DuckDuckGo/Feedback/VPNMetadataCollector.swift index a4103cdd00..b858cf4672 100644 --- a/DuckDuckGo/Feedback/VPNMetadataCollector.swift +++ b/DuckDuckGo/Feedback/VPNMetadataCollector.swift @@ -24,6 +24,7 @@ import Common import NetworkProtection import NetworkExtension import Network +import Subscription struct VPNMetadata: Encodable { @@ -61,11 +62,27 @@ struct VPNMetadata: Encodable { let selectedServer: String } + struct PrivacyProInfo: Encodable { + // swiftlint:disable nesting + enum Source: String, Encodable { + case `internal` + case waitlist + case other + } + // swiftlint:enable nesting + + let enableSource: Source + let betaParticipant: Bool + let hasToken: Bool + let subscriptionActive: Bool + } + let appInfo: AppInfo let deviceInfo: DeviceInfo let networkInfo: NetworkInfo let vpnState: VPNState let vpnSettingsState: VPNSettingsState + let privacyProInfo: PrivacyProInfo func toPrettyPrintedJSON() -> String? { let encoder = JSONEncoder() @@ -99,15 +116,21 @@ protocol VPNMetadataCollector { final class DefaultVPNMetadataCollector: VPNMetadataCollector { private let statusObserver: ConnectionStatusObserver private let serverInfoObserver: ConnectionServerInfoObserver + private let accessManager: NetworkProtectionAccessController + private let tokenStore: NetworkProtectionTokenStore private let settings: VPNSettings private let defaults: UserDefaults init(statusObserver: ConnectionStatusObserver = ConnectionStatusObserverThroughSession(), serverInfoObserver: ConnectionServerInfoObserver = ConnectionServerInfoObserverThroughSession(), + networkProtectionAccessManager: NetworkProtectionAccessController = NetworkProtectionAccessController(), + tokenStore: NetworkProtectionTokenStore = NetworkProtectionKeychainTokenStore(), settings: VPNSettings = .init(defaults: .networkProtectionGroupDefaults), defaults: UserDefaults = .networkProtectionGroupDefaults) { self.statusObserver = statusObserver self.serverInfoObserver = serverInfoObserver + self.accessManager = networkProtectionAccessManager + self.tokenStore = tokenStore self.settings = settings self.defaults = defaults } @@ -118,13 +141,15 @@ final class DefaultVPNMetadataCollector: VPNMetadataCollector { let networkInfoMetadata = await collectNetworkInformation() let vpnState = await collectVPNState() let vpnSettingsState = collectVPNSettingsState() + let privacyProInfo = collectPrivacyProInfo() return VPNMetadata( appInfo: appInfoMetadata, deviceInfo: deviceInfoMetadata, networkInfo: networkInfoMetadata, vpnState: vpnState, - vpnSettingsState: vpnSettingsState + vpnSettingsState: vpnSettingsState, + privacyProInfo: privacyProInfo ) } @@ -242,7 +267,7 @@ final class DefaultVPNMetadataCollector: VPNMetadataCollector { } func collectVPNSettingsState() -> VPNMetadata.VPNSettingsState { - return .init( + .init( connectOnLoginEnabled: settings.connectOnLogin, includeAllNetworksEnabled: settings.includeAllNetworks, enforceRoutesEnabled: settings.enforceRoutes, @@ -251,25 +276,35 @@ final class DefaultVPNMetadataCollector: VPNMetadataCollector { selectedServer: settings.selectedServer.stringValue ?? "automatic" ) } -} - -extension Network.NWPath { - /// A description that's safe from a privacy standpoint. - /// - /// Ref: https://app.asana.com/0/0/1206712493935053/1206712516729780/f - /// - var anonymousDescription: String { - var description = "NWPath(" - description += "status: \(status), " - - if #available(iOS 14.2, *), case .unsatisfied = status { - description += "unsatisfiedReason: \(unsatisfiedReason), " + func collectPrivacyProInfo() -> VPNMetadata.PrivacyProInfo { + let accessType = accessManager.networkProtectionAccessType() + var hasToken: Bool { + guard let token = try? tokenStore.fetchToken(), + !token.hasPrefix(NetworkProtectionKeychainTokenStore.authTokenPrefix) else { + return false + } + return true } - description += "availableInterfaces: \(availableInterfaces)" - description += ")" + return .init( + enableSource: .init(from: accessManager.networkProtectionAccessType()), + betaParticipant: accessType == .waitlistJoined, + hasToken: hasToken, + subscriptionActive: AccountManager(subscriptionAppGroup: Bundle.main.appGroup(bundle: .subs)).accessToken != nil + ) + } +} - return description +extension VPNMetadata.PrivacyProInfo.Source { + init(from accessType: NetworkProtectionAccessType) { + switch accessType { + case .inviteCodeInvited: + self = .internal + case .waitlistInvited: + self = .waitlist + default: + self = .other + } } } diff --git a/DuckDuckGo/OmniBar.swift b/DuckDuckGo/OmniBar.swift index 7feb722c3e..aa7a17d573 100644 --- a/DuckDuckGo/OmniBar.swift +++ b/DuckDuckGo/OmniBar.swift @@ -375,7 +375,7 @@ class OmniBar: UIView { } func refreshText(forUrl url: URL?, forceFullURL: Bool = false) { - + guard !textField.isEditing else { return } guard let url = url else { textField.text = nil return diff --git a/README.md b/README.md index 3f88970641..4f81944322 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,12 @@ We use [SwifLint](https://github.com/realm/SwiftLint) for enforcing Swift style ### Instruments -We have Custom Instruments tool to help visualize and track events that happen during runtime. +We have a Custom Instruments tool to help visualize and track events that happen during runtime. In order to run it: -1. Build a Debug version and install it on Simulator/Device. -2. Select Instruments target and run it on a Mac. A New instance of Instruments app will be run that has a grayed out icon indicating that it works in debug mode with custom instruments attached. -3. Select 'DDG Trace' template or set up a custom one by importing 'DDG Timeline' instrument from Library. +1. Build a debug version and install it on a simulator or device. +2. Select the Instruments target and run it on a Mac. A new instance of the Instruments app will run. It will have a grayed out icon indicating that it works in debug mode with custom instruments attached. +3. Select the 'DDG Trace' template or set up a custom one by importing the 'DDG Timeline' instrument from Library. 4. Start recording. See [Instruments Developer Help](https://help.apple.com/instruments/developer/mac/current/) for reference how to create custom instruments.