Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NetP widget #2142

Merged
merged 31 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1d14222
Add a status widget.
samsymons Oct 27, 2023
0ec685d
Merge branch 'develop' into sam/hack-days-netp-widget
samsymons Nov 1, 2023
ac10fd9
Add buttons to the widget
samsymons Nov 1, 2023
c4cb5a8
Merge branch 'develop' into sam/hack-days-netp-widget
samsymons Nov 3, 2023
2746f58
Move the widget into its own file.
samsymons Nov 4, 2023
4eb355b
Update entitlements.
samsymons Nov 4, 2023
97c42dc
Remove some logging.
samsymons Nov 4, 2023
114caa8
Allow opening NetP settings via the widget.
samsymons Nov 5, 2023
540dac2
Merge branch 'develop' into sam/hack-days-netp-widget
samsymons Nov 9, 2023
62e2e40
Remove reliance on a BSK branch.
samsymons Nov 9, 2023
419e34a
Only show the widget for DEBUG and ALPHA builds.
samsymons Nov 9, 2023
f2e35b0
Copy tweak.
samsymons Nov 9, 2023
8bc904b
Clean up intents.
samsymons Nov 9, 2023
764704c
Fix an issue in the entitlements file.
samsymons Nov 9, 2023
c5b3873
Update copy.
samsymons Nov 9, 2023
c7ec473
Fix SwiftLint.
samsymons Nov 9, 2023
0010fa8
Check VPN status every 0.5 seconds during the intent.
samsymons Nov 9, 2023
9026343
Only use the widget in alpha builds, and have alphas built with Xcode…
samsymons Nov 10, 2023
ea6223b
Fix a compilation error for release builds.
samsymons Nov 10, 2023
0f6d0e3
Merge branch 'develop' into sam/hack-days-netp-widget
samsymons Nov 10, 2023
176cc1e
Have the VPN widget update tied to the VPN status.
samsymons Nov 10, 2023
05e30b3
Remove a log statement.
samsymons Nov 10, 2023
0d6e124
Temporarily remove develop branch check.
samsymons Nov 10, 2023
6e54998
Try a SyncUI package fix.
samsymons Nov 10, 2023
3f70080
Revert "Try a SyncUI package fix."
samsymons Nov 10, 2023
4cc2db5
Revert "Temporarily remove develop branch check."
samsymons Nov 10, 2023
69f284a
Update alpha entitlements.
samsymons Nov 10, 2023
0fd5b77
Merge branch 'develop' into sam/hack-days-netp-widget
samsymons Nov 13, 2023
5685360
Use the group ID prefix for alpha entitlements.
samsymons Nov 13, 2023
f3e6881
Only clear the nav stack if it isn’t the open VPN action.
samsymons Nov 13, 2023
8163d28
Remove the custom widget background color.
samsymons Nov 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Core/AppDeepLinkSchemes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public enum AppDeepLinkSchemes: String, CaseIterable {

case addFavorite = "ddgAddFavorite"

case openVPN = "ddgOpenVPN"

public var url: URL {
URL(string: rawValue + "://")!
}
Expand Down
6 changes: 6 additions & 0 deletions Core/UserDefaults+NetworkProtection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,10 @@ public extension UserDefaults {
}
}

public enum NetworkProtectionUserDefaultKeys {

public static let lastSelectedServer = "com.duckduckgo.network-protection.last-selected-server"

}

