From 144a2f2a7d41f11f4c105845105c8de44928c443 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Thu, 12 Oct 2023 17:47:06 -0700 Subject: [PATCH 1/7] =?UTF-8?q?Add=20an=20=E2=80=9COpen=20VPN=20Settings?= =?UTF-8?q?=E2=80=9D=20quick=20action.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DuckDuckGo/AppDelegate.swift | 39 +++++++++++++++++- .../VPN-16.imageset/Contents.json | 15 +++++++ .../VPN-16.imageset/VPN-16.pdf | Bin 0 -> 3623 bytes 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 DuckDuckGo/Assets.xcassets/VPN-16.imageset/Contents.json create mode 100644 DuckDuckGo/Assets.xcassets/VPN-16.imageset/VPN-16.pdf diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 604b6db7f2..0b6a33d548 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -34,6 +34,10 @@ import Networking import DDGSync import SyncDataProviders +#if NETWORK_PROTECTION +import NetworkProtection +#endif + // swiftlint:disable file_length // swiftlint:disable type_body_length @@ -45,6 +49,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private struct ShortcutKey { static let clipboard = "com.duckduckgo.mobile.ios.clipboard" + +#if NETWORK_PROTECTION + static let openVPNSettings = "com.duckduckgo.mobile.ios.vpn.open-settings" +#endif } private var testing = false @@ -328,6 +336,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { syncService.scheduler.notifyAppLifecycleEvent() fireFailedCompilationsPixelIfNeeded() + refreshShortcuts() } private func fireAppLaunchPixel() { @@ -590,11 +599,20 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private func handleShortCutItem(_ shortcutItem: UIApplicationShortcutItem) { os_log("Handling shortcut item: %s", log: .generalLog, type: .debug, shortcutItem.type) + mainViewController?.clearNavigationStack() autoClear?.applicationWillMoveToForeground() - if shortcutItem.type == ShortcutKey.clipboard, let query = UIPasteboard.general.string { + + if shortcutItem.type == ShortcutKey.clipboard, let query = UIPasteboard.general.string { mainViewController?.loadQueryInNewTab(query) + return + } + +#if NETWORK_PROTECTION + if shortcutItem.type == ShortcutKey.openVPNSettings { + presentNetworkProtectionStatusSettingsModal() } +#endif } private func removeEmailWaitlistState() { @@ -622,6 +640,25 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private var mainViewController: MainViewController? { return window?.rootViewController as? MainViewController } + + func refreshShortcuts() { +#if NETWORK_PROTECTION + guard NetworkProtectionKeychainTokenStore().isFeatureActivated else { + return + } + + let items = [ + UIApplicationShortcutItem(type: ShortcutKey.openVPNSettings, + localizedTitle: "Open VPN Settings", + localizedSubtitle: nil, + icon: UIApplicationShortcutIcon(templateImageName: "VPN-16"), + userInfo: nil) + ] + + UIApplication.shared.shortcutItems = items +#endif + } + } extension AppDelegate: BlankSnapshotViewRecoveringDelegate { diff --git a/DuckDuckGo/Assets.xcassets/VPN-16.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/VPN-16.imageset/Contents.json new file mode 100644 index 0000000000..eb05efadf9 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/VPN-16.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "VPN-16.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/DuckDuckGo/Assets.xcassets/VPN-16.imageset/VPN-16.pdf b/DuckDuckGo/Assets.xcassets/VPN-16.imageset/VPN-16.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9b953299d8fca9202572b409b94907f035e73778 GIT binary patch literal 3623 zcma)5QX38SM*Dy1c`BXS6`(l5=jU`fCwAjA|8y_Nfvo+SUV{2>-nlZ?wQRZ z!90zRyX$i5RCUjz*Dqgv>Bc+^&RBE!^|!&8r%%nZXTx@Xtv?69#8=;M_wP?1i~wHg zs^jTyyS^P(FSmc*Y_?b5JU1`iEPri|!ykhiZF`KpynNZ;9{$(=+T0HP{o%`TvmeUE zo=cav+d;-0W68nHpdEdY6q9j&Of}k6H58YkPo|8~6)Dwhnzxu;1`F9sh;zUoK1hJ1 zi=#41j-)m1(*)TkAxp4rmCIEviMNiwbYJvE*Z3+@FZ*21in*Gs&r9pJ7Vq!*YaCwo zHGib)f(>GXU~Fh^yu*!{JSH0@g=VtGB*dA=EXlcZFyftORSrH8RZlCA<(yVWHqh*x zMCj`nh~`j0yw4F{Ku9nvvC-z>0{$pZbg4yEUCCraR6(kb#*IFCsR=|VM=c=Hib!E! z8moBn0m~V)-lQ?=wrV{4g7X%{73$_m-3Giw_C>5)3B?4WD#2zGDZGm!ycB>^6p60p ztf_1Hl+VJSHrm+es61vj)Y0aekbqLuu*%LPNq3f9imRql?tnRxCv{|mt%sP}A_O+G zE(C&h>{LODB5E6t#1N^M+L{o}#}{v{uzafh4or$~QikYQ914z21yzi5jX0?@I|#nU zP(!Sp*RsU?@6L{5e}+L1fg4~IE4nvwnPm9D^&{Y z5rP+03|rkuZ)B9OV2TAFKrW_|6%y;5YmiS#Y=a~sk+wBaeOObkQX}$oV@clD7CbFh zJqdwnMlECvOLfy`rhu+Q(>ROgG((keo<7=pZ1LitTUT#z;A^#rs6c%p&SUJ zusqHGd=4d}F`6D8+c`ljh?b^R?ib#scATNj@PXD0$!3)!g_DBH^f0naJf@+iNpnPI zNLA;ujwA}ecm(kgK?fZ*myx7(W>PG$-2f(x-PZ5{J|lAu)p;64#%djHu11-L96SiY z(>Ka+9O+55PbyJ7M;)cHv>!P^42)Zl&tun&h}Dap9en`ZY7~^8S=TX*%n3>$6|1vI zV8bVu@W1aS{bIw}h5lni4IP7u_HfWJ3InHHRC>->cM_QJdXUZM zt`edTRqd;~QMEPw-HlE!i=(?Q?~C#^>Z z*kp9F9_Flj44sEXT%ALWYqFm??6@>pq*nBF((`!wOSntuGb5Y9k513J1=fS^mO9e? zsaCqRd@*~AHRO(4D41N)aA%(oz=_Ma>&U`d$9k69J=4yA!}{LEy6yprb0w`giifc- z)UJ2>>B4@w-RnjEeCzkzH~Xa@$;}@FtFQL^!|7<={=&xr&+6yDe&3qa>-GEX1pKhQ zzF9v%d@^rmsdjnILvnt*Si0IC4`}nCSYR#%6^$!Nlj;MJR(zqdr+KIztXr Date: Thu, 12 Oct 2023 19:26:25 -0700 Subject: [PATCH 2/7] Refresh the shortcut status on willResignActive. --- DuckDuckGo/AppDelegate.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 0b6a33d548..fc74c69264 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -339,6 +339,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { refreshShortcuts() } + func applicationWillResignActive(_ application: UIApplication) { + refreshShortcuts() + } + private func fireAppLaunchPixel() { WidgetCenter.shared.getCurrentConfigurations { result in From 9d98e82e50283dcbd8227ed2e549f1369f9bc3c9 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Tue, 17 Oct 2023 09:20:36 +0100 Subject: [PATCH 3/7] Change copy for NetP shortcut. --- DuckDuckGo/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index e9fb7510c9..ea37a0905d 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -654,7 +654,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { let items = [ UIApplicationShortcutItem(type: ShortcutKey.openVPNSettings, - localizedTitle: "Open VPN Settings", + localizedTitle: "Open DuckDuckGo VPN", localizedSubtitle: nil, icon: UIApplicationShortcutIcon(templateImageName: "VPN-16"), userInfo: nil) From a3c6fee37616f5d046cddc282c31e6bb348f1482 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Mon, 23 Oct 2023 14:34:26 +0200 Subject: [PATCH 4/7] Update copy. --- DuckDuckGo/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 4e17272db0..b9a322be52 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -654,7 +654,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { let items = [ UIApplicationShortcutItem(type: ShortcutKey.openVPNSettings, - localizedTitle: "Open DuckDuckGo VPN", + localizedTitle: "Open VPN", localizedSubtitle: nil, icon: UIApplicationShortcutIcon(templateImageName: "VPN-16"), userInfo: nil) From fd214ddbb1ee680ed203397ab032faf92e9f5f34 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Mon, 23 Oct 2023 15:01:27 +0200 Subject: [PATCH 5/7] Add copy to UserText --- DuckDuckGo/AppDelegate.swift | 2 +- DuckDuckGo/UserText.swift | 2 ++ DuckDuckGo/en.lproj/Localizable.strings | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index b9a322be52..6cbced1922 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -654,7 +654,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { let items = [ UIApplicationShortcutItem(type: ShortcutKey.openVPNSettings, - localizedTitle: "Open VPN", + localizedTitle: UserText.netPOpenVPNQuickAction, localizedSubtitle: nil, icon: UIApplicationShortcutIcon(templateImageName: "VPN-16"), userInfo: nil) diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index 4425118079..8d2331337d 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -657,6 +657,8 @@ In addition to the details entered into this form, your app issue report will co static let netPSecureDNSSettingTitle = NSLocalizedString("network.protection.vpn.secure.dns.setting.title", value: "Secure DNS", comment: "Title for the Always on VPN setting item.") static let netPSecureDNSSettingFooter = NSLocalizedString("network.protection.vpn.secure.dns.setting.footer", value: "Network Protection prevents DNS leaks to your Internet Service Provider by routing DNS queries though the VPN tunnel to our own resolver.", comment: "Footer text for the Always on VPN setting item.") + static let netPOpenVPNQuickAction = NSLocalizedString("network.protection.quick-action.open-vpn", value: "Open VPN", comment: "Title text for an iOS quick action that opens VPN settings") + static let inviteDialogContinueButton = NSLocalizedString("invite.dialog.continue.button", value: "Continue", comment: "Continue button on an invite dialog") static let inviteDialogGetStartedButton = NSLocalizedString("invite.dialog.get.started.button", value: "Get Started", comment: "Get Started button on an invite dialog") static let inviteDialogUnrecognizedCodeMessage = NSLocalizedString("invite.dialog.unrecognized.code.message", value: "We didn’t recognize this Invite Code.", comment: "Message to show after user enters an unrecognized invite code") diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index c071c4409f..b966ba574d 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -1393,6 +1393,9 @@ https://duckduckgo.com/mac"; /* Title for the network protection invite success view */ "network.protection.invite.success.title" = "Success! You’re in."; +/* Title text for an iOS quick action that opens VPN settings */ +"network.protection.quick-action.open-vpn" = "Open VPN"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Connected - %@"; From 6e48e928461bd5cff472ad4a9570e5db1eeb3454 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Mon, 23 Oct 2023 15:43:14 +0200 Subject: [PATCH 6/7] Avoid dismissing and re-presenting the settings VC unnecessarily. --- DuckDuckGo/AppDelegate.swift | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 6cbced1922..0b13604477 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -605,10 +605,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private func handleShortCutItem(_ shortcutItem: UIApplicationShortcutItem) { os_log("Handling shortcut item: %s", log: .generalLog, type: .debug, shortcutItem.type) - mainViewController?.clearNavigationStack() autoClear?.applicationWillMoveToForeground() if shortcutItem.type == ShortcutKey.clipboard, let query = UIPasteboard.general.string { + mainViewController?.clearNavigationStack() mainViewController?.loadQueryInNewTab(query) return } @@ -749,6 +749,19 @@ extension AppDelegate: UNUserNotificationCenterDelegate { private func presentSettings(with viewController: UIViewController) { guard let window = window, let rootViewController = window.rootViewController as? MainViewController else { return } + if let navigationController = rootViewController.presentedViewController as? UINavigationController { + if let lastViewController = navigationController.viewControllers.last, lastViewController.isKind(of: type(of: viewController)) { + // Avoid presenting dismissing and re-presenting the view controller if it's already visible: + return + } else { + // Otherwise, replace existing view controllers with the presented one: + navigationController.popToRootViewController(animated: false) + navigationController.pushViewController(viewController, animated: false) + return + } + } + + // If the previous checks failed, make sure the nav stack is reset and present the view controller from scratch: rootViewController.clearNavigationStack() // Give the `clearNavigationStack` call time to complete. From 90c8043703dba372837240916e5b52d6cd2b03b7 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Fri, 27 Oct 2023 11:38:29 +0200 Subject: [PATCH 7/7] Remove some recovered references that are no longer applicable. --- DuckDuckGo.xcodeproj/project.pbxproj | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index fcd7e3b819..3241f608ca 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -1297,7 +1297,6 @@ 6AC6DAB228804F97002723C0 /* BarsAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarsAnimator.swift; sourceTree = ""; }; 6AC98418288055C1005FA9CA /* BarsAnimatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarsAnimatorTests.swift; sourceTree = ""; }; 6FB030C7234331B400A10DB9 /* Configuration.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Configuration.xcconfig; path = Configuration/Configuration.xcconfig; sourceTree = ""; }; - 7B5E1F9D2AB9E1E900DA1172 /* NetworkProtectionDebugFeatures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionDebugFeatures.swift; sourceTree = ""; }; 83004E7F2193BB8200DA013C /* WKNavigationExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKNavigationExtension.swift; sourceTree = ""; }; 83004E832193E14C00DA013C /* UIAlertControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = UIAlertControllerExtension.swift; path = ../Core/UIAlertControllerExtension.swift; sourceTree = ""; }; 83004E852193E5ED00DA013C /* TabViewControllerBrowsingMenuExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabViewControllerBrowsingMenuExtension.swift; sourceTree = ""; }; @@ -3535,7 +3534,6 @@ 83ED3B8D1FA8E63700B47556 /* README.md */, 83ED3B8C1FA8E61D00B47556 /* ManualTestsScript.md */, 85A313962028E78A00327D00 /* release_notes.txt */, - EEF0F8CA2ABC82E100630031 /* Recovered References */, ); sourceTree = ""; }; @@ -4446,14 +4444,6 @@ name = Status; sourceTree = ""; }; - EEF0F8CA2ABC82E100630031 /* Recovered References */ = { - isa = PBXGroup; - children = ( - 7B5E1F9D2AB9E1E900DA1172 /* NetworkProtectionDebugFeatures.swift */, - ); - name = "Recovered References"; - sourceTree = ""; - }; EEFD562D2A65B68B00DAEC48 /* Invite */ = { isa = PBXGroup; children = (