diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2022-11-30 15:43:00 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2022-12-05 10:55:05 +0100 |
| commit | f14081c8afe513df060293d3362c10eaf8ab349a (patch) | |
| tree | a39de241a3324436cb692474b9663da5ad754b25 | |
| parent | f868646830e0c9f764bc243f7ee1316b7171aac4 (diff) | |
| download | mullvadvpn-f14081c8afe513df060293d3362c10eaf8ab349a.tar.xz mullvadvpn-f14081c8afe513df060293d3362c10eaf8ab349a.zip | |
Drop intents support
Sadly we cannot support intents since we can't run multiple copies of our main
scene without synchronizing state between all of them. Not to mention we never
intended this to work this way.
Also, currently there is no known way to prevent user from creating multiple
windows of the app on iPadOS.
| -rw-r--r-- | ios/CHANGELOG.md | 3 | ||||
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 24 | ||||
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme | 2 | ||||
| -rw-r--r-- | ios/MullvadVPN/AppDelegate.swift | 14 | ||||
| -rw-r--r-- | ios/MullvadVPN/Info.plist | 12 | ||||
| -rw-r--r-- | ios/MullvadVPN/IntentHandlers.swift | 89 | ||||
| -rw-r--r-- | ios/MullvadVPN/Intents.intentdefinition | 163 | ||||
| -rw-r--r-- | ios/MullvadVPN/SceneDelegate.swift | 2 | ||||
| -rw-r--r-- | ios/MullvadVPN/SettingsDataSource.swift | 20 | ||||
| -rw-r--r-- | ios/MullvadVPN/SettingsNavigationController.swift | 4 | ||||
| -rw-r--r-- | ios/MullvadVPN/SettingsViewController.swift | 2 | ||||
| -rw-r--r-- | ios/MullvadVPN/ShortcutsDataSource.swift | 234 | ||||
| -rw-r--r-- | ios/MullvadVPN/ShortcutsDataSourceDelegate.swift | 14 | ||||
| -rw-r--r-- | ios/MullvadVPN/ShortcutsManager.swift | 64 | ||||
| -rw-r--r-- | ios/MullvadVPN/ShortcutsViewController.swift | 116 |
15 files changed, 2 insertions, 761 deletions
diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 0cb7b673df..e8751c1985 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -30,9 +30,6 @@ Line wrap the file at 100 chars. Th - Add revoked device view displayed when the app detects that device is no longer registered on backend. - Add ability to manage registered devices if too many devices detected during log-in. -- Add intents: start VPN, stop VPN, reconnect VPN (acts as start VPN when the tunnel is down, - otherwise picks new relay). -- Add menu item to control shortcuts. - Add continuous monitoring of tunnel connection. Verify ping replies to detect whether traffic is really flowing. - Check if device is revoked or account has expired when the tunnel fails to connect on each second diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 042c517930..244617b56f 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -163,8 +163,6 @@ 5871167F2910035700D41AAC /* PreferencesInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5871167E2910035700D41AAC /* PreferencesInteractor.swift */; }; 5871FB96254ADE4E0051A0A4 /* ConsolidatedApplicationLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5871FB95254ADE4E0051A0A4 /* ConsolidatedApplicationLog.swift */; }; 5871FBA0254C26C00051A0A4 /* NSRegularExpression+IPAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5871FB9F254C26BF0051A0A4 /* NSRegularExpression+IPAddress.swift */; }; - 5872631B283F6EAB00E14ADF /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 5872631A283F6EAB00E14ADF /* Intents.intentdefinition */; }; - 5872631D283F755900E14ADF /* IntentHandlers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5872631C283F755900E14ADF /* IntentHandlers.swift */; }; 58727283265D173C00F315B2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 58727282265D173C00F315B2 /* LaunchScreen.storyboard */; }; 5872D6E8286304DE00DB5F4E /* TermsOfService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5872D6E7286304DE00DB5F4E /* TermsOfService.swift */; }; 587425C12299833500CA2045 /* RootContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587425C02299833500CA2045 /* RootContainerViewController.swift */; }; @@ -323,10 +321,6 @@ 58FEEB46260A028D00A621A8 /* GeoJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FEEB45260A028D00A621A8 /* GeoJSON.swift */; }; 58FEEB58260B662E00A621A8 /* AutomaticKeyboardResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FEEB57260B662E00A621A8 /* AutomaticKeyboardResponder.swift */; }; 58FF2C03281BDE02009EF542 /* SettingsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FF2C02281BDE02009EF542 /* SettingsManager.swift */; }; - 753D6C0C28B4BF3E0052D9E1 /* ShortcutsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753D6C0B28B4BF3E0052D9E1 /* ShortcutsManager.swift */; }; - 75FD0C2128B108570021E33E /* ShortcutsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75FD0C2028B108570021E33E /* ShortcutsDataSource.swift */; }; - 75FD0C2328B109860021E33E /* ShortcutsDataSourceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75FD0C2228B109860021E33E /* ShortcutsDataSourceDelegate.swift */; }; - 75FD0C2528B117D30021E33E /* ShortcutsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75FD0C2428B117D30021E33E /* ShortcutsViewController.swift */; }; E1187ABC289BBB850024E748 /* OutOfTimeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1187ABA289BBB850024E748 /* OutOfTimeViewController.swift */; }; E1187ABD289BBB850024E748 /* OutOfTimeContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1187ABB289BBB850024E748 /* OutOfTimeContentView.swift */; }; E158B360285381C60002F069 /* StringFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E158B35F285381C60002F069 /* StringFormatter.swift */; }; @@ -672,8 +666,6 @@ 5871167E2910035700D41AAC /* PreferencesInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesInteractor.swift; sourceTree = "<group>"; }; 5871FB95254ADE4E0051A0A4 /* ConsolidatedApplicationLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsolidatedApplicationLog.swift; sourceTree = "<group>"; }; 5871FB9F254C26BF0051A0A4 /* NSRegularExpression+IPAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSRegularExpression+IPAddress.swift"; sourceTree = "<group>"; }; - 5872631A283F6EAB00E14ADF /* Intents.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = Intents.intentdefinition; sourceTree = "<group>"; }; - 5872631C283F755900E14ADF /* IntentHandlers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentHandlers.swift; sourceTree = "<group>"; }; 58727282265D173C00F315B2 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; }; 5872D6E7286304DE00DB5F4E /* TermsOfService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TermsOfService.swift; sourceTree = "<group>"; }; 587425C02299833500CA2045 /* RootContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootContainerViewController.swift; sourceTree = "<group>"; }; @@ -826,10 +818,6 @@ 58FEEB45260A028D00A621A8 /* GeoJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeoJSON.swift; sourceTree = "<group>"; }; 58FEEB57260B662E00A621A8 /* AutomaticKeyboardResponder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomaticKeyboardResponder.swift; sourceTree = "<group>"; }; 58FF2C02281BDE02009EF542 /* SettingsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsManager.swift; sourceTree = "<group>"; }; - 753D6C0B28B4BF3E0052D9E1 /* ShortcutsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsManager.swift; sourceTree = "<group>"; }; - 75FD0C2028B108570021E33E /* ShortcutsDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsDataSource.swift; sourceTree = "<group>"; }; - 75FD0C2228B109860021E33E /* ShortcutsDataSourceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsDataSourceDelegate.swift; sourceTree = "<group>"; }; - 75FD0C2428B117D30021E33E /* ShortcutsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsViewController.swift; sourceTree = "<group>"; }; E1187ABA289BBB850024E748 /* OutOfTimeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutOfTimeViewController.swift; sourceTree = "<group>"; }; E1187ABB289BBB850024E748 /* OutOfTimeContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutOfTimeContentView.swift; sourceTree = "<group>"; }; E158B35F285381C60002F069 /* StringFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringFormatter.swift; sourceTree = "<group>"; }; @@ -1332,8 +1320,6 @@ 58F3C0A3249CB069003E76BE /* HeaderBarView.swift */, 58FD5BF32428C67600112C88 /* InAppPurchaseButton.swift */, 58CE5E6F224146210008646E /* Info.plist */, - 5872631C283F755900E14ADF /* IntentHandlers.swift */, - 5872631A283F6EAB00E14ADF /* Intents.intentdefinition */, 58727282265D173C00F315B2 /* LaunchScreen.storyboard */, 58E20770274672CA00DE5D77 /* LaunchViewController.swift */, 583DA21325FA4B5C00318683 /* LocationDataSource.swift */, @@ -1388,10 +1374,6 @@ 58ACF64A26553C3F00ACE4B7 /* SettingsSwitchCell.swift */, 58CCA01122424D11004F3011 /* SettingsViewController.swift */, 58677711290976FB006F721F /* SettingsInteractor.swift */, - 75FD0C2028B108570021E33E /* ShortcutsDataSource.swift */, - 75FD0C2228B109860021E33E /* ShortcutsDataSourceDelegate.swift */, - 753D6C0B28B4BF3E0052D9E1 /* ShortcutsManager.swift */, - 75FD0C2428B117D30021E33E /* ShortcutsViewController.swift */, 58BA693023EADA6A009DC256 /* SimulatorTunnelProvider.swift */, 587A01FB23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift */, 58FD5BEF24238EB300112C88 /* SKProduct+Formatting.swift */, @@ -2192,7 +2174,6 @@ 588527B2276B3F0700BAA373 /* LoadTunnelConfigurationOperation.swift in Sources */, 58F1311527E0B2AB007AC5BC /* Result+Extensions.swift in Sources */, 5867770E29096984006F721F /* OutOfTimeInteractor.swift in Sources */, - 5872631B283F6EAB00E14ADF /* Intents.intentdefinition in Sources */, 58F8AC0E25D3F8CE002BE0ED /* ProblemReportReviewViewController.swift in Sources */, 5878A27129091CF20096FC88 /* AccountInteractor.swift in Sources */, 068CE5742927B7A400A068BB /* Migration.swift in Sources */, @@ -2222,7 +2203,6 @@ 5878A279290954790096FC88 /* ConnectInteractor.swift in Sources */, 582AE3102440A6CA00E6733A /* AccountTokenInput.swift in Sources */, 5820EDAB288FF0D2006BF4E4 /* DeviceRowView.swift in Sources */, - 75FD0C2128B108570021E33E /* ShortcutsDataSource.swift in Sources */, 5846227726E22A7C0035F7C2 /* StorePaymentManagerDelegate.swift in Sources */, 58EF581125D69DB400AEBA94 /* StatusImageView.swift in Sources */, 58907D9524D17B4E00CFC3F5 /* DisconnectSplitButton.swift in Sources */, @@ -2240,7 +2220,6 @@ E1187ABD289BBB850024E748 /* OutOfTimeContentView.swift in Sources */, 58CC40EF24A601900019D96E /* ObserverList.swift in Sources */, 58CCA01822426713004F3011 /* AccountViewController.swift in Sources */, - 75FD0C2528B117D30021E33E /* ShortcutsViewController.swift in Sources */, 5871FBA0254C26C00051A0A4 /* NSRegularExpression+IPAddress.swift in Sources */, 58F7CA882692E34000FC59FD /* WireguardKeysContentView.swift in Sources */, 5878A27729093A4F0096FC88 /* StorePaymentBlockObserver.swift in Sources */, @@ -2276,7 +2255,6 @@ 586A950D290125F0007BAF2B /* PresentAlertOperation.swift in Sources */, 58B93A1326C3F13600A55733 /* TunnelState.swift in Sources */, 58FEEB46260A028D00A621A8 /* GeoJSON.swift in Sources */, - 753D6C0C28B4BF3E0052D9E1 /* ShortcutsManager.swift in Sources */, 58CE5E64224146200008646E /* AppDelegate.swift in Sources */, 5878A27329091D6D0096FC88 /* TunnelBlockObserver.swift in Sources */, 5872D6E8286304DE00DB5F4E /* TermsOfService.swift in Sources */, @@ -2288,7 +2266,6 @@ 06410E04292D0F7100AFC18C /* SettingsParser.swift in Sources */, 5878A27D2909657C0096FC88 /* RevokedDeviceInteractor.swift in Sources */, 58677710290975E9006F721F /* SettingsInteractorFactory.swift in Sources */, - 5872631D283F755900E14ADF /* IntentHandlers.swift in Sources */, 58CCA01222424D11004F3011 /* SettingsViewController.swift in Sources */, 06410DFE292CE18F00AFC18C /* KeychainSettingsStore.swift in Sources */, 580F8B8628197958002E0998 /* DNSSettings.swift in Sources */, @@ -2298,7 +2275,6 @@ 58293FAE2510CA58005D0BB5 /* ProblemReportViewController.swift in Sources */, 58B9EB152489139B00095626 /* DisplayChainedError.swift in Sources */, 587B753F2668E5A700DEF7E9 /* NotificationContainerView.swift in Sources */, - 75FD0C2328B109860021E33E /* ShortcutsDataSourceDelegate.swift in Sources */, 58421034282E4B1500F24E46 /* TunnelSettingsV2+REST.swift in Sources */, 58F2E144276A13F300A79513 /* StartTunnelOperation.swift in Sources */, 5868BD33261DCD2600E6027F /* CustomSplitViewController.swift in Sources */, diff --git a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme index dc70ad4ec7..c0ec326af9 100644 --- a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme +++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme @@ -88,7 +88,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - launchStyle = "0" + launchStyle = "1" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift index 1721f80261..27515a798e 100644 --- a/ios/MullvadVPN/AppDelegate.swift +++ b/ios/MullvadVPN/AppDelegate.swift @@ -7,7 +7,6 @@ // import BackgroundTasks -import Intents import MullvadLogging import MullvadREST import Operations @@ -156,19 +155,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD return true } - func application(_ application: UIApplication, handlerFor intent: INIntent) -> Any? { - switch intent { - case is StartVPNIntent: - return StartVPNIntentHandler(tunnelManager: tunnelManager) - case is StopVPNIntent: - return StopVPNIntentHandler(tunnelManager: tunnelManager) - case is ReconnectVPNIntent: - return ReconnectVPNIntentHandler(tunnelManager: tunnelManager) - default: - return nil - } - } - // MARK: - UISceneSession lifecycle func application( diff --git a/ios/MullvadVPN/Info.plist b/ios/MullvadVPN/Info.plist index 687433d3a5..9c9b1744e4 100644 --- a/ios/MullvadVPN/Info.plist +++ b/ios/MullvadVPN/Info.plist @@ -28,18 +28,6 @@ <string>$(MARKETING_VERSION)</string> <key>CFBundleVersion</key> <string>$(CURRENT_PROJECT_VERSION)</string> - <key>INIntentsRestrictedWhileLocked</key> - <array> - <string>StartVPNIntent</string> - <string>StopVPNIntent</string> - <string>ReconnectVPNIntent</string> - </array> - <key>INIntentsSupported</key> - <array> - <string>StartVPNIntent</string> - <string>StopVPNIntent</string> - <string>ReconnectVPNIntent</string> - </array> <key>ITSAppUsesNonExemptEncryption</key> <false/> <key>LSRequiresIPhoneOS</key> diff --git a/ios/MullvadVPN/IntentHandlers.swift b/ios/MullvadVPN/IntentHandlers.swift deleted file mode 100644 index 434f09b8fe..0000000000 --- a/ios/MullvadVPN/IntentHandlers.swift +++ /dev/null @@ -1,89 +0,0 @@ -// -// IntentHandlers.swift -// MullvadVPN -// -// Created by pronebird on 26/05/2022. -// Copyright © 2022 Mullvad VPN AB. All rights reserved. -// - -import Foundation - -final class StartVPNIntentHandler: NSObject, StartVPNIntentHandling { - private let tunnelManager: TunnelManager - - init(tunnelManager: TunnelManager) { - self.tunnelManager = tunnelManager - } - - func handle(intent: StartVPNIntent, completion: @escaping (StartVPNIntentResponse) -> Void) { - tunnelManager.startTunnel { operationCompletion in - let code: StartVPNIntentResponseCode = operationCompletion.isSuccess - ? .success - : .failure - let response = StartVPNIntentResponse(code: code, userActivity: nil) - - completion(response) - } - } -} - -final class StopVPNIntentHandler: NSObject, StopVPNIntentHandling { - private let tunnelManager: TunnelManager - - init(tunnelManager: TunnelManager) { - self.tunnelManager = tunnelManager - } - - func handle(intent: StopVPNIntent, completion: @escaping (StopVPNIntentResponse) -> Void) { - tunnelManager.stopTunnel { operationCompletion in - let code: StopVPNIntentResponseCode = operationCompletion.isSuccess - ? .success - : .failure - let response = StopVPNIntentResponse(code: code, userActivity: nil) - - completion(response) - } - } -} - -final class ReconnectVPNIntentHandler: NSObject, ReconnectVPNIntentHandling { - private let tunnelManager: TunnelManager - - init(tunnelManager: TunnelManager) { - self.tunnelManager = tunnelManager - } - - func handle( - intent: ReconnectVPNIntent, - completion: @escaping (ReconnectVPNIntentResponse) -> Void - ) { - tunnelManager.reconnectTunnel(selectNewRelay: true) { operationCompletion in - let error = operationCompletion.error - - let shouldStartTunnel: Bool - if case .tunnelDown = error as? SendTunnelProviderMessageError { - shouldStartTunnel = true - } else { - shouldStartTunnel = error is UnsetTunnelError - } - - if shouldStartTunnel { - self.tunnelManager.startTunnel { operationCompletion in - completion( - ReconnectVPNIntentResponse( - code: operationCompletion.isSuccess ? .success : .failure, - userActivity: nil - ) - ) - } - } else { - completion( - ReconnectVPNIntentResponse( - code: operationCompletion.isSuccess ? .success : .failure, - userActivity: nil - ) - ) - } - } - } -} diff --git a/ios/MullvadVPN/Intents.intentdefinition b/ios/MullvadVPN/Intents.intentdefinition deleted file mode 100644 index c05dda03cc..0000000000 --- a/ios/MullvadVPN/Intents.intentdefinition +++ /dev/null @@ -1,163 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>INEnums</key> - <array/> - <key>INIntentDefinitionModelVersion</key> - <string>1.2</string> - <key>INIntentDefinitionNamespace</key> - <string>5pIysl</string> - <key>INIntentDefinitionSystemVersion</key> - <string>21F79</string> - <key>INIntentDefinitionToolsBuildVersion</key> - <string>13F100</string> - <key>INIntentDefinitionToolsVersion</key> - <string>13.4.1</string> - <key>INIntents</key> - <array> - <dict> - <key>INIntentCategory</key> - <string>generic</string> - <key>INIntentConfigurable</key> - <true/> - <key>INIntentDescriptionID</key> - <string>jBv2Ko</string> - <key>INIntentIneligibleForSuggestions</key> - <true/> - <key>INIntentManagedParameterCombinations</key> - <dict> - <key></key> - <dict> - <key>INIntentParameterCombinationSupportsBackgroundExecution</key> - <true/> - <key>INIntentParameterCombinationUpdatesLinked</key> - <true/> - </dict> - </dict> - <key>INIntentName</key> - <string>StartVPN</string> - <key>INIntentResponse</key> - <dict> - <key>INIntentResponseCodes</key> - <array> - <dict> - <key>INIntentResponseCodeName</key> - <string>success</string> - <key>INIntentResponseCodeSuccess</key> - <true/> - </dict> - <dict> - <key>INIntentResponseCodeName</key> - <string>failure</string> - </dict> - </array> - <key>INIntentResponseLastParameterTag</key> - <integer>1</integer> - </dict> - <key>INIntentTitle</key> - <string>Start VPN</string> - <key>INIntentTitleID</key> - <string>EpurV0</string> - <key>INIntentType</key> - <string>Custom</string> - <key>INIntentVerb</key> - <string>Do</string> - </dict> - <dict> - <key>INIntentCategory</key> - <string>generic</string> - <key>INIntentConfigurable</key> - <true/> - <key>INIntentDescriptionID</key> - <string>uwGwEw</string> - <key>INIntentIneligibleForSuggestions</key> - <true/> - <key>INIntentManagedParameterCombinations</key> - <dict> - <key></key> - <dict> - <key>INIntentParameterCombinationSupportsBackgroundExecution</key> - <true/> - <key>INIntentParameterCombinationUpdatesLinked</key> - <true/> - </dict> - </dict> - <key>INIntentName</key> - <string>StopVPN</string> - <key>INIntentResponse</key> - <dict> - <key>INIntentResponseCodes</key> - <array> - <dict> - <key>INIntentResponseCodeName</key> - <string>success</string> - <key>INIntentResponseCodeSuccess</key> - <true/> - </dict> - <dict> - <key>INIntentResponseCodeName</key> - <string>failure</string> - </dict> - </array> - </dict> - <key>INIntentTitle</key> - <string>Stop VPN</string> - <key>INIntentTitleID</key> - <string>4GnhAo</string> - <key>INIntentType</key> - <string>Custom</string> - <key>INIntentVerb</key> - <string>Do</string> - </dict> - <dict> - <key>INIntentCategory</key> - <string>generic</string> - <key>INIntentConfigurable</key> - <true/> - <key>INIntentDescriptionID</key> - <string>SPC7AD</string> - <key>INIntentIneligibleForSuggestions</key> - <true/> - <key>INIntentManagedParameterCombinations</key> - <dict> - <key></key> - <dict> - <key>INIntentParameterCombinationSupportsBackgroundExecution</key> - <true/> - <key>INIntentParameterCombinationUpdatesLinked</key> - <true/> - </dict> - </dict> - <key>INIntentName</key> - <string>ReconnectVPN</string> - <key>INIntentResponse</key> - <dict> - <key>INIntentResponseCodes</key> - <array> - <dict> - <key>INIntentResponseCodeName</key> - <string>success</string> - <key>INIntentResponseCodeSuccess</key> - <true/> - </dict> - <dict> - <key>INIntentResponseCodeName</key> - <string>failure</string> - </dict> - </array> - </dict> - <key>INIntentTitle</key> - <string>Reconnect VPN</string> - <key>INIntentTitleID</key> - <string>puJvsb</string> - <key>INIntentType</key> - <string>Custom</string> - <key>INIntentVerb</key> - <string>Do</string> - </dict> - </array> - <key>INTypes</key> - <array/> -</dict> -</plist> diff --git a/ios/MullvadVPN/SceneDelegate.swift b/ios/MullvadVPN/SceneDelegate.swift index 03f6bc3674..b022aa2772 100644 --- a/ios/MullvadVPN/SceneDelegate.swift +++ b/ios/MullvadVPN/SceneDelegate.swift @@ -155,8 +155,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, UISplitViewControllerDe ) } - ShortcutsManager.shared.updateVoiceShortcuts() - setShowsPrivacyOverlay(false) } diff --git a/ios/MullvadVPN/SettingsDataSource.swift b/ios/MullvadVPN/SettingsDataSource.swift index ed4bf23c3d..e2510baf20 100644 --- a/ios/MullvadVPN/SettingsDataSource.swift +++ b/ios/MullvadVPN/SettingsDataSource.swift @@ -43,7 +43,6 @@ final class SettingsDataSource: NSObject, UITableViewDataSource, UITableViewDele enum Item: String { case account case preferences - case shortcuts case version case problemReport case faq @@ -98,7 +97,7 @@ final class SettingsDataSource: NSObject, UITableViewDataSource, UITableViewDele if interactor.deviceState.isLoggedIn { newSnapshot.appendSections([.main]) - newSnapshot.appendItems([.account, .preferences, .shortcuts], in: .main) + newSnapshot.appendItems([.account, .preferences], in: .main) } newSnapshot.appendSections([.version, .problemReport]) @@ -158,23 +157,6 @@ final class SettingsDataSource: NSObject, UITableViewDataSource, UITableViewDele return cell - case .shortcuts: - let cell = tableView.dequeueReusableCell( - withIdentifier: CellReuseIdentifiers.basicCell.rawValue, - for: indexPath - ) as! SettingsCell - cell.titleLabel.text = NSLocalizedString( - "SHORTCUTS_CELL_LABEL", - tableName: "Settings", - value: "Shortcuts", - comment: "" - ) - cell.detailTitleLabel.text = nil - cell.accessibilityIdentifier = nil - cell.disclosureType = .chevron - - return cell - case .version: let cell = tableView.dequeueReusableCell( withIdentifier: CellReuseIdentifiers.basicCell.rawValue, diff --git a/ios/MullvadVPN/SettingsNavigationController.swift b/ios/MullvadVPN/SettingsNavigationController.swift index 6656eb2acb..c43da954cc 100644 --- a/ios/MullvadVPN/SettingsNavigationController.swift +++ b/ios/MullvadVPN/SettingsNavigationController.swift @@ -12,7 +12,6 @@ enum SettingsNavigationRoute { case root case account case preferences - case shortcuts case problemReport } @@ -123,9 +122,6 @@ class SettingsNavigationController: CustomNavigationController, SettingsViewCont interactor: interactorFactory.makePreferencesInteractor() ) - case .shortcuts: - return ShortcutsViewController() - case .problemReport: return ProblemReportViewController( interactor: interactorFactory.makeProblemReportInteractor() diff --git a/ios/MullvadVPN/SettingsViewController.swift b/ios/MullvadVPN/SettingsViewController.swift index 3f5ce245df..2dd09d4d20 100644 --- a/ios/MullvadVPN/SettingsViewController.swift +++ b/ios/MullvadVPN/SettingsViewController.swift @@ -104,8 +104,6 @@ extension SettingsDataSource.Item { return .account case .preferences: return .preferences - case .shortcuts: - return .shortcuts case .version: return nil case .problemReport: diff --git a/ios/MullvadVPN/ShortcutsDataSource.swift b/ios/MullvadVPN/ShortcutsDataSource.swift deleted file mode 100644 index 429d196246..0000000000 --- a/ios/MullvadVPN/ShortcutsDataSource.swift +++ /dev/null @@ -1,234 +0,0 @@ -// -// ShortcutsDataSource.swift -// MullvadVPN -// -// Created by Nikolay Davydov on 20.08.2022. -// Copyright © 2022 Mullvad VPN AB. All rights reserved. -// - -import IntentsUI -import UIKit - -final class ShortcutsDataSource: NSObject, - UITableViewDataSource, - UITableViewDelegate, - ShortcutsManagerDelegate -{ - private enum CellReuseIdentifiers: String, CaseIterable { - case basicCell - - var reusableViewClass: AnyClass { - switch self { - case .basicCell: - return SettingsCell.self - } - } - } - - private enum HeaderFooterReuseIdentifier: String, CaseIterable { - case spacer - - var reusableViewClass: AnyClass { - switch self { - case .spacer: - return EmptyTableViewHeaderFooterView.self - } - } - } - - enum Section: String { - case shortcuts - } - - struct Item: Hashable { - let title: String - let shortcut: INShortcut - let voiceShortcut: INVoiceShortcut? - - var isAdded: Bool { - return voiceShortcut != nil - } - } - - private var snapshot = DataSourceSnapshot<Section, Item>() - - weak var delegate: ShortcutsDataSourceDelegate? - - weak var tableView: UITableView? { - didSet { - tableView?.delegate = self - tableView?.dataSource = self - - registerClasses() - } - } - - override init() { - super.init() - updateDataSnapshot(voiceShortcuts: []) - ShortcutsManager.shared.delegate = self - ShortcutsManager.shared.updateVoiceShortcuts() - } - - private func registerClasses() { - CellReuseIdentifiers.allCases.forEach { cellIdentifier in - tableView?.register( - cellIdentifier.reusableViewClass, - forCellReuseIdentifier: cellIdentifier.rawValue - ) - } - - HeaderFooterReuseIdentifier.allCases.forEach { reuseIdentifier in - tableView?.register( - reuseIdentifier.reusableViewClass, - forHeaderFooterViewReuseIdentifier: reuseIdentifier.rawValue - ) - } - } - - private func updateDataSnapshot(voiceShortcuts: [INVoiceShortcut]) { - var items = [Item]() - - for data in ShortcutData.allCases { - guard let shortcut = data.shortcut else { continue } - let voiceShortcut = voiceShortcuts.first(where: { voiceShortcut in - isVoiceShortcut(voiceShortcut, invokes: shortcut) - }) - let item = Item( - title: data.title, - shortcut: shortcut, - voiceShortcut: voiceShortcut - ) - items.append(item) - } - - var newSnapshot = DataSourceSnapshot<Section, Item>() - newSnapshot.appendSections([.shortcuts]) - newSnapshot.appendItems(items, in: .shortcuts) - - snapshot = newSnapshot - } - - /// Returns whether the voice shortcut performs the same action as the specified shortcut. - private func isVoiceShortcut( - _ voiceShortcut: INVoiceShortcut, - invokes shortcut: INShortcut - ) -> Bool { - if let a = voiceShortcut.shortcut.intent, let b = shortcut.intent { - return type(of: a) == type(of: b) - } - return false - } - - // MARK: - UITableViewDataSource - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - let sectionIdentifier = snapshot.section(at: section)! - return snapshot.numberOfItems(in: sectionIdentifier) ?? 0 - } - - func numberOfSections(in tableView: UITableView) -> Int { - return snapshot.numberOfSections() - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let item = snapshot.itemForIndexPath(indexPath)! - let cell = tableView.dequeueReusableCell( - withIdentifier: CellReuseIdentifiers.basicCell.rawValue, - for: indexPath - ) - if let cell = cell as? SettingsCell { - cell.titleLabel.text = item.title - cell.disclosureType = item.isAdded ? .tick : .none - } - return cell - } - - // MARK: - UITableViewDelegate - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - guard let item = snapshot.itemForIndexPath(indexPath) else { return } - delegate?.shortcutsDataSource(self, didSelectItem: item) - tableView.deselectRow(at: indexPath, animated: true) - } - - func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - return tableView.dequeueReusableHeaderFooterView( - withIdentifier: HeaderFooterReuseIdentifier.spacer.rawValue - ) - } - - func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { - return nil - } - - func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { - return UIMetrics.sectionSpacing - } - - func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { - return 0 - } - - // MARK: - ShortcutsManagerDelegate - - func shortcutsManager( - _ shortcutsManager: ShortcutsManager, - didReceiveVoiceShortcuts voiceShortcuts: [INVoiceShortcut] - ) { - updateDataSnapshot(voiceShortcuts: voiceShortcuts) - tableView?.reloadData() - } -} - -private extension ShortcutsDataSource { - enum ShortcutData: CaseIterable { - case start - case reconnect - case stop - - var title: String { - switch self { - case .start: - return NSLocalizedString( - "SHORTCUTS_NAME_START_VPN", - tableName: "Shortcuts", - value: "Start VPN", - comment: "" - ) - case .reconnect: - return NSLocalizedString( - "SHORTCUTS_NAME_RECONNECT_VPN", - tableName: "Shortcuts", - value: "Reconnect VPN", - comment: "" - ) - case .stop: - return NSLocalizedString( - "SHORTCUTS_NAME_STOP_VPN", - tableName: "Shortcuts", - value: "Stop VPN", - comment: "" - ) - } - } - - var shortcut: INShortcut? { - let intent: INIntent - switch self { - case .start: - intent = StartVPNIntent() - case .reconnect: - intent = ReconnectVPNIntent() - case .stop: - intent = StopVPNIntent() - } - intent.suggestedInvocationPhrase = title - guard let shortcut = INShortcut(intent: intent) else { - assertionFailure("The shortcut has an invalid intent.") - return nil - } - return shortcut - } - } -} diff --git a/ios/MullvadVPN/ShortcutsDataSourceDelegate.swift b/ios/MullvadVPN/ShortcutsDataSourceDelegate.swift deleted file mode 100644 index 2fde666721..0000000000 --- a/ios/MullvadVPN/ShortcutsDataSourceDelegate.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// ShortcutsDataSourceDelegate.swift -// MullvadVPN -// -// Created by Nikolay Davydov on 20.08.2022. -// Copyright © 2022 Mullvad VPN AB. All rights reserved. -// - -protocol ShortcutsDataSourceDelegate: AnyObject { - func shortcutsDataSource( - _ dataSource: ShortcutsDataSource, - didSelectItem item: ShortcutsDataSource.Item - ) -} diff --git a/ios/MullvadVPN/ShortcutsManager.swift b/ios/MullvadVPN/ShortcutsManager.swift deleted file mode 100644 index a991c38c65..0000000000 --- a/ios/MullvadVPN/ShortcutsManager.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// ShortcutsManager.swift -// MullvadVPN -// -// Created by Nikolay Davydov on 23.08.2022. -// Copyright © 2022 Mullvad VPN AB. All rights reserved. -// - -import IntentsUI -import MullvadLogging - -protocol ShortcutsManagerDelegate: AnyObject { - func shortcutsManager( - _ shortcutsManager: ShortcutsManager, - didReceiveVoiceShortcuts voiceShortcuts: [INVoiceShortcut] - ) -} - -final class ShortcutsManager { - static let shared = ShortcutsManager() - - private init() {} - - private let logger = Logger(label: "ShortcutsManager") - - private var voiceShortcutsByID = [UUID: INVoiceShortcut]() { - didSet { - let voiceShortcuts = voiceShortcutsByID.map { $0.value } - delegate?.shortcutsManager(self, didReceiveVoiceShortcuts: voiceShortcuts) - } - } - - weak var delegate: ShortcutsManagerDelegate? - - func updateVoiceShortcuts() { - guard delegate != nil else { return } - INVoiceShortcutCenter.shared.getAllVoiceShortcuts { [weak self] voiceShortcuts, error in - guard let self = self else { return } - if let error = error { - self.logger.error( - error: error, - message: "Failed to fetch voice shortcuts." - ) - return - } - let voiceShortcuts = voiceShortcuts ?? [] - let voiceShortcutsByID = voiceShortcuts - .reduce(into: [UUID: INVoiceShortcut]()) { result, voiceShortcut in - result[voiceShortcut.identifier] = voiceShortcut - } - DispatchQueue.main.async { - self.voiceShortcutsByID = voiceShortcutsByID - } - } - } - - func addVoiceShortcut(_ voiceShortcut: INVoiceShortcut) { - voiceShortcutsByID[voiceShortcut.identifier] = voiceShortcut - } - - func deleteVoiceShortcut(withIdentifier identifier: UUID) { - voiceShortcutsByID[identifier] = nil - } -} diff --git a/ios/MullvadVPN/ShortcutsViewController.swift b/ios/MullvadVPN/ShortcutsViewController.swift deleted file mode 100644 index 953639dca3..0000000000 --- a/ios/MullvadVPN/ShortcutsViewController.swift +++ /dev/null @@ -1,116 +0,0 @@ -// -// ShortcutsViewController.swift -// MullvadVPN -// -// Created by Nikolay Davydov on 20.08.2022. -// Copyright © 2022 Mullvad VPN AB. All rights reserved. -// - -import IntentsUI -import UIKit - -final class ShortcutsViewController: UITableViewController, - ShortcutsDataSourceDelegate, - INUIAddVoiceShortcutViewControllerDelegate, - INUIEditVoiceShortcutViewControllerDelegate -{ - private let dataSource = ShortcutsDataSource() - - override var preferredStatusBarStyle: UIStatusBarStyle { - return .lightContent - } - - init() { - super.init(style: .grouped) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - - tableView.backgroundColor = .secondaryColor - tableView.separatorColor = .secondaryColor - tableView.rowHeight = UITableView.automaticDimension - tableView.estimatedRowHeight = 60 - - dataSource.tableView = tableView - dataSource.delegate = self - - navigationItem.title = NSLocalizedString( - "NAVIGATION_TITLE", - tableName: "Shortcuts", - value: "Shortcuts", - comment: "" - ) - } - - // MARK: - ShortcutsDataSourceDelegate - - func shortcutsDataSource( - _ dataSource: ShortcutsDataSource, - didSelectItem item: ShortcutsDataSource.Item - ) { - let controller: UIViewController - if let voiceShortcut = item.voiceShortcut { - let editShortcutController = INUIEditVoiceShortcutViewController( - voiceShortcut: voiceShortcut - ) - editShortcutController.delegate = self - controller = editShortcutController - } else { - let addShortcutController = INUIAddVoiceShortcutViewController( - shortcut: item.shortcut - ) - addShortcutController.delegate = self - controller = addShortcutController - } - controller.modalPresentationStyle = .formSheet - present(controller, animated: true) - } - - // MARK: - INUIAddVoiceShortcutViewControllerDelegate - - func addVoiceShortcutViewController( - _ controller: INUIAddVoiceShortcutViewController, - didFinishWith voiceShortcut: INVoiceShortcut?, - error: Error? - ) { - if let voiceShortcut = voiceShortcut { - ShortcutsManager.shared.addVoiceShortcut(voiceShortcut) - } - controller.dismiss(animated: true) - } - - func addVoiceShortcutViewControllerDidCancel(_ controller: INUIAddVoiceShortcutViewController) { - controller.dismiss(animated: true) - } - - // MARK: - INUIEditVoiceShortcutViewControllerDelegate - - func editVoiceShortcutViewController( - _ controller: INUIEditVoiceShortcutViewController, - didUpdate voiceShortcut: INVoiceShortcut?, - error: Error? - ) { - controller.dismiss(animated: true) - } - - func editVoiceShortcutViewController( - _ controller: INUIEditVoiceShortcutViewController, - didDeleteVoiceShortcutWithIdentifier deletedVoiceShortcutIdentifier: UUID - ) { - ShortcutsManager.shared.deleteVoiceShortcut( - withIdentifier: deletedVoiceShortcutIdentifier - ) - controller.dismiss(animated: true) - } - - func editVoiceShortcutViewControllerDidCancel( - _ controller: INUIEditVoiceShortcutViewController - ) { - controller.dismiss(animated: true) - } -} |