#endif
20 changes: 19 additions & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@
4B470EE4299C6DFB0086EBDC /* Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F143C2E41E4A4CD400CFDE3A /* Core.framework */; };
4B52648B25F9613B00CB4C24 /* trackerData.json in Resources */ = {isa = PBXBuildFile; fileRef = 4B52648A25F9613B00CB4C24 /* trackerData.json */; };
4B53648A26718D0E001AA041 /* EmailWaitlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B53648926718D0E001AA041 /* EmailWaitlist.swift */; };
4B5C462A2AF2A6E6002A4432 /* VPNIntents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B5C46292AF2A6E6002A4432 /* VPNIntents.swift */; };
4B5C462B2AF2BDC4002A4432 /* VPNIntents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B5C46292AF2A6E6002A4432 /* VPNIntents.swift */; };
4B60AC97252EC07B00E8D219 /* fullscreenvideo.js in Resources */ = {isa = PBXBuildFile; fileRef = 4B60AC96252EC07B00E8D219 /* fullscreenvideo.js */; };
4B60ACA1252EC0B100E8D219 /* FullScreenVideoUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B60ACA0252EC0B100E8D219 /* FullScreenVideoUserScript.swift */; };
4B62C4BA25B930DD008912C6 /* AppConfigurationFetchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B62C4B925B930DD008912C6 /* AppConfigurationFetchTests.swift */; };
Expand All @@ -287,6 +289,7 @@
4B83397329AFB8D2003F7EA9 /* AppTrackingProtectionFeedbackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83397229AFB8D2003F7EA9 /* AppTrackingProtectionFeedbackModel.swift */; };
4B83397529AFBCE6003F7EA9 /* AppTrackingProtectionFeedbackModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83397429AFBCE6003F7EA9 /* AppTrackingProtectionFeedbackModelTests.swift */; };
4B948E2629DCCDB9002531FA /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 4B948E2529DCCDB9002531FA /* Persistence */; };
4BB7CBB02AF59C310014A35F /* VPNWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB7CBAF2AF59C310014A35F /* VPNWidget.swift */; };
4BC21A2F27238B7500229F0E /* RunLoopExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC21A2C272388BD00229F0E /* RunLoopExtensionTests.swift */; };
4BC6DD1C2A60E6AD001EC129 /* ReportBrokenSiteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC6DD1B2A60E6AD001EC129 /* ReportBrokenSiteView.swift */; };
4BE2756827304F57006B20B0 /* URLRequestExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BE27566272F878F006B20B0 /* URLRequestExtension.swift */; };
Expand Down Expand Up @@ -1282,6 +1285,7 @@
4B470EE2299C6DD10086EBDC /* AppTrackingProtectionStoringModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTrackingProtectionStoringModel.swift; sourceTree = "<group>"; };
4B52648A25F9613B00CB4C24 /* trackerData.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = trackerData.json; sourceTree = "<group>"; };
4B53648926718D0E001AA041 /* EmailWaitlist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailWaitlist.swift; sourceTree = "<group>"; };
4B5C46292AF2A6E6002A4432 /* VPNIntents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNIntents.swift; sourceTree = "<group>"; };
4B60AC96252EC07B00E8D219 /* fullscreenvideo.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = fullscreenvideo.js; sourceTree = "<group>"; };
4B60ACA0252EC0B100E8D219 /* FullScreenVideoUserScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenVideoUserScript.swift; sourceTree = "<group>"; };
4B62C4B925B930DD008912C6 /* AppConfigurationFetchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfigurationFetchTests.swift; sourceTree = "<group>"; };
Expand All @@ -1296,6 +1300,7 @@
4B83397029AC18C9003F7EA9 /* AppTrackingProtectionStoringModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTrackingProtectionStoringModelTests.swift; sourceTree = "<group>"; };
4B83397229AFB8D2003F7EA9 /* AppTrackingProtectionFeedbackModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTrackingProtectionFeedbackModel.swift; sourceTree = "<group>"; };
4B83397429AFBCE6003F7EA9 /* AppTrackingProtectionFeedbackModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTrackingProtectionFeedbackModelTests.swift; sourceTree = "<group>"; };
4BB7CBAF2AF59C310014A35F /* VPNWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNWidget.swift; sourceTree = "<group>"; };
4BC21A2C272388BD00229F0E /* RunLoopExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunLoopExtensionTests.swift; sourceTree = "<group>"; };
4BC6DD1B2A60E6AD001EC129 /* ReportBrokenSiteView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportBrokenSiteView.swift; sourceTree = "<group>"; };
4BE27566272F878F006B20B0 /* URLRequestExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = URLRequestExtension.swift; path = ../DuckDuckGo/URLRequestExtension.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3379,6 +3384,14 @@
name = AppTrackingProtection;
sourceTree = "<group>";
};
4B5C46282AF2A6DB002A4432 /* Intents */ = {
isa = PBXGroup;
children = (
4B5C46292AF2A6E6002A4432 /* VPNIntents.swift */,
);
name = Intents;
sourceTree = "<group>";
};
4B6484F427FD1E390050A7A1 /* Waitlist */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -3686,6 +3699,7 @@
8512EA5324ED30D20073EE19 /* Widgets.swift */,
853273AF24FEFE4600E3C778 /* WidgetsExtension.entitlements */,
853273A924FEF24300E3C778 /* WidgetViews.swift */,
4BB7CBAF2AF59C310014A35F /* VPNWidget.swift */,
);
path = Widgets;
sourceTree = "<group>";
Expand Down Expand Up @@ -4473,6 +4487,7 @@
EECD94B22A28B8580085C66E /* NetworkProtection */ = {
isa = PBXGroup;
children = (
4B5C46282AF2A6DB002A4432 /* Intents */,
EE9D68D62AE1527F00B55EF4 /* VPNNotifications */,
EE9D68CF2AE00CE000B55EF4 /* VPNSettings */,
EE458D122ABB651500FC651A /* Debug */,
Expand Down Expand Up @@ -6416,6 +6431,7 @@
020108A729A6ABF600644F9D /* AppTPToggleView.swift in Sources */,
02A54A982A093126000C8FED /* AppTPHomeViewModel.swift in Sources */,
F1617C191E573EA800DEDCAF /* TabSwitcherDelegate.swift in Sources */,
4B5C462A2AF2A6E6002A4432 /* VPNIntents.swift in Sources */,
310742A62848CD780012660B /* BackForwardMenuHistoryItem.swift in Sources */,
858566FB252E55D6007501B8 /* ImageCacheDebugViewController.swift in Sources */,
0290472E29E99A2F0008FE3C /* GenericIconView.swift in Sources */,
Expand Down Expand Up @@ -6645,6 +6661,8 @@
853273B324FF114700E3C778 /* DeepLinks.swift in Sources */,
853273B424FFB36100E3C778 /* UIColorExtension.swift in Sources */,
853273AB24FEF27500E3C778 /* WidgetViews.swift in Sources */,
4B5C462B2AF2BDC4002A4432 /* VPNIntents.swift in Sources */,
4BB7CBB02AF59C310014A35F /* VPNWidget.swift in Sources */,
8512EA5424ED30D20073EE19 /* Widgets.swift in Sources */,
85DB12EB2A1FE2A4000A4A72 /* LockScreenWidgets.swift in Sources */,
8544C37C250B827300A0FE73 /* UserText.swift in Sources */,
Expand Down Expand Up @@ -8477,7 +8495,7 @@
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG NETWORK_PROTECTION";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG NETWORK_PROTECTION ALPHA";
samsymons marked this conversation as resolved.
Show resolved Hide resolved
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = "1,2";
VALID_ARCHS = "$(ARCHS_STANDARD_64_BIT)";
Expand Down
4 changes: 4 additions & 0 deletions DuckDuckGo/AppDelegate+AppDeepLinks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Core

extension AppDelegate {

// swiftlint:disable:next cyclomatic_complexity
samsymons marked this conversation as resolved.
Show resolved Hide resolved
func handleAppDeepLink(_ app: UIApplication, _ mainViewController: MainViewController?, _ url: URL) -> Bool {
guard let mainViewController else { return false }

Expand Down Expand Up @@ -50,6 +51,9 @@ extension AppDelegate {
case .newEmail:
mainViewController.newEmailAddress()

case .openVPN:
presentNetworkProtectionStatusSettingsModal()

default:
guard app.applicationState == .active,
let currentTab = mainViewController.currentTab else {
Expand Down
4 changes: 2 additions & 2 deletions DuckDuckGo/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}

NotificationCenter.default.post(name: AutofillLoginListAuthenticator.Notifications.invalidateContext, object: nil)
mainViewController?.clearNavigationStack()
// mainViewController?.clearNavigationStack()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to fix this, it was getting in the way of the open URL functionality. I'll update it to avoid this check depending on the URL.

autoClear?.applicationWillMoveToForeground()
showKeyboardIfSettingOn = false

Expand Down Expand Up @@ -784,7 +784,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
}

#if NETWORK_PROTECTION
private func presentNetworkProtectionStatusSettingsModal() {
func presentNetworkProtectionStatusSettingsModal() {
if #available(iOS 15, *) {
let networkProtectionRoot = NetworkProtectionRootViewController()
presentSettings(with: networkProtectionRoot)
Expand Down
9 changes: 8 additions & 1 deletion DuckDuckGo/NetworkProtectionStatusViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import Foundation
import Combine
import NetworkProtection
import WidgetKit

final class NetworkProtectionStatusViewModel: ObservableObject {
private static var dateFormatter: DateComponentsFormatter = {
Expand Down Expand Up @@ -57,7 +58,11 @@ final class NetworkProtectionStatusViewModel: ObservableObject {

// MARK: Toggle Item
@Published public var isNetPEnabled = false
@Published public var statusMessage: String
@Published public var statusMessage: String {
didSet {
WidgetCenter.shared.reloadTimelines(ofKind: "VPNStatusWidget")
}
}
samsymons marked this conversation as resolved.
Show resolved Hide resolved
@Published public var shouldDisableToggle: Bool = false

// MARK: Connection Details
Expand Down Expand Up @@ -178,6 +183,8 @@ final class NetworkProtectionStatusViewModel: ObservableObject {
} else {
await disableNetP()
}

WidgetCenter.shared.reloadTimelines(ofKind: "VPNStatusWidget")
}

@MainActor
Expand Down
102 changes: 102 additions & 0 deletions DuckDuckGo/VPNIntents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//
// VPNIntents.swift
// DuckDuckGo
//
// Copyright © 2023 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import AppIntents
import NetworkExtension
import WidgetKit

@available(iOS 17.0, *)
struct DisableVPNIntent: AppIntent {

static let title: LocalizedStringResource = "Disable VPN"
static let description: LocalizedStringResource = "Disables the DuckDuckGo VPN"
static let openAppWhenRun: Bool = false

@MainActor
func perform() async throws -> some IntentResult {
do {
let managers = try await NETunnelProviderManager.loadAllFromPreferences()
guard let manager = managers.first else {
return .result()
}

manager.isOnDemandEnabled = false
try await manager.saveToPreferences()
manager.connection.stopVPNTunnel()

WidgetCenter.shared.reloadTimelines(ofKind: "VPNStatusWidget")
var iterations = 0

while iterations <= 10 {
try? await Task.sleep(interval: .seconds(0.5))

if manager.connection.status == .disconnected {
return .result()
}

iterations += 1
}
samsymons marked this conversation as resolved.
Show resolved Hide resolved

return .result()
} catch {
return .result()
}
}

}

@available(iOS 17.0, *)
struct EnableVPNIntent: AppIntent {

static let title: LocalizedStringResource = "Enable VPN"
static let description: LocalizedStringResource = "Enables the DuckDuckGo VPN"
static let openAppWhenRun: Bool = false

@MainActor
func perform() async throws -> some IntentResult {
do {
let managers = try await NETunnelProviderManager.loadAllFromPreferences()
guard let manager = managers.first else {
return .result()
}

manager.isOnDemandEnabled = true
try await manager.saveToPreferences()
try manager.connection.startVPNTunnel()

WidgetCenter.shared.reloadTimelines(ofKind: "VPNStatusWidget")
var iterations = 0

while iterations <= 10 {
try? await Task.sleep(interval: .seconds(0.5))

if manager.connection.status == .connected {
return .result()
}

iterations += 1
}

return .result()
} catch {
return .result()
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@
#if NETWORK_PROTECTION

import Foundation
import NetworkProtection
import Common
import Combine
import Core
import Networking
import NetworkExtension
import NetworkProtection

// Initial implementation for initial Network Protection tests. Will be fleshed out with https://app.asana.com/0/1203137811378537/1204630829332227/f
final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider {

private var cancellables = Set<AnyCancellable>()

// MARK: - PacketTunnelProvider.Event reporting

private static var packetTunnelProviderEvents: EventMapping<PacketTunnelProvider.Event> = .init { event, _, _, _ in
Expand Down Expand Up @@ -183,6 +186,7 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider {
debugEvents: Self.networkProtectionDebugEvents(controllerErrorStore: errorStore),
providerEvents: Self.packetTunnelProviderEvents)
startMonitoringMemoryPressureEvents()
observeServerChanges()
APIRequest.Headers.setUserAgent(DefaultUserAgentManager.duckDuckGoUserAgent)
}

Expand All @@ -209,6 +213,14 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider {
source.resume()
}
}

private func observeServerChanges() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should also utilise the observable UserDefaults pattern with this.

lastSelectedServerInfoPublisher.sink { server in
let location = server?.serverLocation ?? "Unknown Location"
UserDefaults.networkProtectionGroupDefaults.set(location, forKey: NetworkProtectionUserDefaultKeys.lastSelectedServer)
}
.store(in: &cancellables)
}
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xE7",
"green" : "0x68",
"red" : "0x44"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
12 changes: 12 additions & 0 deletions Widgets/Assets.xcassets/vpn-off.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "vpn-off.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.
12 changes: 12 additions & 0 deletions Widgets/Assets.xcassets/vpn-on.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "vpn-on.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.
1 change: 1 addition & 0 deletions Widgets/DeepLinks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ struct DeepLinks {

static let addFavorite = AppDeepLinkSchemes.addFavorite.url

static let openVPN = AppDeepLinkSchemes.openVPN.url
}
Loading
Loading