Skip to content

Commit

Permalink
Add an “Open VPN Settings” quick action (#2089)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
samsymons authored Oct 27, 2023
1 parent 97551e4 commit 1280d29
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 12 deletions.
10 changes: 0 additions & 10 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1297,7 +1297,6 @@
6AC6DAB228804F97002723C0 /* BarsAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarsAnimator.swift; sourceTree = "<group>"; };
6AC98418288055C1005FA9CA /* BarsAnimatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarsAnimatorTests.swift; sourceTree = "<group>"; };
6FB030C7234331B400A10DB9 /* Configuration.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Configuration.xcconfig; path = Configuration/Configuration.xcconfig; sourceTree = "<group>"; };
7B5E1F9D2AB9E1E900DA1172 /* NetworkProtectionDebugFeatures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionDebugFeatures.swift; sourceTree = "<group>"; };
83004E7F2193BB8200DA013C /* WKNavigationExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKNavigationExtension.swift; sourceTree = "<group>"; };
83004E832193E14C00DA013C /* UIAlertControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = UIAlertControllerExtension.swift; path = ../Core/UIAlertControllerExtension.swift; sourceTree = "<group>"; };
83004E852193E5ED00DA013C /* TabViewControllerBrowsingMenuExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabViewControllerBrowsingMenuExtension.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3535,7 +3534,6 @@
83ED3B8D1FA8E63700B47556 /* README.md */,
83ED3B8C1FA8E61D00B47556 /* ManualTestsScript.md */,
85A313962028E78A00327D00 /* release_notes.txt */,
EEF0F8CA2ABC82E100630031 /* Recovered References */,
);
sourceTree = "<group>";
};
Expand Down Expand Up @@ -4446,14 +4444,6 @@
name = Status;
sourceTree = "<group>";
};
EEF0F8CA2ABC82E100630031 /* Recovered References */ = {
isa = PBXGroup;
children = (
7B5E1F9D2AB9E1E900DA1172 /* NetworkProtectionDebugFeatures.swift */,
);
name = "Recovered References";
sourceTree = "<group>";
};
EEFD562D2A65B68B00DAEC48 /* Invite */ = {
isa = PBXGroup;
children = (
Expand Down
58 changes: 56 additions & 2 deletions DuckDuckGo/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -329,6 +337,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

syncService.scheduler.notifyAppLifecycleEvent()
fireFailedCompilationsPixelIfNeeded()
refreshShortcuts()
}

func applicationWillResignActive(_ application: UIApplication) {
refreshShortcuts()
}

private func fireAppLaunchPixel() {
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
Expand Down
15 changes: 15 additions & 0 deletions DuckDuckGo/Assets.xcassets/VPN-16.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "VPN-16.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.
2 changes: 2 additions & 0 deletions DuckDuckGo/UserText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
3 changes: 3 additions & 0 deletions DuckDuckGo/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -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 - %@";

Expand Down

0 comments on commit 1280d29

Please sign in to comment.