Skip to content

Commit

Permalink
Release iOS: VPN widget for control center & Siri shortcut (#3772)
Browse files Browse the repository at this point in the history
Task/Issue URL:
https://app.asana.com/0/1206580121312550/1209007855977973/f

## Description

Addresses final ship-review feedback for our new control center widget
and releases it for all users.
  • Loading branch information
diegoreymendez authored Jan 7, 2025
1 parent a4142e3 commit 5591a5a
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 11 deletions.
2 changes: 0 additions & 2 deletions DuckDuckGo/NetworkProtectionVPNSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ struct NetworkProtectionVPNSettingsView: View {
}.daxBodyRegular()
}

#if ALPHA || DEBUG
if #available(iOS 18.0, *) {
NavigationLink {
ControlCenterWidgetEducationView(navBarTitle: "Add DuckDuckGo VPN Shortcut to Your Control Center",
Expand All @@ -187,7 +186,6 @@ struct NetworkProtectionVPNSettingsView: View {
.frame(width: 24, height: 24)
}.daxBodyRegular()
}
#endif
} header: {
Text(UserText.netPVPNShortcutsSectionHeader)
}
Expand Down
2 changes: 0 additions & 2 deletions DuckDuckGo/VPNAutoShortcuts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import AppIntents
import Foundation

#if ALPHA || DEBUG
@available(iOS 17.0, *)
struct VPNAutoShortcutsiOS17: AppShortcutsProvider {

Expand Down Expand Up @@ -60,4 +59,3 @@ struct VPNAutoShortcutsiOS17: AppShortcutsProvider {
systemImageName: "globe")
}
}
#endif
34 changes: 34 additions & 0 deletions Widgets/UserText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,40 @@ struct UserText {
return localized.format(arguments: endDate)
}

// MARK: - Control Center Widget

static let vpnControlWidgetOn = NSLocalizedString(
"vpn.control.widget.on",
value: "VPN is ON",
comment: "Title for the control widget when enabled")

static let vpnControlWidgetOff = NSLocalizedString(
"vpn.control.widget.off",
value: "VPN is OFF",
comment: "Title for the control widget when disabled")

static let vpnControlWidgetLocationUnknown = NSLocalizedString(
"vpn.control.widget.location-unknown",
value: "Unknown Location",
comment: "Description for the control widget when the location is unknown")

static let vpnControlWidgetConnecting = NSLocalizedString(
"vpn.control.widget.connecting",
value: "Connecting...",
comment: "Description for the control widget when connecting")

static let vpnControlWidgetDisconnecting = NSLocalizedString(
"vpn.control.widget.disconnecting",
value: "Disconnecting...",
comment: "Description for the control widget when disconnecting")

static let vpnControlWidgetNotConnected = NSLocalizedString(
"vpn.control.widget.not-connected",
value: "Not Connected",
comment: "Description for the control widget when not connected")

// MARK: - Misc...

static let lockScreenSearchTitle = NSLocalizedString(
"lock.screen.widget.search.title",
value: "Private Search",
Expand Down
30 changes: 25 additions & 5 deletions Widgets/VPNControlWidget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,52 @@
// limitations under the License.
//

import Core
import Foundation
import SwiftUI
import WidgetKit

#if ALPHA || DEBUG
@available(iOSApplicationExtension 18.0, *)
public struct VPNControlWidget: ControlWidget {
static let displayName = LocalizedStringResource(stringLiteral: "DuckDuckGo\nVPN")
static let description = LocalizedStringResource(stringLiteral: "View and manage your VPN connection. Requires a Privacy Pro subscription.")
static let unknownLocation = UserText.vpnControlWidgetLocationUnknown

public init() {}

public var body: some ControlWidgetConfiguration {
StaticControlConfiguration(kind: .vpn,
provider: VPNControlStatusValueProvider()) { status in

ControlWidgetToggle("DuckDuckGo\nVPN", isOn: status.isConnected, action: ControlWidgetToggleVPNIntent()) { isOn in
ControlWidgetToggle(title(status: status), isOn: status.isConnected, action: ControlWidgetToggleVPNIntent()) { isOn in
if isOn {
Label("Connected", image: "ControlCenter-VPN-on")
Label(location(status: status), image: "ControlCenter-VPN-on")
} else {
Label("Not Connected", image: "ControlCenter-VPN-off")
Label(UserText.vpnControlWidgetNotConnected, image: "ControlCenter-VPN-off")
}
}
.tint(.green)
}.displayName(Self.displayName)
.description(Self.description)
}

private func title(status: VPNStatus) -> String {
if status.isConnected {
return UserText.vpnControlWidgetOn
} else {
return UserText.vpnControlWidgetOff
}
}

private func location(status: VPNStatus) -> String {
if status.isConnecting {
return UserText.vpnControlWidgetConnecting
} else if status.isDisconnecting {
return UserText.vpnControlWidgetDisconnecting
} else if status.isConnected {
return UserDefaults.networkProtectionGroupDefaults.string(forKey: NetworkProtectionUserDefaultKeys.lastSelectedServerCity) ?? Self.unknownLocation
} else {
return Self.unknownLocation
}
}
}
#endif
18 changes: 18 additions & 0 deletions Widgets/VPNWidget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@ enum VPNStatus {
case error
case notConfigured

var isConnecting: Bool {
switch self {
case .status(let status):
return status == .connecting
default:
return false
}
}

var isDisconnecting: Bool {
switch self {
case .status(let status):
return status == .disconnecting
default:
return false
}
}

var isConnected: Bool {
switch self {
case .status(let status):
Expand Down
2 changes: 0 additions & 2 deletions Widgets/Widgets.swift
Original file line number Diff line number Diff line change
Expand Up @@ -259,11 +259,9 @@ struct VPNBundle: WidgetBundle {
VPNSnoozeLiveActivity()
}

#if ALPHA || DEBUG
if #available(iOS 18, *) {
VPNControlWidget()
}
#endif
}
}

Expand Down
18 changes: 18 additions & 0 deletions Widgets/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,24 @@
/* Title shown to the user when adding the Voice Search lock screen widget */
"lock.screen.widget.voice.title" = "Voice Search";

/* Description for the control widget when connecting */
"vpn.control.widget.connecting" = "Connecting...";

/* Description for the control widget when disconnecting */
"vpn.control.widget.disconnecting" = "Disconnecting...";

/* Description for the control widget when the location is unknown */
"vpn.control.widget.location-unknown" = "Unknown Location";

/* Description for the control widget when not connected */
"vpn.control.widget.not-connected" = "Not Connected";

/* Title for the control widget when disabled */
"vpn.control.widget.off" = "VPN is OFF";

/* Title for the control widget when enabled */
"vpn.control.widget.on" = "VPN is ON";

/* Description of search passwords widget in widget gallery */
"widget.gallery.passwords.description" = "Quickly search your saved DuckDuckGo passwords.";

Expand Down

0 comments on commit 5591a5a

Please sign in to comment.