diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2020-07-30 10:10:17 +0300 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2020-07-30 10:10:17 +0300 |
| commit | 43110ce2e27fcda977458469005921bec788a1de (patch) | |
| tree | 1c976d19b58e9a6d57e4673bfd9e1fa76ac0bd98 | |
| parent | ca590bb989cea195b67208dc49da9419da14e9a1 (diff) | |
| parent | 437dcd524b8285735c9de3f3ce616d10614adc85 (diff) | |
| download | mullvadvpn-43110ce2e27fcda977458469005921bec788a1de.tar.xz mullvadvpn-43110ce2e27fcda977458469005921bec788a1de.zip | |
Merge branch 'add-reconnect-button'
31 files changed, 573 insertions, 444 deletions
diff --git a/ios/AdditionalAssets/TranslucentDangerSplitLeftButton.svg b/ios/AdditionalAssets/TranslucentDangerSplitLeftButton.svg new file mode 100644 index 0000000000..ef4210291e --- /dev/null +++ b/ios/AdditionalAssets/TranslucentDangerSplitLeftButton.svg @@ -0,0 +1,3 @@ +<svg viewBox="0 0 44 44" xmlns="http://www.w3.org/2000/svg"> + <path d="M4 0 L44 0 Q44 0 44 0 L44 44 Q44 44 44 44 L4 44 Q0 44 0 40 L0 4 Q0 0 4 0 Z" fill="rgba(227, 64, 57, 0.40)"></path> +</svg> diff --git a/ios/AdditionalAssets/TranslucentDangerSplitRightButton.svg b/ios/AdditionalAssets/TranslucentDangerSplitRightButton.svg new file mode 100644 index 0000000000..12e8e1442e --- /dev/null +++ b/ios/AdditionalAssets/TranslucentDangerSplitRightButton.svg @@ -0,0 +1,3 @@ +<svg viewBox="0 0 44 44" xmlns="http://www.w3.org/2000/svg"> + <path d="M0 0 L40 0 Q44 0 44 4 L44 40 Q44 44 40 44 L0 44 Q0 44 0 44 L0 0 Q0 0 0 0 Z" fill="rgba(227, 64, 57, 0.40)"></path> +</svg> diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index fccf62bda3..13cb39d70b 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -24,6 +24,7 @@ Line wrap the file at 100 chars. Th ## [Unreleased] ### Added +- Add button to reconnect the tunnel. - Add support for iOS 12. - Ship the initial relay list with the app, and do once an hour periodic refresh in background. - Refresh account expiry when visiting settings. diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index dc01442a6d..0c188fa520 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -54,7 +54,6 @@ 5840250222B1124600E4CFEC /* IpAddress+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5840250022B1124600E4CFEC /* IpAddress+Codable.swift */; }; 5840250422B11AB700E4CFEC /* MullvadEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5840250322B11AB700E4CFEC /* MullvadEndpoint.swift */; }; 5840250522B11AB700E4CFEC /* MullvadEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5840250322B11AB700E4CFEC /* MullvadEndpoint.swift */; }; - 5845F838236C466400B2D93C /* TunnelControlViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5845F837236C466400B2D93C /* TunnelControlViewController.swift */; }; 5845F842236CBACD00B2D93C /* PacketTunnelIpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5845F841236CBACD00B2D93C /* PacketTunnelIpc.swift */; }; 5845F843236CBDAB00B2D93C /* PacketTunnelIpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5845F841236CBACD00B2D93C /* PacketTunnelIpc.swift */; }; 584E96BC240FD4DA00D3334F /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A1AA8623F43901009F7EA6 /* Location.swift */; }; @@ -106,6 +105,8 @@ 5888AD87227B17950051EB06 /* SelectLocationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5888AD86227B17950051EB06 /* SelectLocationController.swift */; }; 588D2FE3248AC27F00E313F7 /* AsyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E973DD24850EB600096F90 /* AsyncOperation.swift */; }; 58906DE02445C7A5002F0673 /* NEProviderStopReason+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58906DDF2445C7A5002F0673 /* NEProviderStopReason+Debug.swift */; }; + 58907D9324D167B400CFC3F5 /* DisconnectSplitButton.xib in Resources */ = {isa = PBXBuildFile; fileRef = 58907D9224D167B400CFC3F5 /* DisconnectSplitButton.xib */; }; + 58907D9524D17B4E00CFC3F5 /* DisconnectSplitButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58907D9424D17B4E00CFC3F5 /* DisconnectSplitButton.swift */; }; 5896AE7E246ACE65005B36CB /* KeychainAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEDEB245059F000CB0F5B /* KeychainAttributes.swift */; }; 5896AE7F246ACE76005B36CB /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEDF6245088E100CB0F5B /* Keychain.swift */; }; 5896AE80246ACE79005B36CB /* KeychainClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FAEE0024533A9C00CB0F5B /* KeychainClass.swift */; }; @@ -169,7 +170,6 @@ 58D0C7A223F1CECF00FE9BA7 /* MullvadVPNScreenshots.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D0C7A023F1CECF00FE9BA7 /* MullvadVPNScreenshots.swift */; }; 58DF28A52417CB4B00E836B0 /* AppStorePaymentManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DF28A42417CB4B00E836B0 /* AppStorePaymentManager.swift */; }; 58E6771F24ADFE7800AA26E7 /* SettingsNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E6771E24ADFE7800AA26E7 /* SettingsNavigationController.swift */; }; - 58EC4E6C23915325003F5C5B /* Bundle+MullvadVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EC4E6B23915325003F5C5B /* Bundle+MullvadVersion.swift */; }; 58F19E35228C15BA00C7710B /* SpinnerActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F19E34228C15BA00C7710B /* SpinnerActivityIndicatorView.swift */; }; 58F3C0962492617E003E76BE /* AsyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E973DD24850EB600096F90 /* AsyncOperation.swift */; }; 58F3C099249B978C003E76BE /* x25519.c in Sources */ = {isa = PBXBuildFile; fileRef = 58F3C098249B978C003E76BE /* x25519.c */; }; @@ -270,7 +270,6 @@ 5835B7CB233B76CB0096D79F /* TunnelManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelManager.swift; sourceTree = "<group>"; }; 5840250022B1124600E4CFEC /* IpAddress+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IpAddress+Codable.swift"; sourceTree = "<group>"; }; 5840250322B11AB700E4CFEC /* MullvadEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MullvadEndpoint.swift; sourceTree = "<group>"; }; - 5845F837236C466400B2D93C /* TunnelControlViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelControlViewController.swift; sourceTree = "<group>"; }; 5845F841236CBACD00B2D93C /* PacketTunnelIpc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelIpc.swift; sourceTree = "<group>"; }; 584B26F3237434D00073B10E /* RelaySelectorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelaySelectorTests.swift; sourceTree = "<group>"; }; 58561C98239A5D1500BD6B5E /* IPEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPEndpoint.swift; sourceTree = "<group>"; }; @@ -300,6 +299,8 @@ 5888AD82227B11080051EB06 /* SelectLocationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectLocationCell.swift; sourceTree = "<group>"; }; 5888AD86227B17950051EB06 /* SelectLocationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectLocationController.swift; sourceTree = "<group>"; }; 58906DDF2445C7A5002F0673 /* NEProviderStopReason+Debug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NEProviderStopReason+Debug.swift"; sourceTree = "<group>"; }; + 58907D9224D167B400CFC3F5 /* DisconnectSplitButton.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DisconnectSplitButton.xib; sourceTree = "<group>"; }; + 58907D9424D17B4E00CFC3F5 /* DisconnectSplitButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisconnectSplitButton.swift; sourceTree = "<group>"; }; 5894E725236B2801008A2793 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; 5896AE83246D5889005B36CB /* CustomDateComponentsFormatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDateComponentsFormatting.swift; sourceTree = "<group>"; }; 5896AE85246D6AD8005B36CB /* CustomDateComponentsFormattingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDateComponentsFormattingTests.swift; sourceTree = "<group>"; }; @@ -351,7 +352,6 @@ 58DF28A42417CB4B00E836B0 /* AppStorePaymentManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStorePaymentManager.swift; sourceTree = "<group>"; }; 58E6771E24ADFE7800AA26E7 /* SettingsNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsNavigationController.swift; sourceTree = "<group>"; }; 58E973DD24850EB600096F90 /* AsyncOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncOperation.swift; sourceTree = "<group>"; }; - 58EC4E6B23915325003F5C5B /* Bundle+MullvadVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+MullvadVersion.swift"; sourceTree = "<group>"; }; 58ECD29123F178FD004298B6 /* Screenshots.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Screenshots.xcconfig; sourceTree = "<group>"; }; 58F19E34228C15BA00C7710B /* SpinnerActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpinnerActivityIndicatorView.swift; sourceTree = "<group>"; }; 58F3C097249B978C003E76BE /* x25519.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = x25519.h; sourceTree = "<group>"; }; @@ -498,7 +498,6 @@ 58CE5E6A224146210008646E /* Assets.xcassets */, 588534BD246193C00018B744 /* AutomaticKeyRotationManager.swift */, 589AB4F6227B64450039131E /* BasicTableViewCell.swift */, - 58EC4E6B23915325003F5C5B /* Bundle+MullvadVersion.swift */, 58F840B12464491D0044E708 /* ChainedError.swift */, 58A1AA8B23F5584B009F7EA6 /* ConnectionPanelView.swift */, 58CCA00F224249A1004F3011 /* ConnectViewController.swift */, @@ -507,6 +506,8 @@ 5896AE83246D5889005B36CB /* CustomDateComponentsFormatting.swift */, 582BB1B0229569620055B6EF /* CustomNavigationBar.swift */, 58C6B35D22BBBFE3003C19AD /* Data+HexCoding.swift */, + 58907D9424D17B4E00CFC3F5 /* DisconnectSplitButton.swift */, + 58907D9224D167B400CFC3F5 /* DisconnectSplitButton.xib */, 58B9EB142489139B00095626 /* DisplayChainedError.swift */, 5873884C239E6D7E00E96C4E /* EmbeddedViewContainerView.swift */, 58F3C0A3249CB069003E76BE /* HeaderBarView.swift */, @@ -560,7 +561,6 @@ 581CBCED229826FD00727D7F /* StaticTableViewDataSource.swift */, 5807E2BF2432038B00F5FF30 /* String+Split.swift */, 5862805322428EF100F5A6E1 /* TranslucentButtonBlurView.swift */, - 5845F837236C466400B2D93C /* TunnelControlViewController.swift */, 5835B7CB233B76CB0096D79F /* TunnelManager.swift */, 587AD7C523421D7000E93A53 /* TunnelSettings.swift */, 58AEEF6A2344A46200C9BBD5 /* TunnelSettingsManager.swift */, @@ -798,6 +798,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 58907D9324D167B400CFC3F5 /* DisconnectSplitButton.xib in Resources */, 58F3C0A2249CA1E0003E76BE /* HeaderBarView.xib in Resources */, 58F3C0A624A50157003E76BE /* relays.json in Resources */, 58CE5E6E224146210008646E /* LaunchScreen.storyboard in Resources */, @@ -912,7 +913,6 @@ 58BA692E23E99EFF009DC256 /* Locking.swift in Sources */, 580EE21E24B3237F00F9D8A1 /* OutputOperation.swift in Sources */, 5840250122B1124600E4CFEC /* IpAddress+Codable.swift in Sources */, - 58EC4E6C23915325003F5C5B /* Bundle+MullvadVersion.swift in Sources */, 5857F24724C882D700CF6F47 /* SelectLocationNavigationController.swift in Sources */, 580EE21224B322FC00F9D8A1 /* ResultOperation.swift in Sources */, 58BA693123EADA6A009DC256 /* SimulatorTunnelProvider.swift in Sources */, @@ -933,12 +933,12 @@ 580EE20924B3224200F9D8A1 /* RetryOperation.swift in Sources */, 582AE3102440A6CA00E6733A /* AccountTokenInput.swift in Sources */, 58FAEDF7245088E100CB0F5B /* Keychain.swift in Sources */, + 58907D9524D17B4E00CFC3F5 /* DisconnectSplitButton.swift in Sources */, 5888AD87227B17950051EB06 /* SelectLocationController.swift in Sources */, 580EE20424B321EC00F9D8A1 /* OperationObserver.swift in Sources */, 58F19E35228C15BA00C7710B /* SpinnerActivityIndicatorView.swift in Sources */, 58A99ED3240014A0006599E9 /* ConsentViewController.swift in Sources */, 58FAEE0124533A9C00CB0F5B /* KeychainClass.swift in Sources */, - 5845F838236C466400B2D93C /* TunnelControlViewController.swift in Sources */, 58CCA0162242560B004F3011 /* UIColor+Palette.swift in Sources */, 58AEEF6B2344A46200C9BBD5 /* TunnelSettingsManager.swift in Sources */, 587CBFE322807F530028DED3 /* UIColor+Helpers.swift in Sources */, diff --git a/ios/MullvadVPN/AppButton.swift b/ios/MullvadVPN/AppButton.swift index 25eed30cf8..6a58100c25 100644 --- a/ios/MullvadVPN/AppButton.swift +++ b/ios/MullvadVPN/AppButton.swift @@ -98,6 +98,54 @@ private extension UIControl.State { /// A subclass that implements action buttons used across the app @IBDesignable class AppButton: CustomButton { + static let defaultContentInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) + + enum Style: Int { + case `default` + case danger + case success + case translucentDanger + case translucentNeutral + case translucentDangerSplitLeft + case translucentDangerSplitRight + + var backgroundImage: UIImage? { + switch self { + case .default: + return UIImage(named: "DefaultButton") + case .danger: + return UIImage(named: "DangerButton") + case .success: + return UIImage(named: "SuccessButton") + case .translucentDanger: + return UIImage(named: "TranslucentDangerButton") + case .translucentNeutral: + return UIImage(named: "TranslucentNeutralButton") + case .translucentDangerSplitLeft: + return UIImage(named: "TranslucentDangerSplitLeftButton") + case .translucentDangerSplitRight: + return UIImage(named: "TranslucentDangerSplitRightButton") + } + } + } + + var style: Style = .default { + didSet { + updateButtonBackground() + } + } + + @IBInspectable var interfaceBuilderStyle: Int { + get { + return self.style.rawValue + } + set { + if let style = Style(rawValue: newValue) { + self.style = style + } + } + } + override init(frame: CGRect) { super.init(frame: frame) commonInit() @@ -112,19 +160,19 @@ private extension UIControl.State { var contentInsets = contentEdgeInsets if contentInsets.top == 0 { - contentInsets.top = 10 + contentInsets.top = Self.defaultContentInsets.top } if contentInsets.bottom == 0 { - contentInsets.bottom = 10 + contentInsets.bottom = Self.defaultContentInsets.bottom } if contentInsets.right == 0 { - contentInsets.right = 10 + contentInsets.right = Self.defaultContentInsets.right } if contentInsets.left == 0 { - contentInsets.left = 10 + contentInsets.left = Self.defaultContentInsets.left } contentEdgeInsets = contentInsets @@ -138,6 +186,15 @@ private extension UIControl.State { setTitleColor(titleColor, for: state) } } + + // Avoid setting the background image if it's already set via Interface Builder + if backgroundImage(for: .normal) == nil { + updateButtonBackground() + } + } + + private func updateButtonBackground() { + setBackgroundImage(style.backgroundImage, for: .normal) } } diff --git a/ios/MullvadVPN/Assets.xcassets/DangerButton.imageset/DangerButton.pdf b/ios/MullvadVPN/Assets.xcassets/DangerButton.imageset/DangerButton.pdf Binary files differindex c1f95daf3b..7350298c29 100644 --- a/ios/MullvadVPN/Assets.xcassets/DangerButton.imageset/DangerButton.pdf +++ b/ios/MullvadVPN/Assets.xcassets/DangerButton.imageset/DangerButton.pdf diff --git a/ios/MullvadVPN/Assets.xcassets/DefaultButton.imageset/DefaultButton.pdf b/ios/MullvadVPN/Assets.xcassets/DefaultButton.imageset/DefaultButton.pdf Binary files differindex efae69e9a3..0bf94571f6 100644 --- a/ios/MullvadVPN/Assets.xcassets/DefaultButton.imageset/DefaultButton.pdf +++ b/ios/MullvadVPN/Assets.xcassets/DefaultButton.imageset/DefaultButton.pdf diff --git a/ios/MullvadVPN/Assets.xcassets/IconReload.imageset/Contents.json b/ios/MullvadVPN/Assets.xcassets/IconReload.imageset/Contents.json new file mode 100644 index 0000000000..ff6e723432 --- /dev/null +++ b/ios/MullvadVPN/Assets.xcassets/IconReload.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "IconReload.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/ios/MullvadVPN/Assets.xcassets/IconReload.imageset/IconReload.pdf b/ios/MullvadVPN/Assets.xcassets/IconReload.imageset/IconReload.pdf new file mode 100644 index 0000000000..dd17312eec --- /dev/null +++ b/ios/MullvadVPN/Assets.xcassets/IconReload.imageset/IconReload.pdf @@ -0,0 +1,71 @@ +%PDF-1.5 +% +4 0 obj +<< /Length 5 0 R + /Filter /FlateDecode +>> +stream +xURKNE1wl@,p %9PxS(pIQ}1_'FEd3$Y)WkJ.^t#_S|rt'OdzW(t}.H܆"atk~kmۖyXMk7BqH)pjgH +lBRT,hseXJ֙%P/y#\ͯYR[<<4Ɓ(d>n3<q5M!V=)7Za +h0ȵp?~p{80 +i)M kdq!wma*lE!uqIA +endstream +endobj +5 0 obj + 360 +endobj +3 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +2 0 obj +<< /Type /Page % 1 + /Parent 1 0 R + /MediaBox [ 0 0 512 512 ] + /Contents 4 0 R + /Group << + /Type /Group + /S /Transparency + /I true + /CS /DeviceRGB + >> + /Resources 3 0 R +>> +endobj +1 0 obj +<< /Type /Pages + /Kids [ 2 0 R ] + /Count 1 +>> +endobj +6 0 obj +<< /Producer (cairo 1.16.0 (https://cairographics.org)) + /CreationDate (D:20200723135151+03'00) +>> +endobj +7 0 obj +<< /Type /Catalog + /Pages 1 0 R +>> +endobj +xref +0 8 +0000000000 65535 f +0000000764 00000 n +0000000546 00000 n +0000000474 00000 n +0000000015 00000 n +0000000452 00000 n +0000000829 00000 n +0000000945 00000 n +trailer +<< /Size 8 + /Root 7 0 R + /Info 6 0 R +>> +startxref +997 +%%EOF diff --git a/ios/MullvadVPN/Assets.xcassets/SuccessButton.imageset/Contents.json b/ios/MullvadVPN/Assets.xcassets/SuccessButton.imageset/Contents.json index 37fe41d860..1c5bb6e4fc 100644 --- a/ios/MullvadVPN/Assets.xcassets/SuccessButton.imageset/Contents.json +++ b/ios/MullvadVPN/Assets.xcassets/SuccessButton.imageset/Contents.json @@ -1,26 +1,26 @@ { "images" : [ { - "idiom" : "universal", "filename" : "SuccessButton.pdf", + "idiom" : "universal", "resizing" : { - "mode" : "9-part", - "center" : { - "mode" : "tile", - "width" : 1, - "height" : 1 - }, "cap-insets" : { "bottom" : 4, - "top" : 4, + "left" : 4, "right" : 4, - "left" : 4 - } + "top" : 4 + }, + "center" : { + "height" : 1, + "mode" : "tile", + "width" : 1 + }, + "mode" : "9-part" } } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -}
\ No newline at end of file +} diff --git a/ios/MullvadVPN/Assets.xcassets/SuccessButton.imageset/SuccessButton.pdf b/ios/MullvadVPN/Assets.xcassets/SuccessButton.imageset/SuccessButton.pdf Binary files differindex fb318000c5..005ed8e333 100644 --- a/ios/MullvadVPN/Assets.xcassets/SuccessButton.imageset/SuccessButton.pdf +++ b/ios/MullvadVPN/Assets.xcassets/SuccessButton.imageset/SuccessButton.pdf diff --git a/ios/MullvadVPN/Assets.xcassets/TranslucentDangerButton.imageset/TranslucentDangerButton.pdf b/ios/MullvadVPN/Assets.xcassets/TranslucentDangerButton.imageset/TranslucentDangerButton.pdf Binary files differindex dc8dc8d705..dd63aaa23c 100644 --- a/ios/MullvadVPN/Assets.xcassets/TranslucentDangerButton.imageset/TranslucentDangerButton.pdf +++ b/ios/MullvadVPN/Assets.xcassets/TranslucentDangerButton.imageset/TranslucentDangerButton.pdf diff --git a/ios/MullvadVPN/Assets.xcassets/TranslucentDangerSplitLeftButton.imageset/Contents.json b/ios/MullvadVPN/Assets.xcassets/TranslucentDangerSplitLeftButton.imageset/Contents.json new file mode 100644 index 0000000000..3ffcbb4089 --- /dev/null +++ b/ios/MullvadVPN/Assets.xcassets/TranslucentDangerSplitLeftButton.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "TranslucentDangerSplitLeftButton.pdf", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "bottom" : 3, + "left" : 21, + "right" : 22, + "top" : 3 + }, + "center" : { + "height" : 1, + "mode" : "tile", + "width" : 1 + }, + "mode" : "9-part" + } + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/MullvadVPN/Assets.xcassets/TranslucentDangerSplitLeftButton.imageset/TranslucentDangerSplitLeftButton.pdf b/ios/MullvadVPN/Assets.xcassets/TranslucentDangerSplitLeftButton.imageset/TranslucentDangerSplitLeftButton.pdf Binary files differnew file mode 100644 index 0000000000..8a9ca176c1 --- /dev/null +++ b/ios/MullvadVPN/Assets.xcassets/TranslucentDangerSplitLeftButton.imageset/TranslucentDangerSplitLeftButton.pdf diff --git a/ios/MullvadVPN/Assets.xcassets/TranslucentDangerSplitRightButton.imageset/Contents.json b/ios/MullvadVPN/Assets.xcassets/TranslucentDangerSplitRightButton.imageset/Contents.json new file mode 100644 index 0000000000..560e715aae --- /dev/null +++ b/ios/MullvadVPN/Assets.xcassets/TranslucentDangerSplitRightButton.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "TranslucentDangerSplitRightButton.pdf", + "idiom" : "universal", + "resizing" : { + "cap-insets" : { + "bottom" : 3, + "left" : 21, + "right" : 22, + "top" : 3 + }, + "center" : { + "height" : 1, + "mode" : "tile", + "width" : 1 + }, + "mode" : "9-part" + } + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/MullvadVPN/Assets.xcassets/TranslucentDangerSplitRightButton.imageset/TranslucentDangerSplitRightButton.pdf b/ios/MullvadVPN/Assets.xcassets/TranslucentDangerSplitRightButton.imageset/TranslucentDangerSplitRightButton.pdf Binary files differnew file mode 100644 index 0000000000..f033dc42f6 --- /dev/null +++ b/ios/MullvadVPN/Assets.xcassets/TranslucentDangerSplitRightButton.imageset/TranslucentDangerSplitRightButton.pdf diff --git a/ios/MullvadVPN/Assets.xcassets/TranslucentNeutralButton.imageset/TranslucentNeutralButton.pdf b/ios/MullvadVPN/Assets.xcassets/TranslucentNeutralButton.imageset/TranslucentNeutralButton.pdf Binary files differindex b4f5b3b123..fc29439ce1 100644 --- a/ios/MullvadVPN/Assets.xcassets/TranslucentNeutralButton.imageset/TranslucentNeutralButton.pdf +++ b/ios/MullvadVPN/Assets.xcassets/TranslucentNeutralButton.imageset/TranslucentNeutralButton.pdf diff --git a/ios/MullvadVPN/Base.lproj/Main.storyboard b/ios/MullvadVPN/Base.lproj/Main.storyboard index 9cf7ac24e6..7bb8bab957 100644 --- a/ios/MullvadVPN/Base.lproj/Main.storyboard +++ b/ios/MullvadVPN/Base.lproj/Main.storyboard @@ -206,7 +206,7 @@ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="3rI-k6-N1S"> - <rect key="frame" x="24" y="245" width="327" height="212"/> + <rect key="frame" x="24" y="307" width="327" height="212"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SECURE CONNECTION" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HNy-mU-nui"> <rect key="frame" x="0.0" y="0.0" width="327" height="24"/> @@ -253,29 +253,39 @@ </view> </subviews> </stackView> - <containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Dv1-XA-XQG" userLabel="Tunnel control buttons"> - <rect key="frame" x="24" y="481" width="327" height="162"/> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kbg-vd-uS0"> + <rect key="frame" x="24" y="543" width="327" height="100"/> + <subviews> + <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="oZQ-uX-qg5"> + <rect key="frame" x="0.0" y="0.0" width="327" height="100"/> + <constraints> + <constraint firstAttribute="height" constant="100" placeholder="YES" id="xyU-Ve-38D"/> + </constraints> + </stackView> + </subviews> + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <constraints> - <constraint firstAttribute="height" constant="162" placeholder="YES" id="kLh-W4-WzX"/> + <constraint firstItem="oZQ-uX-qg5" firstAttribute="leading" secondItem="kbg-vd-uS0" secondAttribute="leading" id="CVK-s8-K47"/> + <constraint firstAttribute="trailing" secondItem="oZQ-uX-qg5" secondAttribute="trailing" id="IEd-jf-HBD"/> + <constraint firstAttribute="bottom" secondItem="oZQ-uX-qg5" secondAttribute="bottom" id="pAj-Dl-cfL"/> + <constraint firstItem="oZQ-uX-qg5" firstAttribute="top" secondItem="kbg-vd-uS0" secondAttribute="top" id="qfE-M4-Vqw"/> </constraints> - <connections> - <segue destination="7We-1v-DIF" kind="embed" identifier="EmbedTunnelControls" id="SUp-a7-wNJ"/> - </connections> - </containerView> + </view> </subviews> <color key="backgroundColor" name="Primary"/> <constraints> <constraint firstItem="3rI-k6-N1S" firstAttribute="leading" secondItem="PNd-mm-N1B" secondAttribute="leadingMargin" id="5fs-z1-QKf"/> <constraint firstAttribute="trailingMargin" secondItem="3rI-k6-N1S" secondAttribute="trailing" id="NFr-rI-7r3"/> - <constraint firstItem="iBo-pG-OTz" firstAttribute="trailing" secondItem="Dv1-XA-XQG" secondAttribute="trailing" constant="24" id="OPf-p4-dPn"/> - <constraint firstItem="Dv1-XA-XQG" firstAttribute="top" secondItem="3rI-k6-N1S" secondAttribute="bottom" constant="24" id="hRj-EJ-ls3"/> - <constraint firstItem="iBo-pG-OTz" firstAttribute="bottom" secondItem="Dv1-XA-XQG" secondAttribute="bottom" constant="24" id="sjJ-M6-O74"/> - <constraint firstItem="Dv1-XA-XQG" firstAttribute="leading" secondItem="iBo-pG-OTz" secondAttribute="leading" constant="24" id="ye6-tj-a0l"/> + <constraint firstItem="iBo-pG-OTz" firstAttribute="bottom" secondItem="kbg-vd-uS0" secondAttribute="bottom" constant="24" id="Und-F7-4LQ"/> + <constraint firstItem="kbg-vd-uS0" firstAttribute="leading" secondItem="iBo-pG-OTz" secondAttribute="leading" constant="24" id="WON-cN-WSC"/> + <constraint firstItem="kbg-vd-uS0" firstAttribute="top" secondItem="3rI-k6-N1S" secondAttribute="bottom" constant="24" id="Yrd-dM-tsp"/> + <constraint firstItem="iBo-pG-OTz" firstAttribute="trailing" secondItem="kbg-vd-uS0" secondAttribute="trailing" constant="24" id="cbX-QT-hOe"/> </constraints> <edgeInsets key="layoutMargins" top="0.0" left="24" bottom="24" right="24"/> <viewLayoutGuide key="safeArea" id="iBo-pG-OTz"/> </view> <connections> + <outlet property="buttonsStackView" destination="oZQ-uX-qg5" id="dvm-AG-6HG"/> <outlet property="cityLabel" destination="mfr-Ic-PYf" id="vut-Me-cdj"/> <outlet property="connectionPanel" destination="ocV-9f-WDZ" id="Uad-bl-KFU"/> <outlet property="countryLabel" destination="9Yf-sl-l3q" id="L3N-Jn-zlr"/> @@ -895,7 +905,7 @@ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="JYh-33-d0O"> - <rect key="frame" x="0.0" y="0.0" width="375" height="597"/> + <rect key="frame" x="0.0" y="0.0" width="375" height="598"/> <subviews> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="N9k-cQ-tlw" userLabel="Content view"> <rect key="frame" x="0.0" y="0.0" width="375" height="558"/> @@ -934,7 +944,7 @@ <nil key="highlightedColor"/> </label> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Cas-Tk-gcz" customClass="LinkButton" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="20" y="516" width="128" height="22"/> + <rect key="frame" x="20" y="516" width="20" height="22"/> <fontDescription key="fontDescription" name=".AppleSystemUIFont" family=".AppleSystemUIFont" pointSize="18"/> <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <state key="normal" title="Privacy Policy" image="IconExtlink"/> @@ -970,10 +980,10 @@ </constraints> </scrollView> <view contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" translatesAutoresizingMaskIntoConstraints="NO" id="16P-Q0-ZO9" userLabel="Footer"> - <rect key="frame" x="0.0" y="597" width="375" height="70"/> + <rect key="frame" x="0.0" y="598" width="375" height="69"/> <subviews> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ttw-7B-1MM" customClass="AppButton" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="16" y="24" width="343" height="22"/> + <rect key="frame" x="16" y="24" width="343" height="21"/> <accessibility key="accessibilityConfiguration" identifier="AgreeButton"/> <state key="normal" title="Agree and continue" backgroundImage="DefaultButton"> <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> @@ -1011,235 +1021,6 @@ </objects> <point key="canvasLocation" x="-551" y="27"/> </scene> - <!--Tunnel Control View Controller--> - <scene sceneID="Zzn-S1-fVu"> - <objects> - <viewController id="7We-1v-DIF" customClass="TunnelControlViewController" customModule="MullvadVPN" customModuleProvider="target" sceneMemberID="viewController"> - <view key="view" contentMode="scaleToFill" id="Ayf-ZB-cCJ"> - <rect key="frame" x="0.0" y="0.0" width="327" height="162"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <viewLayoutGuide key="safeArea" id="MPh-Rn-8Ad"/> - </view> - <connections> - <outlet property="connectedView" destination="KX2-Pi-Myg" id="qSH-w0-rkV"/> - <outlet property="connectingView" destination="ZYQ-G2-MXc" id="Hg7-Sg-PFl"/> - <outlet property="disconnectedView" destination="7l6-on-fbR" id="UT0-9I-faY"/> - </connections> - </viewController> - <placeholder placeholderIdentifier="IBFirstResponder" id="C3V-lo-QFw" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/> - <view contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="7l6-on-fbR"> - <rect key="frame" x="0.0" y="0.0" width="261" height="121"/> - <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> - <subviews> - <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="shY-Lj-oYx"> - <rect key="frame" x="0.0" y="0.0" width="261" height="121"/> - <subviews> - <visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JHO-Ca-Zzd" customClass="TranslucentButtonBlurView" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="261" height="52.5"/> - <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="jVH-JP-pJo"> - <rect key="frame" x="0.0" y="0.0" width="261" height="52.5"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hVz-q0-Xpd" customClass="AppButton" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="261" height="52.5"/> - <accessibility key="accessibilityConfiguration" identifier="SelectLocationButton"/> - <inset key="contentEdgeInsets" minX="0.0" minY="0.0" maxX="0.0" maxY="10"/> - <state key="normal" title="Select location" backgroundImage="TranslucentNeutralButton"> - <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - </state> - <connections> - <action selector="handleSelectLocation:" destination="7We-1v-DIF" eventType="touchUpInside" id="P4j-WA-6kP"/> - </connections> - </button> - </subviews> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstAttribute="bottom" secondItem="hVz-q0-Xpd" secondAttribute="bottom" id="4Q2-Zh-7fM"/> - <constraint firstAttribute="trailing" secondItem="hVz-q0-Xpd" secondAttribute="trailing" id="AsY-cg-1fj"/> - <constraint firstItem="hVz-q0-Xpd" firstAttribute="leading" secondItem="jVH-JP-pJo" secondAttribute="leading" id="Eya-ig-8On"/> - <constraint firstItem="hVz-q0-Xpd" firstAttribute="top" secondItem="jVH-JP-pJo" secondAttribute="top" id="UY2-bK-i8s"/> - </constraints> - </view> - <blurEffect style="light"/> - </visualEffectView> - <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vxU-Mt-fMo" customClass="AppButton" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="68.5" width="261" height="52.5"/> - <accessibility key="accessibilityConfiguration" identifier="ConnectButton"/> - <state key="normal" title="Secure my connection" backgroundImage="SuccessButton"> - <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - </state> - <connections> - <action selector="handleSecureConnection:" destination="7We-1v-DIF" eventType="touchUpInside" id="XmB-3v-mds"/> - </connections> - </button> - </subviews> - </stackView> - </subviews> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstItem="shY-Lj-oYx" firstAttribute="leading" secondItem="7l6-on-fbR" secondAttribute="leading" id="1th-7a-npG"/> - <constraint firstItem="shY-Lj-oYx" firstAttribute="bottom" secondItem="7l6-on-fbR" secondAttribute="bottom" id="53Y-II-Op2"/> - <constraint firstAttribute="trailing" secondItem="shY-Lj-oYx" secondAttribute="trailing" id="6QT-NE-4OX"/> - <constraint firstItem="shY-Lj-oYx" firstAttribute="trailing" secondItem="7l6-on-fbR" secondAttribute="trailing" id="ORr-CI-hQE"/> - <constraint firstItem="shY-Lj-oYx" firstAttribute="leading" secondItem="7l6-on-fbR" secondAttribute="leading" id="U5c-eT-w73"/> - <constraint firstItem="shY-Lj-oYx" firstAttribute="top" secondItem="7l6-on-fbR" secondAttribute="top" id="cea-vh-q3j"/> - <constraint firstAttribute="bottom" secondItem="shY-Lj-oYx" secondAttribute="bottom" id="isW-KM-CjB"/> - <constraint firstItem="shY-Lj-oYx" firstAttribute="top" secondItem="7l6-on-fbR" secondAttribute="top" id="zkC-qT-d72"/> - </constraints> - </view> - <view contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="ZYQ-G2-MXc"> - <rect key="frame" x="0.0" y="0.0" width="261" height="120"/> - <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> - <subviews> - <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="K2E-tQ-f7f"> - <rect key="frame" x="0.0" y="0.0" width="261" height="120"/> - <subviews> - <visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="HGl-4o-WUZ" customClass="TranslucentButtonBlurView" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> - <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="tbu-8S-1Wk"> - <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dbp-iY-d0K" customClass="AppButton" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> - <accessibility key="accessibilityConfiguration" identifier="SwitchLocationButton"/> - <inset key="contentEdgeInsets" minX="0.0" minY="0.0" maxX="0.0" maxY="10"/> - <state key="normal" title="Switch location" backgroundImage="TranslucentNeutralButton"> - <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - </state> - <connections> - <action selector="handleSelectLocation:" destination="7We-1v-DIF" eventType="touchUpInside" id="8dc-Ta-6qx"/> - </connections> - </button> - </subviews> - <constraints> - <constraint firstAttribute="bottom" secondItem="dbp-iY-d0K" secondAttribute="bottom" id="5LU-aU-B47"/> - <constraint firstAttribute="trailing" secondItem="dbp-iY-d0K" secondAttribute="trailing" id="7PF-WL-1iS"/> - <constraint firstItem="dbp-iY-d0K" firstAttribute="leading" secondItem="tbu-8S-1Wk" secondAttribute="leading" id="8h9-Dh-uGH"/> - <constraint firstItem="dbp-iY-d0K" firstAttribute="top" secondItem="tbu-8S-1Wk" secondAttribute="top" id="Xop-Vl-s9E"/> - </constraints> - </view> - <blurEffect style="light"/> - </visualEffectView> - <visualEffectView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" translatesAutoresizingMaskIntoConstraints="NO" id="wTZ-S5-pHI" customClass="TranslucentButtonBlurView" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="68" width="261" height="52"/> - <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="hWm-jl-c2l"> - <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="PDP-HS-RB2" customClass="AppButton" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> - <state key="normal" title="Cancel" backgroundImage="TranslucentDangerButton"> - <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - </state> - <connections> - <action selector="handleDisconnect:" destination="7We-1v-DIF" eventType="touchUpInside" id="lgN-mu-5IE"/> - </connections> - </button> - </subviews> - <constraints> - <constraint firstItem="PDP-HS-RB2" firstAttribute="leading" secondItem="hWm-jl-c2l" secondAttribute="leading" id="0iK-cH-2T7"/> - <constraint firstItem="PDP-HS-RB2" firstAttribute="top" secondItem="hWm-jl-c2l" secondAttribute="top" id="Q9r-wY-cgh"/> - <constraint firstAttribute="bottom" secondItem="PDP-HS-RB2" secondAttribute="bottom" id="Rvj-jF-cwH"/> - <constraint firstAttribute="trailing" secondItem="PDP-HS-RB2" secondAttribute="trailing" id="Thh-Vt-L4v"/> - </constraints> - </view> - <blurEffect style="light"/> - </visualEffectView> - </subviews> - </stackView> - </subviews> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstItem="K2E-tQ-f7f" firstAttribute="trailing" secondItem="ZYQ-G2-MXc" secondAttribute="trailing" id="8jj-NR-Sva"/> - <constraint firstItem="K2E-tQ-f7f" firstAttribute="bottom" secondItem="ZYQ-G2-MXc" secondAttribute="bottom" id="eza-7A-uNf"/> - <constraint firstItem="K2E-tQ-f7f" firstAttribute="top" secondItem="ZYQ-G2-MXc" secondAttribute="top" id="ndG-qN-nvg"/> - <constraint firstItem="K2E-tQ-f7f" firstAttribute="leading" secondItem="ZYQ-G2-MXc" secondAttribute="leading" id="phY-um-kkn"/> - <constraint firstAttribute="bottom" secondItem="K2E-tQ-f7f" secondAttribute="bottom" id="qUI-AH-MDO"/> - <constraint firstItem="K2E-tQ-f7f" firstAttribute="top" secondItem="ZYQ-G2-MXc" secondAttribute="top" id="s3u-gd-29a"/> - <constraint firstAttribute="trailing" secondItem="K2E-tQ-f7f" secondAttribute="trailing" id="x5Q-Uy-6xz"/> - <constraint firstItem="K2E-tQ-f7f" firstAttribute="leading" secondItem="ZYQ-G2-MXc" secondAttribute="leading" id="xkx-qv-Dcr"/> - </constraints> - </view> - <view contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="KX2-Pi-Myg"> - <rect key="frame" x="0.0" y="0.0" width="261" height="120"/> - <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> - <subviews> - <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="J70-p3-IaC"> - <rect key="frame" x="0.0" y="0.0" width="261" height="120"/> - <subviews> - <visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fgw-rj-lK4" customClass="TranslucentButtonBlurView" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> - <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="dSR-qU-xgH"> - <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ZbQ-zA-ZS8" customClass="AppButton" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> - <accessibility key="accessibilityConfiguration" identifier="SwitchLocationButton"/> - <inset key="contentEdgeInsets" minX="0.0" minY="0.0" maxX="0.0" maxY="10"/> - <state key="normal" title="Switch location" backgroundImage="TranslucentNeutralButton"> - <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - </state> - <connections> - <action selector="handleSelectLocation:" destination="7We-1v-DIF" eventType="touchUpInside" id="rZA-gs-2O6"/> - </connections> - </button> - </subviews> - <constraints> - <constraint firstItem="ZbQ-zA-ZS8" firstAttribute="leading" secondItem="dSR-qU-xgH" secondAttribute="leading" id="c2Y-VY-hME"/> - <constraint firstAttribute="bottom" secondItem="ZbQ-zA-ZS8" secondAttribute="bottom" id="eUE-C6-e9C"/> - <constraint firstAttribute="trailing" secondItem="ZbQ-zA-ZS8" secondAttribute="trailing" id="ggD-w4-oQQ"/> - <constraint firstItem="ZbQ-zA-ZS8" firstAttribute="top" secondItem="dSR-qU-xgH" secondAttribute="top" id="oMx-RW-wv9"/> - </constraints> - </view> - <blurEffect style="light"/> - </visualEffectView> - <visualEffectView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" translatesAutoresizingMaskIntoConstraints="NO" id="sAx-Or-5Yn" customClass="TranslucentButtonBlurView" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="68" width="261" height="52"/> - <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="ANR-Gb-KsI"> - <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="d5t-ia-qxF" customClass="AppButton" customModule="MullvadVPN" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="261" height="52"/> - <accessibility key="accessibilityConfiguration" identifier="DisconnectButton"/> - <state key="normal" title="Disconnect" backgroundImage="TranslucentDangerButton"> - <color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - </state> - <connections> - <action selector="handleDisconnect:" destination="7We-1v-DIF" eventType="touchUpInside" id="S6r-fB-gnj"/> - </connections> - </button> - </subviews> - <constraints> - <constraint firstAttribute="trailing" secondItem="d5t-ia-qxF" secondAttribute="trailing" id="6AH-65-3Vh"/> - <constraint firstAttribute="bottom" secondItem="d5t-ia-qxF" secondAttribute="bottom" id="7aQ-oK-5Yo"/> - <constraint firstItem="d5t-ia-qxF" firstAttribute="leading" secondItem="ANR-Gb-KsI" secondAttribute="leading" id="EKV-6Y-UtV"/> - <constraint firstItem="d5t-ia-qxF" firstAttribute="top" secondItem="ANR-Gb-KsI" secondAttribute="top" id="NKf-WN-rVa"/> - </constraints> - </view> - <blurEffect style="light"/> - </visualEffectView> - </subviews> - </stackView> - </subviews> - <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstItem="J70-p3-IaC" firstAttribute="bottom" secondItem="KX2-Pi-Myg" secondAttribute="bottom" id="0FU-IO-L7p"/> - <constraint firstItem="J70-p3-IaC" firstAttribute="top" secondItem="KX2-Pi-Myg" secondAttribute="top" id="KZn-7H-iDI"/> - <constraint firstItem="J70-p3-IaC" firstAttribute="top" secondItem="KX2-Pi-Myg" secondAttribute="top" id="NnJ-v1-dtl"/> - <constraint firstItem="J70-p3-IaC" firstAttribute="leading" secondItem="KX2-Pi-Myg" secondAttribute="leading" id="ZWy-eg-o1m"/> - <constraint firstAttribute="trailing" secondItem="J70-p3-IaC" secondAttribute="trailing" id="cFk-ve-P9q"/> - <constraint firstItem="J70-p3-IaC" firstAttribute="leading" secondItem="KX2-Pi-Myg" secondAttribute="leading" id="kjo-BJ-Odb"/> - <constraint firstItem="J70-p3-IaC" firstAttribute="trailing" secondItem="KX2-Pi-Myg" secondAttribute="trailing" id="saI-Tt-ucW"/> - <constraint firstAttribute="bottom" secondItem="J70-p3-IaC" secondAttribute="bottom" id="tBe-df-ho5"/> - </constraints> - </view> - </objects> - <point key="canvasLocation" x="2541" y="78"/> - </scene> </scenes> <resources> <image name="DangerButton" width="9" height="9"/> @@ -1248,8 +1029,6 @@ <image name="IconSuccess" width="60" height="60"/> <image name="LogoIcon" width="253" height="253"/> <image name="SuccessButton" width="9" height="9"/> - <image name="TranslucentDangerButton" width="9" height="9"/> - <image name="TranslucentNeutralButton" width="9" height="9"/> <namedColor name="Primary"> <color red="0.16078431372549021" green="0.30196078431372547" blue="0.45098039215686275" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> </namedColor> diff --git a/ios/MullvadVPN/Bundle+MullvadVersion.swift b/ios/MullvadVPN/Bundle+MullvadVersion.swift deleted file mode 100644 index 872376c600..0000000000 --- a/ios/MullvadVPN/Bundle+MullvadVersion.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// Bundle+MullvadVersion.swift -// MullvadVPN -// -// Created by pronebird on 29/11/2019. -// Copyright © 2019 Mullvad VPN AB. All rights reserved. -// - -import Foundation - -private let kInfoDictionaryMullvadVersionSuffixKey = "MullvadVersionSuffix" - -extension Bundle { - - var mullvadVersion: String? { - let shortVersion = infoDictionary?["CFBundleShortVersionString"] as? String - let versionSuffix = infoDictionary?[kInfoDictionaryMullvadVersionSuffixKey] as? String - - return [shortVersion, versionSuffix] - .compactMap { $0 } - .joined(separator: "-") - } - -} diff --git a/ios/MullvadVPN/ConnectViewController.swift b/ios/MullvadVPN/ConnectViewController.swift index 52b8a87f8c..53e5fd47ab 100644 --- a/ios/MullvadVPN/ConnectViewController.swift +++ b/ios/MullvadVPN/ConnectViewController.swift @@ -10,17 +10,19 @@ import UIKit import NetworkExtension import os -class ConnectViewController: UIViewController, - RootContainment, - TunnelControlViewControllerDelegate, - TunnelObserver, +class ConnectViewController: UIViewController, RootContainment, TunnelObserver, SelectLocationDelegate { - @IBOutlet var secureLabel: UILabel! @IBOutlet var countryLabel: UILabel! @IBOutlet var cityLabel: UILabel! @IBOutlet var connectionPanel: ConnectionPanelView! + @IBOutlet var buttonsStackView: UIStackView! + + private let connectButton = makeButton(style: .success) + private let selectLocationButton = makeButton(style: .translucentNeutral) + private lazy var selectLocationBlurView = Self.makeBlurButton(button: selectLocationButton) + private let splitDisconnectButtonView = DisconnectSplitButton(bundle: nil) private let alertPresenter = AlertPresenter() @@ -47,15 +49,21 @@ class ConnectViewController: UIViewController, setNeedsHeaderBarStyleAppearanceUpdate() updateSecureLabel() updateTunnelConnectionInfo() + updateButtons() } } - private var showedAccountView = false + private var showedAccountView = true override func viewDidLoad() { super.viewDidLoad() connectionPanel.collapseButton.addTarget(self, action: #selector(handleConnectionPanelButton(_:)), for: .touchUpInside) + connectButton.addTarget(self, action: #selector(handleConnect(_:)), for: .touchUpInside) + splitDisconnectButtonView.primaryButton.addTarget(self, action: #selector(handleDisconnect(_:)), for: .touchUpInside) + splitDisconnectButtonView.secondaryButton.addTarget(self, action: #selector(handleReconnect(_:)), for: .touchUpInside) + + selectLocationButton.addTarget(self, action: #selector(handleSelectLocation(_:)), for: .touchUpInside) TunnelManager.shared.addObserver(self) self.tunnelState = TunnelManager.shared.tunnelState @@ -67,14 +75,6 @@ class ConnectViewController: UIViewController, showAccountViewForExpiredAccount() } - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - if case .embedTunnelControls = SegueIdentifier.Connect.from(segue: segue) { - let tunnelControlController = segue.destination as! TunnelControlViewController - tunnelControlController.view.translatesAutoresizingMaskIntoConstraints = false - tunnelControlController.delegate = self - } - } - // MARK: - TunnelObserver func tunnelStateDidChange(tunnelState: TunnelState) { @@ -87,21 +87,6 @@ class ConnectViewController: UIViewController, // no-op } - // MARK: - TunnelControlViewControllerDelegate - - func tunnelControlViewController(_ controller: TunnelControlViewController, handleAction action: TunnelControlAction) { - switch action { - case .connect: - connectTunnel() - - case .disconnect: - disconnectTunnel() - - case .selectLocation: - showSelectLocation() - } - } - // MARK: - SelectLocationDelegate func selectLocationController(_ controller: SelectLocationController, didSelectLocation location: RelayLocation) { @@ -129,6 +114,63 @@ class ConnectViewController: UIViewController, // MARK: - Private + private class func makeBlurButton(button: AppButton) -> UIView { + let effectView = TranslucentButtonBlurView(effect: UIBlurEffect(style: .light)) + effectView.contentView.addSubview(button) + + NSLayoutConstraint.activate([ + button.topAnchor.constraint(equalTo: effectView.contentView.topAnchor), + button.leadingAnchor.constraint(equalTo: effectView.contentView.leadingAnchor), + button.trailingAnchor.constraint(equalTo: effectView.contentView.trailingAnchor), + button.bottomAnchor.constraint(equalTo: effectView.contentView.bottomAnchor) + ]) + + return effectView + } + + private class func makeButton(style: AppButton.Style) -> AppButton { + let button = AppButton(type: .custom) + button.style = style + button.translatesAutoresizingMaskIntoConstraints = false + button.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .semibold) + return button + } + + private func updateButtons() { + switch tunnelState { + case .disconnected: + selectLocationButton.setTitle(NSLocalizedString("Select location", comment: ""), for: .normal) + connectButton.setTitle(NSLocalizedString("Secure connection", comment: ""), for: .normal) + + setArrangedButtons([selectLocationBlurView, connectButton]) + + case .connecting: + selectLocationButton.setTitle(NSLocalizedString("Switch location", comment: ""), for: .normal) + splitDisconnectButtonView.primaryButton.setTitle(NSLocalizedString("Cancel", comment: ""), for: .normal) + + setArrangedButtons([selectLocationBlurView, splitDisconnectButtonView]) + + case .connected, .reconnecting, .disconnecting: + selectLocationButton.setTitle(NSLocalizedString("Switch location", comment: ""), for: .normal) + splitDisconnectButtonView.primaryButton.setTitle(NSLocalizedString("Disconnect", comment: ""), for: .normal) + + setArrangedButtons([selectLocationBlurView, splitDisconnectButtonView]) + } + } + + private func setArrangedButtons(_ newButtons: [UIView]) { + buttonsStackView.arrangedSubviews.forEach { (button) in + if !newButtons.contains(button) { + buttonsStackView.removeArrangedSubview(button) + button.removeFromSuperview() + } + } + + newButtons.forEach { (button) in + buttonsStackView.addArrangedSubview(button) + } + } + private func updateSecureLabel() { secureLabel.text = tunnelState.textForSecureLabel().uppercased() secureLabel.textColor = tunnelState.textColorForSecureLabel() @@ -208,6 +250,10 @@ class ConnectViewController: UIViewController, } } + private func reconnectTunnel() { + TunnelManager.shared.reconnectTunnel(completionHandler: nil) + } + private func showAccountViewForExpiredAccount() { guard !showedAccountView else { return } @@ -239,6 +285,22 @@ class ConnectViewController: UIViewController, connectionPanel.toggleConnectionInfoVisibility() } + @objc func handleConnect(_ sender: Any) { + connectTunnel() + } + + @objc func handleDisconnect(_ sender: Any) { + disconnectTunnel() + } + + @objc func handleReconnect(_ sender: Any) { + reconnectTunnel() + } + + @objc func handleSelectLocation(_ sender: Any) { + showSelectLocation() + } + } private extension TunnelState { diff --git a/ios/MullvadVPN/ConnectionPanelView.swift b/ios/MullvadVPN/ConnectionPanelView.swift index fbf0ae44d3..325fc17bc2 100644 --- a/ios/MullvadVPN/ConnectionPanelView.swift +++ b/ios/MullvadVPN/ConnectionPanelView.swift @@ -182,12 +182,12 @@ class ConnectionPanelCollapseButton: CustomButton { enum Style { case up, down - var image: UIImage { + var image: UIImage? { switch self { case .up: - return UIImage(imageLiteralResourceName: "IconChevronUp") + return UIImage(named: "IconChevronUp") case .down: - return UIImage(imageLiteralResourceName: "IconChevronDown") + return UIImage(named: "IconChevronDown") } } } diff --git a/ios/MullvadVPN/DisconnectSplitButton.swift b/ios/MullvadVPN/DisconnectSplitButton.swift new file mode 100644 index 0000000000..a52d4e0af1 --- /dev/null +++ b/ios/MullvadVPN/DisconnectSplitButton.swift @@ -0,0 +1,59 @@ +// +// DisconnectSplitButton.swift +// MullvadVPN +// +// Created by pronebird on 29/07/2020. +// Copyright © 2020 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import UIKit + +private let kSplitSeparatorWidth = CGFloat(1) + +class DisconnectSplitButton: UIView { + @IBOutlet var contentView: UIView! + @IBOutlet var primaryButton: AppButton! + @IBOutlet var secondaryButton: AppButton! + + private var secondaryButtonObserver: NSObjectProtocol? + + init(bundle: Bundle?) { + super.init(frame: .zero) + + loadFromNib(bundle: bundle) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func loadFromNib(bundle: Bundle?) { + let nib = UINib(nibName: "DisconnectSplitButton", bundle: bundle) + _ = nib.instantiate(withOwner: self, options: nil) + + primaryButton.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .semibold) + + contentView.translatesAutoresizingMaskIntoConstraints = false + addSubview(contentView) + + NSLayoutConstraint.activate([ + contentView.leadingAnchor.constraint(equalTo: leadingAnchor), + contentView.trailingAnchor.constraint(equalTo: trailingAnchor), + contentView.topAnchor.constraint(equalTo: topAnchor), + contentView.bottomAnchor.constraint(equalTo: bottomAnchor) + ]) + + secondaryButtonObserver = secondaryButton.observe(\.bounds, options: [.new]) { [weak self] (button, change) in + self?.adjustTitleLabelPosition() + } + } + + private func adjustTitleLabelPosition() { + var contentInsets = AppButton.defaultContentInsets + contentInsets.left = secondaryButton.frame.width + kSplitSeparatorWidth + contentInsets.right = 0 + + primaryButton.contentEdgeInsets = contentInsets + } +} diff --git a/ios/MullvadVPN/DisconnectSplitButton.xib b/ios/MullvadVPN/DisconnectSplitButton.xib new file mode 100644 index 0000000000..6b160f829b --- /dev/null +++ b/ios/MullvadVPN/DisconnectSplitButton.xib @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> + <device id="retina6_1" orientation="portrait" appearance="light"/> + <dependencies> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/> + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + </dependencies> + <objects> + <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="DisconnectSplitButton" customModule="MullvadVPN" customModuleProvider="target"> + <connections> + <outlet property="contentView" destination="iN0-l3-epB" id="l9N-3B-7an"/> + <outlet property="primaryButton" destination="PD0-Fv-22K" id="dSp-cI-IXZ"/> + <outlet property="secondaryButton" destination="2ka-zL-tZX" id="U5c-9n-CJD"/> + </connections> + </placeholder> + <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> + <view contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="iN0-l3-epB"> + <rect key="frame" x="0.0" y="0.0" width="420" height="50"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <stackView opaque="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" spacing="1" translatesAutoresizingMaskIntoConstraints="NO" id="LYA-qF-TWz"> + <rect key="frame" x="0.0" y="0.0" width="420" height="50"/> + <subviews> + <visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="gDt-Zi-SNP" customClass="TranslucentButtonBlurView" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="369" height="50"/> + <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="Z5z-dl-BIA"> + <rect key="frame" x="0.0" y="0.0" width="369" height="50"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="PD0-Fv-22K" userLabel="Primary Button" customClass="AppButton" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="369" height="50"/> + <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="18"/> + <state key="normal" title="Button"/> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="number" keyPath="interfaceBuilderStyle"> + <integer key="value" value="5"/> + </userDefinedRuntimeAttribute> + </userDefinedRuntimeAttributes> + </button> + </subviews> + <constraints> + <constraint firstAttribute="trailing" secondItem="PD0-Fv-22K" secondAttribute="trailing" id="0sb-IJ-x0M"/> + <constraint firstItem="PD0-Fv-22K" firstAttribute="leading" secondItem="Z5z-dl-BIA" secondAttribute="leading" id="Ah6-1f-VuI"/> + <constraint firstItem="PD0-Fv-22K" firstAttribute="top" secondItem="Z5z-dl-BIA" secondAttribute="top" id="gfC-T2-1NN"/> + <constraint firstAttribute="bottom" secondItem="PD0-Fv-22K" secondAttribute="bottom" id="nto-wB-nPC"/> + </constraints> + </view> + <blurEffect style="regular"/> + </visualEffectView> + <visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XyY-R3-ytQ" customClass="TranslucentButtonBlurView" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="370" y="0.0" width="50" height="50"/> + <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="N0x-Hd-Pi6"> + <rect key="frame" x="0.0" y="0.0" width="50" height="50"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" horizontalCompressionResistancePriority="50" verticalCompressionResistancePriority="50" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2ka-zL-tZX" userLabel="Secondary Button" customClass="AppButton" customModule="MullvadVPN" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="50" height="50"/> + <constraints> + <constraint firstAttribute="width" secondItem="2ka-zL-tZX" secondAttribute="height" multiplier="1:1" id="oBq-BR-iqo"/> + </constraints> + <state key="normal" title="Button" image="IconReload"/> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="number" keyPath="interfaceBuilderStyle"> + <integer key="value" value="6"/> + </userDefinedRuntimeAttribute> + </userDefinedRuntimeAttributes> + </button> + </subviews> + <constraints> + <constraint firstAttribute="bottom" secondItem="2ka-zL-tZX" secondAttribute="bottom" id="3Sh-gX-GkA"/> + <constraint firstItem="2ka-zL-tZX" firstAttribute="leading" secondItem="N0x-Hd-Pi6" secondAttribute="leading" id="6ts-lS-vm6"/> + <constraint firstItem="2ka-zL-tZX" firstAttribute="top" secondItem="N0x-Hd-Pi6" secondAttribute="top" id="8HK-t6-YVW"/> + <constraint firstAttribute="trailing" secondItem="2ka-zL-tZX" secondAttribute="trailing" id="rvn-tI-FOp"/> + </constraints> + </view> + <blurEffect style="regular"/> + </visualEffectView> + </subviews> + <constraints> + <constraint firstItem="2ka-zL-tZX" firstAttribute="height" secondItem="PD0-Fv-22K" secondAttribute="height" id="3cN-0n-7HF"/> + </constraints> + </stackView> + </subviews> + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <constraints> + <constraint firstItem="LYA-qF-TWz" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="2kj-go-VNG"/> + <constraint firstAttribute="bottom" secondItem="LYA-qF-TWz" secondAttribute="bottom" id="C7Z-zY-N4F"/> + <constraint firstAttribute="trailing" secondItem="LYA-qF-TWz" secondAttribute="trailing" id="L01-AG-HOt"/> + <constraint firstItem="LYA-qF-TWz" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="d9k-Gy-lnD"/> + </constraints> + <nil key="simulatedTopBarMetrics"/> + <nil key="simulatedBottomBarMetrics"/> + <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> + <point key="canvasLocation" x="142.02898550724638" y="-25.446428571428569"/> + </view> + </objects> + <resources> + <image name="IconReload" width="512" height="512"/> + </resources> +</document> diff --git a/ios/MullvadVPN/LoginViewController.swift b/ios/MullvadVPN/LoginViewController.swift index 20cb90d387..b6cb895389 100644 --- a/ios/MullvadVPN/LoginViewController.swift +++ b/ios/MullvadVPN/LoginViewController.swift @@ -220,11 +220,11 @@ class LoginViewController: UIViewController, RootContainment { switch loginState { case .failure: let opacity: CGFloat = self.accountTextField.isEditing ? 0 : 1 - statusImageView.image = UIImage(imageLiteralResourceName: "IconFail") + statusImageView.image = UIImage(named: "IconFail") animateStatusImage(to: opacity) case .success: - statusImageView.image = UIImage(imageLiteralResourceName: "IconSuccess") + statusImageView.image = UIImage(named: "IconSuccess") animateStatusImage(to: 1) case .default, .authenticating: diff --git a/ios/MullvadVPN/SegueIdentifier.swift b/ios/MullvadVPN/SegueIdentifier.swift index 3e9f2db97b..2edf12abeb 100644 --- a/ios/MullvadVPN/SegueIdentifier.swift +++ b/ios/MullvadVPN/SegueIdentifier.swift @@ -22,10 +22,6 @@ extension SegueIdentifier { case showAccount = "ShowAccount" } - enum Connect: String, SegueConvertible { - case embedTunnelControls = "EmbedTunnelControls" - } - enum Account: String, SegueConvertible { case logout = "Logout" } diff --git a/ios/MullvadVPN/SelectLocationCell.swift b/ios/MullvadVPN/SelectLocationCell.swift index 8d29a1d960..0b5fa63dac 100644 --- a/ios/MullvadVPN/SelectLocationCell.swift +++ b/ios/MullvadVPN/SelectLocationCell.swift @@ -13,11 +13,11 @@ class SelectLocationCell: BasicTableViewCell { let locationLabel = UILabel() let statusIndicator = RelayStatusIndicatorView() - let tickImageView = UIImageView(image: UIImage(imageLiteralResourceName: "IconTick")) + let tickImageView = UIImageView(image: UIImage(named: "IconTick")) let collapseButton = UIButton(type: .custom) - private let chevronDown = UIImage(imageLiteralResourceName: "IconChevronDown") - private let chevronUp = UIImage(imageLiteralResourceName: "IconChevronUp") + private let chevronDown = UIImage(named: "IconChevronDown") + private let chevronUp = UIImage(named: "IconChevronUp") var isDisabled = false { didSet { diff --git a/ios/MullvadVPN/SettingsViewController.swift b/ios/MullvadVPN/SettingsViewController.swift index 4fd4f82a8a..bee3d3a458 100644 --- a/ios/MullvadVPN/SettingsViewController.swift +++ b/ios/MullvadVPN/SettingsViewController.swift @@ -91,8 +91,9 @@ class SettingsViewController: UITableViewController { let middleSection = StaticTableViewSection() let versionRow = StaticTableViewRow(reuseIdentifier: CellIdentifier.appVersion.rawValue) { (_, cell) in let cell = cell as! SettingsAppVersionCell + let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String - cell.versionLabel.text = Bundle.main.mullvadVersion + cell.versionLabel.text = version } versionRow.isSelectable = false diff --git a/ios/MullvadVPN/TranslucentButtonBlurView.swift b/ios/MullvadVPN/TranslucentButtonBlurView.swift index 7679d7f251..87d44f8e47 100644 --- a/ios/MullvadVPN/TranslucentButtonBlurView.swift +++ b/ios/MullvadVPN/TranslucentButtonBlurView.swift @@ -27,6 +27,34 @@ private let kButtonCornerRadius = CGFloat(4) private func setup() { layer.cornerRadius = kButtonCornerRadius layer.masksToBounds = true + + updateCornerMask() + } + + override func awakeFromNib() { + super.awakeFromNib() + + updateCornerMask() + } + + private func updateCornerMask() { + for case let button as AppButton in contentView.subviews { + layer.maskedCorners = Self.cornerMask(buttonStyle: button.style) + } + } + + private class func cornerMask(buttonStyle: AppButton.Style) -> CACornerMask { + switch buttonStyle { + case .translucentDangerSplitLeft: + return [.layerMinXMinYCorner, .layerMinXMaxYCorner] + case .translucentDangerSplitRight: + return [.layerMaxXMinYCorner, .layerMaxXMaxYCorner] + default: + return [ + .layerMinXMinYCorner, .layerMinXMaxYCorner, + .layerMaxXMinYCorner, .layerMaxXMaxYCorner + ] + } } } diff --git a/ios/MullvadVPN/TunnelControlViewController.swift b/ios/MullvadVPN/TunnelControlViewController.swift deleted file mode 100644 index c380bafdd6..0000000000 --- a/ios/MullvadVPN/TunnelControlViewController.swift +++ /dev/null @@ -1,104 +0,0 @@ -// -// TunnelControlView.swift -// MullvadVPN -// -// Created by pronebird on 01/11/2019. -// Copyright © 2019 Mullvad VPN AB. All rights reserved. -// - -import UIKit - -enum TunnelControlAction { - /// An action emitted only when the tunnel is down - case connect - - /// An action emitted when user either selects to cancel the connection or disconnect when - /// the tunnel is already connected - case disconnect - - /// An action emitted when user requests to either select the location when the tunnel is down - /// or change the location when the tunnel is connecting or connected. - case selectLocation -} - -protocol TunnelControlViewControllerDelegate: class { - func tunnelControlViewController(_ controller: TunnelControlViewController, handleAction action: TunnelControlAction) -> Void -} - -class TunnelControlViewController: UIViewController, TunnelObserver { - - @IBOutlet var disconnectedView: UIView! - @IBOutlet var connectingView: UIView! - @IBOutlet var connectedView: UIView! - - weak var delegate: TunnelControlViewControllerDelegate? - - private var controlsView: UIView? - - override func viewDidLoad() { - super.viewDidLoad() - - TunnelManager.shared.addObserver(self) - self.didReceiveTunnelState(TunnelManager.shared.tunnelState) - } - - // MARK: - TunnelObserver - - func tunnelStateDidChange(tunnelState: TunnelState) { - DispatchQueue.main.async { - self.didReceiveTunnelState(tunnelState) - } - } - - func tunnelPublicKeyDidChange(publicKey: WireguardPublicKey?) { - // no-op - } - - /// MARK: - Private - - private func didReceiveTunnelState(_ tunnelState: TunnelState) { - switch tunnelState { - case .disconnected: - addControlsView(disconnectedView) - - case .connecting: - addControlsView(connectingView) - - case .connected, .reconnecting, .disconnecting: - addControlsView(connectedView) - } - } - - private func addControlsView(_ nextControlsView: UIView) { - guard controlsView != nextControlsView else { return } - - controlsView?.removeFromSuperview() - controlsView = nextControlsView - - nextControlsView.translatesAutoresizingMaskIntoConstraints = false - - view.addSubview(nextControlsView) - - NSLayoutConstraint.activate([ - nextControlsView.topAnchor.constraint(equalTo: view.topAnchor), - nextControlsView.bottomAnchor.constraint(equalTo: view.bottomAnchor), - nextControlsView.leadingAnchor.constraint(equalTo: view.leadingAnchor), - nextControlsView.trailingAnchor.constraint(equalTo: view.trailingAnchor), - ]) - } - - // MARK: - Actions - - @IBAction func handleSecureConnection(_ sender: Any) { - delegate?.tunnelControlViewController(self, handleAction: .connect) - } - - @IBAction func handleDisconnect(_ sender: Any) { - delegate?.tunnelControlViewController(self, handleAction: .disconnect) - } - - @IBAction func handleSelectLocation(_ sender: Any) { - delegate?.tunnelControlViewController(self, handleAction: .selectLocation) - } - -} diff --git a/ios/MullvadVPN/TunnelManager.swift b/ios/MullvadVPN/TunnelManager.swift index ec234a348f..f45daca22a 100644 --- a/ios/MullvadVPN/TunnelManager.swift +++ b/ios/MullvadVPN/TunnelManager.swift @@ -435,6 +435,28 @@ class TunnelManager { exclusityController.addOperation(operation, categories: [.tunnelControl]) } + func reconnectTunnel(completionHandler: (() -> Void)?) { + let operation = AsyncBlockOperation { (finish) in + guard let tunnelIpc = self.tunnelIpc else { + finish() + return + } + + tunnelIpc.reloadTunnelSettings { (result) in + if case .failure(let error) = result { + error.logChain(message: "Failed to reconnect the tunnel") + } + finish() + } + } + + operation.addDidFinishBlockObserver { (operation) in + completionHandler?() + } + + exclusityController.addOperation(operation, categories: [.tunnelControl]) + } + func setAccount(accountToken: String, completionHandler: @escaping (Result<(), TunnelManager.Error>) -> Void) { let operation = ResultOperation<(), TunnelManager.Error> { (finish) in let result = Self.makeTunnelSettings(accountToken: accountToken) diff --git a/ios/convert-assets.rb b/ios/convert-assets.rb index a082667e04..0d49431304 100755 --- a/ios/convert-assets.rb +++ b/ios/convert-assets.rb @@ -28,6 +28,7 @@ GRAPHICAL_ASSETS=[ "icon-fail.svg", "icon-fastest.svg", "icon-nearest.svg", + "icon-reload.svg", "icon-settings.svg", "icon-spinner.svg", "icon-success.svg", @@ -76,7 +77,9 @@ ADDITIONAL_ASSETS = [ "SuccessButton.svg", "DangerButton.svg", "TranslucentDangerButton.svg", - "TranslucentNeutralButton.svg" + "TranslucentNeutralButton.svg", + "TranslucentDangerSplitLeftButton.svg", + "TranslucentDangerSplitRightButton.svg" ] # Functions @@ -113,6 +116,9 @@ def genereate_app_icon() end def generate_additional_assets() + # Fix PDF "CreationDate" for reproducible output + environment_variables = { "SOURCE_DATE_EPOCH" => "1596022781" } + for asset_name in ADDITIONAL_ASSETS do svg_file = File.join(ADDITIONAL_ASSETS_DIR, asset_name) image_name = File.basename(svg_file, ".svg") @@ -125,7 +131,7 @@ def generate_additional_assets() end puts "Generate #{image_name} -> #{output_file}" - system("rsvg-convert", "-f", "pdf", "-o", output_file, svg_file) + system(environment_variables, "rsvg-convert", "-f", "pdf", "-o", output_file, svg_file) end end |
