From 1280d29505f2d24ed3e0336148047615f09f86aa Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Fri, 27 Oct 2023 11:45:57 +0200 Subject: [PATCH] =?UTF-8?q?Add=20an=20=E2=80=9COpen=20VPN=20Settings?= =?UTF-8?q?=E2=80=9D=20quick=20action=20(#2089)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task/Issue URL: https://app.asana.com/0/414235014887631/1205784963805147/f Tech Design URL: CC: @graeme @diegoreymendez Description: This PR adds an iOS VPN quick action. --- DuckDuckGo.xcodeproj/project.pbxproj | 10 --- DuckDuckGo/AppDelegate.swift | 58 +++++++++++++++++- .../VPN-16.imageset/Contents.json | 15 +++++ .../VPN-16.imageset/VPN-16.pdf | Bin 0 -> 3623 bytes DuckDuckGo/UserText.swift | 2 + DuckDuckGo/en.lproj/Localizable.strings | 3 + 6 files changed, 76 insertions(+), 12 deletions(-) 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.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 = ( diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index ee385aa81a..0b13604477 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 @@ -329,6 +337,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { syncService.scheduler.notifyAppLifecycleEvent() fireFailedCompilationsPixelIfNeeded() + refreshShortcuts() + } + + func applicationWillResignActive(_ application: UIApplication) { + refreshShortcuts() } private func fireAppLaunchPixel() { @@ -591,11 +604,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?.clearNavigationStack() mainViewController?.loadQueryInNewTab(query) + return + } + +#if NETWORK_PROTECTION + if shortcutItem.type == ShortcutKey.openVPNSettings { + presentNetworkProtectionStatusSettingsModal() } +#endif } private func removeEmailWaitlistState() { @@ -623,6 +645,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: UserText.netPOpenVPNQuickAction, + localizedSubtitle: nil, + icon: UIApplicationShortcutIcon(templateImageName: "VPN-16"), + userInfo: nil) + ] + + UIApplication.shared.shortcutItems = items +#endif + } + } extension AppDelegate: BlankSnapshotViewRecoveringDelegate { @@ -708,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. 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